I am introducing SwiftUI to an existing app that also supports iOS 12. I have availability checks around the SwiftUI Code :
@available(iOS 13.0, *)
The code compiles fine and runs on iOS 13. However, when I run the app iOS 12 (Simulator/actual device), the app crashes:
dyld: Library not loaded: /System/Library/Frameworks/SwiftUI.framework/SwiftUI
Referenced from: .....
Reason: no suitable image found. Did find:
/System/Library/Frameworks/SwiftUI.framework/SwiftUI: mach-o, but not built for iOS simulator
The app crashes when running on an iOS 12 device as well, so not just a simulator problem.
I tried wrapping the import statements in #if canImport but that does not help:
#if canImport(SwiftUI)
Essentially, I am trying to implement new features that run on iOS 13 + MacCatalyst only using SwiftUI while still allowing the app to run on iOS 12 (without the new SwiftUI features)
You need to mark the SwiftUI framework as being imported weakly. The normal way to include frameworks in Swift apps is to do nothing—the compiler uses the import statements to determine which modules to load, and generates the linker commands to that effect. You can customize this behavior in Xcode, but you first need to explicitly link against SwiftUI, then you can mark the framework as optional.
- In the Xcode Project navigator, select your project file (blue Xcode icon, topmost in the tree).
- In the editor that opens, ensure your app target is selected (or whichever target you're using SwiftUI from).
- Select the “Build Phases” tab.
- Pop open the “Link Binary With Libraries” section.
- Click the + button under the list that appears.
- Type “SwiftUI” into the search field of the popup sheet, select “SwiftUI.framework,” and click “Add.”
- On the new row in the table, click the “Required” popup button in the last column.
- Change the value to “Optional.”
Build your app/framework now, and if you look at the logs for the link step you'll see
-weak_framework SwiftUI
in the arguments. This is what tells the linker that the SwiftUI framework is optional, and it shouldn't crash if it's not available (all imported symbols will be zero, and will now cause a crash only if used).If you feel like going the extra mile to check on things, you can use
otool -lvV
to check the output binary, where you'll see something like this:Load command 13
cmd LC_LOAD_WEAK_DYLIB
cmdsize 80
name /System/Library/Frameworks/SwiftUI.framework/SwiftUI (offset 24)
time stamp 2 Wed Dec 31 16:00:02 1969
current version 1.0.0
compatibility version 1.0.0
Load command 14
cmd LC_LOAD_DYLIB
cmdsize 88
name /System/Library/Frameworks/Foundation.framework/Foundation (offset 24)
time stamp 2 Wed Dec 31 16:00:02 1969
current version 1673.126.0
compatibility version 300.0.0
Note that SwiftUI uses LC_LOAD_WEAK_DYLIB while Foundation uses LC_LOAD_DYLIB. This is what we want.
Now, I don't have any iOS 12 devices or simulators around to check this myself, but this ought to be everything you need to get things up & running—just make sure you're properly wrapping all your SwiftUI code in @available blocks, as you've already started to do.