What's the correct way to check for unavailable API?

I ran into a problem recently with my production app and an update for iOS 18. In this example I was using a new API added to the RC candidate of iOS 18.0, using this API as an example, I couldn't find a satisfactory way to avoid crashing on iOS 18.1 where the API was not available. I had plenty of users running the iOS 18.1 Beta and ultimately it's my fault if a version of my app did work, and then didn't after an update....

This code causes a crash on iOS 18.1 beta as the .appleSleepingBreathingDisturbances API doesn't seem to have made it's way into the beta:

if #available(iOS 18.0, *), #available(watchOS 11, *) {
healthKitTypesToRead.insert(HKQuantityType.quantityType(forIdentifier: .appleSleepingBreathingDisturbances)!)
       }

I tried this but it still crashed on 18.1:

if #available(iOS 18.0, *), #available(watchOS 11, *) {
            if let newQuantity = HKQuantityType.quantityType(forIdentifier: .appleSleepingBreathingDisturbances) { 
                healthKitTypesToRead.insert(newQuantity)
            }
        }

In the end the only way I could resolve this was the following:

if #available(iOS 18.1, *){
            // Do nothing
        }
        else if #available(iOS 18.0, *), #available(watchOS 11, *) {
            if let newQuantity = HKQuantityType.quantityType(forIdentifier: .appleSleepingBreathingDisturbances) { 
                healthKitTypesToRead.insert(newQuantity)
            }
        }

This seems like a poor solution and I'll have to ensure I release a new version of the app once iOS 18.1 has the available API added to enable support for the feature.

How could I have checked availability for this API correctly without causing the app to crash? I'm asking this question more as a Swift language feature rather than issue with the specific API as I'm sure that will get resolved soon anyway.

Thanks

Answered by DTS Engineer in 805095022
How could I have checked availability for this API correctly without causing the app to crash?

I don’t think there’s a good answer here. Swift’s availability is predicated on the fact that APIs don’t disappear in later OS releases, but that’s exactly what happened here. Situations like this are exceedingly rare, so it’s hard to come up with a general solution to it. And honestly, I’m not sure it’s worth coming up with such a solution [1].

Did you file a bug about this specific missing API? Or did it already land in a later iOS 18.1 beta?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Back in the day we used a very different availability strategy, one based on checking for the symbol being nil. That worked well in the more chaotic environment that was traditional Mac OS. However, it produced its own set of problems, and with Mac OS X’s more orderly approach to OS releases, the new model really does make more sense.

How could I have checked availability for this API correctly without causing the app to crash?

I don’t think there’s a good answer here. Swift’s availability is predicated on the fact that APIs don’t disappear in later OS releases, but that’s exactly what happened here. Situations like this are exceedingly rare, so it’s hard to come up with a general solution to it. And honestly, I’m not sure it’s worth coming up with such a solution [1].

Did you file a bug about this specific missing API? Or did it already land in a later iOS 18.1 beta?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Back in the day we used a very different availability strategy, one based on checking for the symbol being nil. That worked well in the more chaotic environment that was traditional Mac OS. However, it produced its own set of problems, and with Mac OS X’s more orderly approach to OS releases, the new model really does make more sense.

Thank you for the further information. It's good to know there's no specific availability clause I could have otherwise added.

Regarding your comment: "Situations like this are exceedingly rare".... Apple increasingly likes withholding a few features from the annual iOS X.0 betas over the summer and then adding them in for RC candidates. (Who doesn't love a surprise!) This can also happen for iOS X.X releases if It coincides with a new feature from a product launch.

Over the years with the existence of public betas there is greater percentage of users running beta OS versions. At the same time most major OS updates in beta through the summer have added additional beta branches (iOS 16, 17 & 18 have all added X.1 betas before X.0 was released). Beta users will blame third party apps for crashes that occur even in the post example where it's really to do with their OS "branch" for lack of a better term.

Going forward it seems we do need a better way to check this availability in Swift or Apple needs to work to align beta updates of OS X.X to OS X.X+1 if that makes sense.... Otherwise this problem will reoccur with seemingly no way to avoid it.

On this specific example, it's not fixed in iOS 18.1 Beta 4 or Xcode 16.1 Beta 2, but I haven't filed a feedback as it's almost impossible for it not to be fixed pre-release, many apps including the Health app would break if it wasn't.

On this specific example, it's not fixed in iOS 18.1 Beta 4 or Xcode 16.1 Beta 2, but I haven't filed a feedback

Really you should. File first, ask about it later, that’s my maxim (-:

Anyway, one of my colleagues managed to track down an existing bug for this (r. 135905362). It is, as you say, not fixed in the current beta. As always, I recommend that you retest as new betas are seeded.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I think I originally didn't file the feedback as I assumed there would be a way to do the availability check and didn't see it as a "bug". There's also the slight problem not not getting responses to feedback 😕

In any case, It is now resolved in the latest iOS 18.1 beta 4 seed (although I'll have to wait a little for testers to update to is before changing my app code to support the API in iOS 18.1 - the crashes will still be my fault!) I have now also filed feedback FB15252677 😊

What's the correct way to check for unavailable API?
 
 
Q