SwiftUI
Using iOS 18’s new TabView with a sidebar
Published on: June 12, 2024In iOS 18, Apple has revamped the way that tab bars look. They used to be positioned at the bottom of the screen with an icon and a text underneath. Starting with iOS 18, tab bars will no longer be displayed in that manner. Instead, on iPad you will have your tab bar on the top of the screen with text-only items while on iPhone your tab bar will retain its old look. In addition to changing how a tab bar looks, Apple has also added new behavior to the tab bar; it can expand into a sidebar that contains...
Read more...Building a stretchy header view with SwiftUI on iOS 18
Published on: June 11, 2024In iOS 18, SwiftUI's ScrollView has gotten lots of love. We have several new features for ScrollView that give tons of control to us as developers. One of my favorite interactions with scroll views is when I can drag on a list an a header image animates along with it. In UIKit we'd implement a UIScrollViewDelegate and read the content offset on scroll. In SwiftUI we could achieve the stretchy header effect with GeometryReader but that's never felt like a nice solution. In iOS 18, it's possible to achieve a stretchy header with little to no workarounds by using the...
Read more...Programmatic navigation in SwiftUI with NavigationPath and navigationDestination
Published on: May 22, 2024One of the key features that was missing from SwiftUI when it first shipped was a good way to do programmatic navigation. There were some ways to handle this before iOS 16 introduced NavigationPath but it wasn’t very satisfying to use those APIs and they could be rather unreliable at times. To see an example, take a look at this post I wrote about handling deeplinks on iOS 14. In this post, I’d like to revisit programmatic navigation through iOS 16’s NavigationPath API which is a huge leap forward in terms of developer experience and reliability at the same time....
Read more...Turn off sidebar hiding on NavigationSplitView in SwiftUI
Published on: May 21, 2024By default, a NavigationSplitView in SwiftUI will show users an option to toggle the visibility of the sidebar. If you want to prevent this button from showing up so that users will always have to see your sidebar, you can do this by applying the toolbar(removing:) view modifier to your split view's sidebar as follows: NavigationSplitView(sidebar: { ExercisesList() .toolbar(removing: .sidebarToggle) }, detail: { ExerciseDetail(exercise: exercises.first!) }) The downside of doing this is that the button is hidden both in portrait and landscape modes. The result is that landscape users can no longer access your app's sidebar. To fix this you...
Read more...With iOS 17, we’ve gained a new way to provide observable data to our SwiftUI views. Until iOS 17, we’d use either an ObservableObject with @StateObject, @ObservedObject, or @EnvironmentObject whenever we had a reference type that we wanted to observe in one of our SwiftUI views. For lots of apps this worked absolutely fine, but these objects have a dependency on the Combine framework (which in my opinion isn’t a big deal), and they made it really hard for developers to limit which properties a view would observe. In iOS 17, we gained the new @Observable macro. I wrote about...
Read more...WIth the introduction of Xcode 15 beta and its corresponding beta OSses (I would say iOS 17 beta, but of course we also get macOS, iPadOS, and other betas...) Apple has introduced new state mangement tools for SwiftUI. One of these new tools is the @Bindable property wrapper. In an earlier post I explained that @Binding and @Bindable do not solve the same problem, and that they will co-exist in your applications. In this post, I would like to clarify the purpose and the use cases for @Bindable a little bit better so that you can make better decisions when...
Read more...Sometimes in SwiftUI apps I’ll find that I have a model with an optional value that I’d like to pass to a view that requires a non optional value. This is especially the case when you’re using Core Data in your SwiftUI apps and use auto-generated models. These models will usually include optional values for properties (even when your Core Data model marks these fields as required). Consider the following example: @Observable class SearchService { var results: [SearchResult] = [] var query: String? } Let me start by acknowledging that yes, this object can be written with a query: String...
Read more...Xcode 14 “Publishing changes from within view updates is not allowed, this will cause undefined behavior”
Updated on: April 23, 2024UPDATE FOR XCODE 14.1: This issue appears to have been partially fixed in Xcode 14.1. Some occurences of the warning are fixed, others aren't. In this post I'm collecting situations me and others run into and track whether they are fixed or not. If you have another sample that you think is similar, please send a sample of your code on Twitter as a Github Gist. Dear reader, if you've found this page you're probably encountering the error from the post title. Let me start by saying this post does not offer you a quick fix. Instead, it serves to...
Read more...This post is up to date for Xcode 15 and newer. It supersedes a version of this post that you can find here On iOS 15, Apple granted developers the ability to present partially visible bottom sheets using a component called UISheetPresentationController. Originally, we had to resort to using a UIHostingController to bring this component to SwiftUI. With iOS 16, we don't have to do this anymore. You can make use of the presentationDetents view modifier to configure your sheets to be fully visible, approximately half visible, or some custom fraction of the screen's height. To do this, you can...
Read more...It's been a while since I published my post that helps you wrap your head around Swift's property wrappers. Since then, I've done more and more SwiftUI related work and one challenge that I recently had to dig into was passing dependencies from SwiftUI's environment into a custom property wrapper. While figuring this out I learned about the DynamicProperty protocol which is a protocol that you can conform your property wrappers to. When your property wrapper conforms to the DynamicProperty protocol, your property wrapper will essentially become a part of your SwiftUI view. This means that your property wrapper can...
Read more...