Provide views, controls, and layout structures for declaring your app's user interface using SwiftUI.

SwiftUI Documentation

Post

Replies

Boosts

Views

Activity

Xcode 15 beta 7 Previews building issue
Summary When trying to display SwiftUI previews, building the previews may fail with the following error: Linking failed: linker command failed with exit code 1 (use -v to see invocation) ld: warning: search path '/Applications/Xcode.app/Contents/SharedFrameworks-iphonesimulator' not found ld: warning: Could not find or use auto-linked framework 'CoreAudioTypes': framework 'CoreAudioTypes' not found Note that may app does not use CoreAudioTypes. Observation This issue seems to occur when two conditions are met: The SwiftUI view must be located in a Swift Package Somewhere in either the View or the #Preview a type from another package has to be used. Say I have to packages one named Model-package and one named UI-Package. The UI-Package depends on the Model-Package. If I have a SwiftUI view in the UI-Package that uses a type of the Model-Package either in the View itself or in the #Preview, then the described error occurs. If I have a View in the UI-package that does not use a type of the Model-Package anywhere in its View or #Preview then the SwiftUI Preview builds and renders successful. I created a bug report: FB13033812
35
11
28k
Aug ’23
@State ViewModel memory leak in iOS 17 (new Observable)
Our app has an architecture based on ViewModels. Currently, we are working on migrating from the ObservableObject protocol to the Observable macro (iOS 17+). The official docs about this are available here: https://developer.apple.com/documentation/swiftui/migrating-from-the-observable-object-protocol-to-the-observable-macro Our ViewModels that were previously annotated with @StateObject now use just @State, as recommended in the official docs. Some of our screens (a screen is a SwiftUI view with a corresponding ViewModel) are presented modally. We expect that after dismissing a SwiftUI view that was presented modally, its corresponding ViewModel, which is owned by this view (via the @State modifier), will be deinitialized. However, it seems there is a memory leak, as the ViewModel is not deinitialized after a modal view is dismissed. Here's a simple code where ModalView is presented modally (through the .sheet modifier), and ModalViewModel, which is a @State of ModalView, is never deinitialized. import SwiftUI import Observation @Observable final class ModalViewModel { init() { print("Simple ViewModel Inited") } deinit { print("Simple ViewModel Deinited") // never called } } struct ModalView: View { @State var viewModel: ModalViewModel = ModalViewModel() let closeButtonClosure: () -> Void var body: some View { ZStack { Color.yellow .ignoresSafeArea() Button("Close") { closeButtonClosure() } } } } struct ContentView: View { @State var presentSheet: Bool = false var body: some View { Button("Present sheet modally") { self.presentSheet = true } .sheet(isPresented: $presentSheet) { ModalView { self.presentSheet = false } } } } #Preview { ContentView() } Is this a bug in the iOS 17 beta version or intended behavior? Is it possible to build a relationship between the View and ViewModel in a way where the ViewModel will be deinitialized after the View is dismissed? Thank you in advance for the help.
3
12
2.6k
Aug ’23
SwiftUI - Using Bindable with Environment
At the moment, using Bindable for an object stored in Environment works in a cumbersome way: struct ContentView: View { @Environment(Model.self) var model var body: some View { @Bindable var model = model VStack { Text(model.someField.uppercased()) TextField("", text: $model.someField) someSubView } .padding() } @ViewBuilder var someSubView: some View { @Bindable var model = model TextField("", text: $model.someField) } } A new @Bindable needs to be instantiated for each computed property in the view, which creates boilerplate I would like to avoid. I made a new property wrapper which functions the same as the EnvironmentObject wrapper, but for Observable: @propertyWrapper struct EnvironmentObservable<Value: AnyObject & Observable>: DynamicProperty { @Environment var wrappedValue: Value public init(_ objectType: Value.Type) { _wrappedValue = .init(objectType) } public init() { _wrappedValue = .init(Value.self) } private var store: Bindable<Value>! var projectedValue: Bindable<Value> { store } mutating func update() { store = Bindable(wrappedValue) } } Example: struct ContentView: View { @EnvironmentObservable var model: Model var body: some View { VStack { Text(model.someField.uppercased()) SubView(value: $model.someField) someSubView } .padding() } var someSubView: some View { TextField("", text: $model.someField) } } I was wondering if there would be any downsides to using this method? In my testings it seems to behave the same, but I'm not sure if using this could have a performance impact.
1
2
740
Sep ’23
SwiftUI Map: is it possible to add an inset to the map visible rectangle?
In UIKit, we can add an insets to a MKMapView with setVisibleMapRect to have additional space around a specified MKMapRect. It's useful for UIs like Apple Maps or FlightyApp (see first screenshot attached). This means we can have a modal sheet above the map but still can see all the content added to the map. I'm trying to do the same for a SwiftUI Map (on iOS 17) but I can't find a way to do it: see screenshot 2 below. Is it possible to obtain the same result or should I file a feedback for an improvement?
2
0
1.5k
Sep ’23
MapProxy conversion from screen to coords is wrong on macOS
Try the following code on macOS, and you'll see the marker is added in the wrong place, as the conversion from screen coordinates to map coordinates doesn't work correctly. The screenCoord value is correct, but reader.convert(screenCoord, from: .local) offsets the resulting coordinate by the height of the content above the map, despite the .local parameter. struct TestMapView: View { @State var placeAPin = false @State var pinLocation :CLLocationCoordinate2D? = nil @State private var cameraProsition: MapCameraPosition = .camera( MapCamera( centerCoordinate: .denver, distance: 3729, heading: 92, pitch: 70 ) ) var body: some View { VStack { Text("This is a bug demo.") Text("If there are other views above the map, the MapProxy doesn't convert the coordinates correctly.") MapReader { reader in Map( position: $cameraProsition, interactionModes: .all ) { if let pl = pinLocation { Marker("(\(pl.latitude), \(pl.longitude))", coordinate: pl) } } .onTapGesture(perform: { screenCoord in pinLocation = reader.convert(screenCoord, from: .local) placeAPin = false if let pinLocation { print("tap: screen \(screenCoord), location \(pinLocation)") } }) .mapControls{ MapCompass() MapScaleView() MapPitchToggle() } .mapStyle(.standard(elevation: .automatic)) } } } } extension CLLocationCoordinate2D { static var denver = CLLocationCoordinate2D(latitude: 39.742043, longitude: -104.991531) } (FB13135770)
4
0
977
Sep ’23
tvOS app icon is not used in a multiplatform app
I have a multiplatform app where I support iOS, macOS and tvOS. There is one target which supports it all. In my assets catalog I have the AppIcon entry which holds the app icon for iOS and macOS. This works as expected. However the tvOS app icon is ignored. I added an "tvOS App Icon & Top Shelf Image" asset to my asset catalog and filled it with my icons for tvOS. Then I added it in the target’s general settings App Icon entry under App Icons and Launch Screen like shown in the screenshot. What am I missing? What needs to be done to make this work?
4
0
1k
Sep ’23
navigationDestination + NavigationPath broken on iOS 17
Using the new navigationDestination and NavigationPath functions previously on iOS 16 everything has been working fine using a custom back button, which calls path.removeLast(). However, if we try this on iOS 17, the screen being removed flashes white. You can try this code as an example (NOTE THE WHITE FLASH ON REMOVE LAST): struct DetailView: View { @Binding var path: NavigationPath var body: some View { ZStack { Color.black VStack(alignment: .center) { Spacer() Button(action: { path.removeLast() }, label: { Text("BACK") }) Spacer() } } } } struct ParentView: View { @State var path: NavigationPath = .init() var body: some View { NavigationStack(path: $path) { ZStack { Color.red VStack(alignment: .center) { Spacer() Button(action: { path.append("TEST") }, label: { Text("FORWARD") }) Spacer() } } .navigationDestination(for: String.self) { _ in DetailView(path: $path) } } } } Any work arounds? Suggestions?
3
2
1.3k
Sep ’23
LazyVStack in ScrollView flitters on insertion in iOS 17
I built this Hacker News client a couple of months ago, the app works fine in iOS 16 but after I upgraded to iOS 17, the scrollview flitters whenever there's an insertion. I have tried removing withAnimation block around insertion, and also made sure items in list are identifiable. Anything else I should try? Replacing LazyVStack with VStack will make the issue go away but LazyVStack is kinda necessary here bc I don't have pagination atm. screen recording: https://www.reddit.com/link/16hc43h/video/r6feg0l6zxnb1/player ScrollView { // other views here LazyVStack { ForEach(store.items) { item in CommentTile(item) } } // other views here }
3
1
1.2k
Sep ’23
Set .bottomBar and .status placement for ToolbarItem in SwiftUI cased wired swipe to back behavior
Click Button and use swipe to back at pushed screen's left edge, keep finger in screen middle a bit, means don't finish the swipe back gesture, able to see first screen's navigation bar and title already moved. If keep the finger in middle then cancel the gesture, first screen's title and navigation item will be in second screen. import SwiftUI @main struct TestApp: App { var body: some Scene { WindowGroup { NavigationStack { List { NavigationLink("NavigationLink") { Text("Text") .navigationTitle("Text") } } .navigationTitle("navigationTitle") .toolbar { ToolbarItem(placement: .bottomBar) { Button("Button") { } } } } } } }
2
2
482
Sep ’23
UIViewRepresentable in background of a SwiftUI View clobbers preferences
I am working on a project where we have a UIViewRepresentable View in the background of a SwiftUI View, and the preferences for the foreground view are getting clobbered. If I put the UIViewRepresentable View in the foreground (overlay), then preferences on the SwiftUI view are honored. If I use a native SwiftUI View in the background then the preferences are honored. Consider this code: import SwiftUI struct ContentView: View { var body: some View { MyView() .background() { BackgroundView() } .onPreferenceChange(MyPreferenceKey.self) { value in if let value { print("-- preference changed: \(value)") } else { print("-- preference changed to `nil`") } } } } struct MyView: View { var body: some View { Text("Hello") .preference(key: MyPreferenceKey.self, value: "Hello World") } } struct MyPreferenceKey: PreferenceKey { static func reduce(value: inout String?, nextValue: () -> String?) {} } struct BackgroundView: UIViewRepresentable { func makeUIView(context: Context) -> UIButton { UIButton() } func updateUIView(_ uiView: UIButton, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator() } class Coordinator { init() {} } } BackgroundView is a UIViewRepresentable View. In this case the printed output is: -- preference changed to `nil` However, if you use .overlay instead of .background: MyView() .overlay { BackgroundView() } Then it prints: -- preference changed: Hello World Which is what it should. Is there a way to workaround this?
1
0
871
Sep ’23
Unknown Crash - OUTLINED_FUNCTION_2
We've got hundreds of crashes in our SwiftUI app which we think are "silent" crashes as there are no complaints from clients and yet - it happens on the main thread in the foreground so I'm not completely sure. The annoying thing is that we have no idea by the stack trace what is causing this issue, I feel helpless as this is causing some very loud noise through management and honestly - myself who wants to have this noise cleared. this particular crash is the highest impact (one of a few different weird crashes in our app without clear stack trace) 0 SwiftUI 0x895d90 OUTLINED_FUNCTION_2 + 836 1 SwiftUI 0x895da8 OUTLINED_FUNCTION_2 + 860 2 SwiftUI 0x1329880 OUTLINED_FUNCTION_2 + 424 3 SwiftUI 0x6806c OUTLINED_FUNCTION_441 + 584 4 SwiftUI 0x481b0 OUTLINED_FUNCTION_194 + 544 5 UIKitCore 0x1b7194 -[UIViewController removeChildViewController:notifyDidMove:] + 128 6 UIKitCore 0x77d6e8 -[UINavigationController removeChildViewController:notifyDidMove:] + 80 7 UIKitCore 0x205224 -[UIViewController dealloc] + 768 8 UIKitCore 0x1036c -[UINavigationController viewDidDisappear:] + 372 9 UIKitCore 0xd9c4 -[UIViewController _setViewAppearState:isAnimating:] + 1012 10 UIKitCore 0x46e61c -[UIViewController __viewDidDisappear:] + 136 11 UIKitCore 0x7ec024 __64-[UIViewController viewDidMoveToWindow:shouldAppearOrDisappear:]_block_invoke_3 + 44 12 UIKitCore 0x1a3f24 -[UIViewController _executeAfterAppearanceBlock] + 84 13 UIKitCore 0x1a3e68 -[_UIAfterCACommitBlock run] + 72 14 UIKitCore 0x1a3d9c -[_UIAfterCACommitQueue flush] + 176 15 UIKitCore 0x1a3ca8 _runAfterCACommitDeferredBlocks + 496 16 UIKitCore 0x3f530 _cleanUpAfterCAFlushAndRunDeferredBlocks + 108 17 CoreFoundation 0x43564 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 28 18 CoreFoundation 0xabd9c __CFRunLoopDoBlocks + 368 19 CoreFoundation 0x7bbbc __CFRunLoopRun + 856 20 CoreFoundation 0x80ed4 CFRunLoopRunSpecific + 612 21 GraphicsServices 0x1368 GSEventRunModal + 164 22 UIKitCore 0x3a23d0 -[UIApplication _run] + 888 23 UIKitCore 0x3a2034 UIApplicationMain + 340 24 SwiftUI 0x1d1014 OUTLINED_FUNCTION_895 + 2420 25 SwiftUI 0x13216c block_copy_helper.1 + 388 26 SwiftUI 0x11b4bc OUTLINED_FUNCTION_901 + 2868 Number 27 will be our app's Main function and that it - no other trace of our apps code. Firebase is saying this happens 100% on iOS 16 only and always on the foreground. How can I get to the bottom of this? how can I debug such a crash?
11
2
2.4k
Sep ’23
String catalogs in packages
We have separated much of our UI into different packages to reduce complexity and compile time. When we recently tested using new .xcstrings string catalogs, we hit an unexpected problem. Strings extracted from SwiftUI components like Text or Button are extracted into the Localizable.xcstrings in the same package, but the default behaviour of Text(_ key:tableName:bundle:comment:) is to use Bundle.main. When the default behaviour of the string extraction isn't to extract to the main app target, this introduces a very fragile system where it's easy to add code that looks localised, but ends up failing lookup at runtime. I don't feel comfortable that we will always remember to define the correct module every time we create a Text. Also, other components like Button doesn't have an init that takes a Bundle, so we would also have to remember that Button(_ titleKey:action:) can now only be used in a package if we make sure that the main bundle contains a matching key. Is there a way for us to make sure that strings are always extracted to the same place as they are resolved against by default? Either by having strings in packages extracted to an xcstrings file in the main app or having Text default to resolving against the module bundle by default?
3
4
1.5k
Sep ’23
How to support foregroundColor (deprecated) and foregroundStyle in watchOS 9/10?
In my Watch app on watchOS 9 I was using .foregroundColor(myColour) to colour the text in a widgetLabel on a corner complication like this: let myColour: Color = functionThatReturnsAColorObjectConstructedLike Color.init(...) // green .widgetLabel { Text(myText) .foregroundColor(myColour) } It worked fine; the widget label was green. Now, in watchOS 10, I see that foregroundColor() is being deprecated in favour of foregroundStyle(), and I can use .foregroundStyle(.green), and - importantly - foregroundStyle() is only available on watchOS 10 and newer. myColour is calculated depending on some other info, so I can't just write .green, and when I use .foregroundStyle(myColour) the widget label comes out as white every time, even if I set myColour = .green. I think I have to use some sort of extension to pick the right combination, something like: extension View { func foregroundType(colour: Colour, style: any ShapeStyle) -> some THING? { if #available(watchOS 10.0, *) { return foregroundStyle(style) } else { return foregroundColor(colour) } } } // Usage let myStyle: any ShapeStyle = SOMETHING? ... .widgetLabel { Text(myText) .foregroundType(colour: myColour, style: myStyle) It doesn't work. I just can't figure out what should be returned, nor how to return it. Any ideas?
2
2
2.4k
Sep ’23
Disabled button in SwiftUI .alert not working
I found an issue when implementing an alert with a TextField to input a name. I want the action button to be disabled until a name has been entered, but the action block is never executed when the button has become enabled and pressed. The problem seems to appear only when name is initially an empty string. Tested with iOS 17.0. struct MyView: View { @State private var name = "" var body: some View { SomeView() .alert(...) { TextField("Name", text: $name) Button("Action") { // Action }.disabled(name.isEmpty) Button("Cancel", role: .cancel) {} } } }
9
5
3.2k
Sep ’23
No ObservableObject of type AuthViewModel found, but only on Mac
Hi. I am very new to SwiftUI and still trying to learn. I just encountered a very weird problem that I can't figure out. I have an iPad application that runs on Apple Silicon Macs as "Designed for iPad"; for some reason, this issue only comes up when running it on a Mac, even if the code is exactly the same. This is the view that's causing issues: struct ProfileEditView: View { @EnvironmentObject var mainModel: MainViewModel @EnvironmentObject var authModel: AuthViewModel [some other stuff] var body: some View { GeometryReader { geo in VStack(spacing: 20) { navigationBar() .padding(.horizontal, 3) if authModel.user != nil { VStack(alignment: .leading, spacing: 30) { PhotosPicker(selection: self.$imageSelection, matching: .images, preferredItemEncoding: .compatible) { ProfilePicView(editIconShown: true) } .disabled(authModel.profilePicIsLoading) .padding(.horizontal, geo.size.width * 0.3) .padding(.bottom) VStack(spacing: 10) { if let error = self.error { Text(error) .foregroundStyle(Color.red) .font(.footnote) .italic() } SettingsTextFieldView(String(localized: "Your name:"), value: self.$name) { if nameIsValid { authModel.updateName(self.name) } } } Spacer() actions() } .padding(.horizontal, 5) .padding(.top) .onAppear { self.name = authModel.user!.name } } } .padding() } } } Obviously, I am injecting the ViewModels instances at the app entry point: @main struct MyApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate @StateObject var authViewModel: AuthViewModel = AuthViewModel() @StateObject var mainViewModel: MainViewModel = MainViewModel() @StateObject var statsViewModel: StatsViewModel = StatsViewModel() @StateObject var purchaseViewModel: PurchaseViewModel = PurchaseViewModel() @State var revenueCatIsConfigured: Bool = false init() { // MARK: Firebase configuration FirebaseConfiguration.shared.setLoggerLevel(.min) FirebaseApp.configure() // MARK: RevenueCat configuration if let uid = AuthViewModel.getLoggedInUserID() { Purchases.logLevel = .error Purchases.configure(withAPIKey: "redacted", appUserID: uid) self.revenueCatIsConfigured = true } } var body: some Scene { WindowGroup { MainView() .environmentObject(authViewModel) .environmentObject(mainViewModel) .environmentObject(statsViewModel) .environmentObject(purchaseViewModel) } } } And lastly, ProfileEditView that is causing issues, is a subview of MainView: MainView has a switch statement, based on the selected tab; one of the possible views is SettingsView, which can show ProfileEditView as a sheet modifier. For some reason, only when running on mac, I get the error Thread 1: Fatal error: No ObservableObject of type AuthViewModel found. A View.environmentObject(_:) for AuthViewModel may be missing as an ancestor of this view. This will come up every time I reference authModel in this view alone. Both the iPad and iPhone versions work just fine, and again, the code is exactly the same, since it's a "Designed for iPad". I don't even know how to troubleshoot the issue.
5
2
1k
Sep ’23
SwiftUI Commands and StrictConcurrency Warnings Issue
I have enabled “StrictConcurrency” warnings in my project that uses SwiftUI. I have a Commands struct. It has a Button, whose action is calling an async method via Task{}. This builds without warnings within Views, but not Commands. There the compiler reports “Main actor-isolated property 'body' cannot be used to satisfy nonisolated protocol requirement”. Looking at SwiftUI: In View, body is declared @MainActor: @ViewBuilder @MainActor var body: Self.Body { get } In Commands, body is not declared @MainActor: @CommandsBuilder var body: Self.Body { get } So the common practice of making a Button action asynchronous: Button { Task { await model.load() } } label:{ Text("Async Button") } will succeed without warnings in Views, but not in Commands. Is this intentional? I've filed FB13212559. Thank you.
3
0
908
Sep ’23
SF Symbol image color in the menu bar
I can set color of the SF symbol image in the application window but cannot do the same in the menu bar. I wonder how I can change the color in the menu? import SwiftUI @main struct ipmenuApp: App { var body: some Scene { MenuBarExtra { Image(systemName: "bookmark.circle.fill") .renderingMode(.original) .foregroundStyle(.red) } label: { Image(systemName: "bookmark.circle.fill") .renderingMode(.original) .foregroundStyle(.red) } } } xcodebuild -version Xcode 15.0 Build version 15A240d
4
1
1k
Oct ’23
Invalid Numeric Value (NaN) Error in SwiftUI's TextField on Long-Press
I'm experiencing a peculiar issue with SwiftUI's TextField. Whenever I long-press on the TextField, the console outputs an error about passing an invalid numeric value (NaN, or not-a-number) to the CoreGraphics API. This issue persists even in a new Xcode project with minimal code. Code Snippet: import SwiftUI struct ContentView: View { @State private var text: String = "" var body: some View { TextField("Placeholder", text: $text) } } Error: this application, or a library it uses, has passed an invalid numeric value (NaN, or not-a-number) to CoreGraphics API and this value is being ignored. Please fix this problem. Steps to Reproduce: Create a new SwiftUI project in Xcode. Add a TextField to the ContentView. Run the app on a device or simulator. Long-press inside the TextField. What I've Tried: Updating to the latest version of Xcode and iOS. Using UIViewRepresentable to wrap a UIKit UITextField. Creating a new Xcode project to isolate the issue. None of these steps have resolved the issue. Questions: Has anyone else encountered this problem? Are there any known workarounds for this issue? Is this a known bug, and if so, has it been addressed in any updates?
17
25
7.6k
Oct ’23