SDK conditional code with canImport(module, _version: "1.2.3.4")

I am a Software Engineer working on open source and enterprise mobile SDKs for iOS and MacOS developers written in Swift. From 🇩🇪 and happily living in 🇺🇸
Search for a command to run...

I am a Software Engineer working on open source and enterprise mobile SDKs for iOS and MacOS developers written in Swift. From 🇩🇪 and happily living in 🇺🇸
No comments yet. Be the first to comment.
WWDC26 kicked off on June 8, 2026. The information in this article reflects the information published by Apple on that date. There are 14 new frameworks. Name Description AccessoryAccess Manage

WWDC25 kicked off on June 9, 2025. The information in this article reflects the information published by Apple on that date. New Frameworks NameDescription AlarmKitSchedule prominent alarms and countdowns to help people manage their time. AVR...

WWDC25 is almost here, and I couldn’t be more excited! Whether you're attending the official events, community meetups, or just soaking in the energy around Cupertino, there’s no better time to connect, share ideas, and celebrate everything we love a...

In this blog post, I’ll share an observation and advice regarding the caching behavior of network responses by Apple’s APIs. If you are unfamiliar with caching of network responses then I recommend Apple’s article Accessing cached data that introduce...
WWDC24 kicked off on June 10, 2024. The information in this article reflects the information published by Apple on that date. New Frameworks NameDescription AccessorySetupKitUse AccessorySetupKit to discover accessories with Bluetooth or Wi-Fi...

In this blog post, I explain when and why you might want to use the versioned canImport compilation statement, which challenges arise, and what better alternatives exist.
Example: #if canImport(SwiftUI, _version: "4.1.17")
At work, we wanted to use SwiftUI.Font.width(_:) function in a Swift package with // swift-tools-version: 5.7 and minimum deployment target of iOS 15.
// is this sufficient?
if #available(iOS 16.0, *) {
let font = Font.system(.headline)
_ = font.width(Font.Width.compressed)
}
Our CI job reported a compilation error when using Xcode 14 (iOS 16.0). We were using Xcode 14.1 (or higher) and did not encounter an issue before.
SwiftUI.Font.width(_:) was actually introduced in iOS 16.1 and not in iOS 16.0 as the documentation suggests. This explains why Xcode 14.1 worked fine, as Xcode 14.1 contains the iOS 16.1 SDK.

canImportWhile researching a solution, I stumbled on an interesting fact that #if canImport allows specifying a version that gets checked during compile-time. The version gets compared against the -user-module-version flag in the .swiftmodule file using.

If you are curious: user-module-version for SwiftUI is
4.0.90.1.107 when shipped in iOS 16
4.1.17.100 when shipped in OS 16.1
4.2.11 when shipped in iOS 16.2
#if canImport(SwiftUI, _version: "4.1.17")
// code executes when compiled with iOS SDK 16.1 or higher
#endif
Using the versioned canImport has some interesting advantages as pointed out by Allan Shortlidge in the Swift Forums:
Suppose the framework and the APIs in question are cross-platform. In that case, you can (theoretically) write a single query to determine the build time availability of the API across multiple platforms because module versions tend to be aligned across the aligned platform-specific SDKs.
It allows you to gracefully handle things like APIs introduced midway through the betas even though the overall system/SDK version number hasn't changed.
It works for any Swift module that has an embedded user-module-version, regardless of whether the module is distributed with an SDK that is legible to the compiler.
It is tedious to scrape the user-module-version of the relevant framework out of the SDK as it is not documented on the internet.
The easiest but still complex procedure is:
install the Xcode with the desired SDK
Determine the SDK path where the frameworks are stored on your disk
xcrun -sdk iphoneos --show-sdk-path
user-module-version in the .swiftinterface file./Applications/Xcode-14.2.0.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/SwiftUI.framework/Modules/SwiftUI.swiftmodule/arm64-apple-ios.swiftinterfaceWe decided not to use canImport for our example because of the non-intuitive version number, and we discussed the following options:
increase the supported swift-tools-version to 5.7.1 OR
use an additional compilation statement
We ended up with the latter.
#if swift(>=5.7.1)
if #available(iOS 16.0, watchOS 9.0, *) {
// use a iOS 16.1 specific API like SwiftUI's Font.weight
}
#endif
Shoutout and thanks to Jon Shier for his input in Swift Forums:
There’s no good solution for this as we can’t check for SDK versions at build time or dynamically check for symbols at runtime. What you can do is a build time check for the Swift version (
#if swift(>=5.8)) around the checks for the new API. Technically it’s not a guarantee but Apple’s inflexibility with SDK versions in Xcode works in your favor here, so it should be stable.
I hope the Swift team will introduce a better option to support SDK-conditional code in the future.