I am using #canImport(JournalingSuggestions), but something is going wrong and my app is attempting to import the framework on iPad, and crashing on launch. How can I ensure that it's properly filtered out from everything except iPhone?
import SwiftUI
#if canImport(JournalingSuggestions)
import JournalingSuggestions
#endif
struct JournalingSuggestionsView: View {
var body: some View {
#if canImport(JournalingSuggestions)
JournalingSuggestionsPicker {
Text("Open Journaling Suggestions")
} onCompletion: { suggestion in
print(suggestion)
}
#else
Text("Journaling suggestions not available on this platform.")
#endif
}
}
Error:
dyld[8689]: Library not loaded: /System/Library/Frameworks/JournalingSuggestions.framework/JournalingSuggestions
Referenced from: <A656E6BC-4883-3245-BE71-3F84C2F41119> /private/var/containers/Bundle/Application/C6C11F57-AFAA-442A-B726-7AADDDB50D79/Catalog.app/Catalog
Reason: tried: '/System/Library/Frameworks/JournalingSuggestions.framework/JournalingSuggestions' (no such file), '/private/preboot/Cryptexes/OS/System/Library/Frameworks/JournalingSuggestions.framework/JournalingSuggestions' (no such file), '/System/Library/Frameworks/JournalingSuggestions.framework/JournalingSuggestions' (no such file, not in dyld cache)
System info: Xcode 15.2 iPadOS 17.3.1
As an update to @eskimo 's response,
There's a few cases here:
- Case #1: Building for the simulator.
- Case #2: Building for a real device, but running on a device where the framework isn't present (eg: iPad, Mac)
- Case #3: Building for an iOS version before iOS 17.2 where the framework isn't available
For case #1: use #if canImport(JournalingSuggestions)
like this:
#if canImport(JournalingSuggestions)
import JournalingSuggestions
#endif
Then, in your code where you use JournalingSuggestionsPicker
, check if you can import it first. This way, if you're not building for a real device, the framework will not be imported.
For case #2:
Since you're building for a real device, the framework can be imported. Since it's not an iPhone, your app will crash when run. Protect against this by first checking if you can import UIKit, then setting isJournalingSuggestionsAvailable
to be true if you're on an iPhone. If you're on Mac, where UIKit can't be imported, isJournalingSuggestionsAvailable
will be false anyways. For example:
#if canImport(UIKit)
import UIKit
let isJournalingSuggestionsAvailable = UIDevice.current.userInterfaceIdiom == .phone
#else
let isJournalingSuggestionsAvailable = false
#endif
In your code, where you use JournalingSuggestionsPicker, first check if you can import it with the info in case #1 above, then check if isJournalingSuggestionsAvailable
is true.
For case #3:
To handle the older iOS case, you need if #available(iOS 17.2, *)
Altogether, it looks like this in code:
#if canImport(JournalingSuggestions)
if isJournalingSuggestionsAvailable {
if #available(iOS 17.2, *) {
JournalingSuggestionsPicker {
Text("Choose a suggestion")
} onCompletion: { suggestion in
print("suggestion:", suggestion)
}
} else {
Text("Requires iOS 17.2.")
}
} else {
Text("Dynamically not available on this platform.")
}
#else
Text("Statically not available on this platform.")
#endif
}
Important - iPad weak linking
To ensure your app can run on iPad without crashing, you must weak link to the framework. The normal way to do this is via the Status column in the Link Binary With Libraries build phase. That doesn’t work here because the framework isn’t available on the iOS Simulator platform, so the simulator build fails, as @bryan1anderson pointed out. The workaround is to pass in -Xlinker -weak_framework -Xlinker JournalingSuggestions
via the Other Linker Flags build setting.