Swift availability check for Mac Catalyst is failing!

I have added an iOS 15+ function to my app and now it is crashing on my M1 iMac running in iPad Mac Catalyst mode on macOS 11.6. Could this be a potential bug in macOS, Xcode or Swift?

I have this code:

if #available(iOS 15.0, macCatalyst 15.0, *) {
    dataSource.applySnapshotUsingReloadData(fullSnapshot, completion: nil)
} else {
    dataSource.apply(fullSnapshot, animatingDifferences: false)
}

When I run it on my mac, I can see it is running thinking it is iOS 14.7 using print(UIDevice.current.systemVersion) however it runs the code within the available check and crashes because it is not available on my macOS version yet!

I have tried using this code too:

#if targetEnvironment(macCatalyst)
            print("HERE")
#else
            print("HERE2")
#endif

and it prints HERE2 in the console!

I don't see how this is possible?

Accepted Reply

There is a known issue with Swift's availability checks specifically for iOS apps running on Apple silicon. Here is the information in the Xcode 13 Release Notes about it:

Availability checks in iPhone and iPad apps on a Mac with Apple silicon always return true. This causes iOS apps running in macOS 11 Big Sur to see iOS 15 APIs as available, resulting in crashes. This only affects apps available in the Mac App Store built with the “My Mac (Designed for iPhone)” or “My Mac (Designed for iPad)” run destination. It doesn’t affect Mac Catalyst apps. (83378814)

Workaround: Use the following code to check for iOS 15 availability:

    if #available(iOS 15, *), ProcessInfo.processInfo.operatingSystemVersion.majorVersion >= 15 {

Replies

Let's first define what you're trying to run:

 on my M1 iMac running in iPad Mac Catalyst mode

Are you trying to specifically run for Mac Catalyst? Or are you specifically trying to run your iOS app that's unmodified on your M1 Mac? This second option is only available on Apple silicon Macs, and is not the same as running as a Mac Catalyst app.

I’m trying to run my iOS app unmodified on an M1 Mac. So that explains why the macCatalyst check doesn’t pass. But why does the iOS 15+ check succeed when it even reports it’s running as iOS 14.7?

There is a known issue with Swift's availability checks specifically for iOS apps running on Apple silicon. Here is the information in the Xcode 13 Release Notes about it:

Availability checks in iPhone and iPad apps on a Mac with Apple silicon always return true. This causes iOS apps running in macOS 11 Big Sur to see iOS 15 APIs as available, resulting in crashes. This only affects apps available in the Mac App Store built with the “My Mac (Designed for iPhone)” or “My Mac (Designed for iPad)” run destination. It doesn’t affect Mac Catalyst apps. (83378814)

Workaround: Use the following code to check for iOS 15 availability:

    if #available(iOS 15, *), ProcessInfo.processInfo.operatingSystemVersion.majorVersion >= 15 {

Based on the release notes, this looks like it's been fixed in the Xcode 13.2 beta.

Fixed an issue where availability checks in iPhone and iPad apps running on a Mac with Apple silicon always returned true, which caused iOS apps running in macOS Big Sur to see iOS 15 APIs as available and crash when trying to use those APIs. These availability checks now return the correct result for apps compiled with Xcode 13.2. (83378814)