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

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")`

## Example of SDK conditional code

At work, we wanted to use [SwiftUI.Font.width(\_:)](https://developer.apple.com/documentation/swiftui/font/width(_:)) function in a Swift package with `// swift-tools-version: 5.7` and minimum deployment target of iOS 15.

```swift
// 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(\_:)](https://developer.apple.com/documentation/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.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1679690910985/72405187-300f-4a08-922d-11d24d94bc5e.png align="center")

## Versioned `canImport`

While 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.

![swiftinterface file for SwiftUI shipped in iOS SDK 16.2](https://cdn.hashnode.com/res/hashnode/image/upload/v1679963495337/6e605209-08ae-415c-9c2f-c6153da25e5c.png align="center")

[It returns true if the module version on disk is greater or equal to the specified value and returns false otherwise](https://github.com/apple/swift/blob/release/5.7/test/Parse/versioned_canimport.swift).

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
    

```swift
#if canImport(SwiftUI, _version: "4.1.17")
  // code executes when compiled with iOS SDK 16.1 or higher
#endif
```

### Advantages

Using the versioned `canImport` has some interesting advantages as pointed out by [Allan Shortlidge](https://forums.swift.org/t/build-time-sdk-availability-check/59679/2) [in the Swift Forums](https://forums.swift.org/t/build-time-sdk-availability-check/59679/2):

* 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.
    

### Disadvantages

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
    

```bash
xcrun -sdk iphoneos --show-sdk-path
```

* Then lookup the `user-module-version` in the `.swiftinterface` file.  
    On my machine, using [Xcodes](https://www.xcodes.app/) for installing multiple Xcode installations in parallel, the path for the swiftinterface of SwiftUI shipped in iOS SDK 16.2 is: `/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.swiftinterface`
    

## Alternatives

We 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.

```swift
#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](https://forums.swift.org/t/using-apis-introduced-in-ios-minor-releases/63897/2):

> 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.
