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
SwiftUI
RSS for tagProvide views, controls, and layout structures for declaring your app's user interface using SwiftUI.
Post
Replies
Boosts
Views
Activity
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.
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.
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?
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)
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?
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?
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
}
Is it possible to somehow support copy/update edit modes when using quickLookPreview modifier, like in QLPreviewController? After pressing 'Done' it only allows me to discard or save to files instead.
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") {
}
}
}
}
}
}
}
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?
I want to share UserDefaults between main App and macOS Widget, in iOS I can do it using AppGroups, but how to do it between macOS Widget and main App, because macOS widget's AppGroup require to use "Team Identifier" as prefix and main App's AppGroup require prefix to be "group" so how can I share UserDefaults between the two?
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?
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?
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?
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) {}
}
}
}
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.
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.
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
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?