How can I use conditional code for a new OS version while building with the current SDK?

While iOS 14 and macOS Big Sur 11 are in public beta, I'd like to add some conditional code to work around significant issues. I'm still building my app with the current SDKs because I plan to release updates before the fall, when the new OS versions are released.

Some time back, the recommended way to handle this in Objective-C was to check if a class existed before using it:

Code Block
if (NSClassFromString(@"UINewClassName"))


Or you could check if a class had been added to an existing method:

Code Block
if ([object respondsToSelector:@selector(newMethodName:)]) {



Then at some point we got #available in Swift and @available in Objective-C, which is now the recommended way to handle this:

Code Block
if #available(iOS 14, *)

Code Block
if (@available(iOS 14.0, *))


This works great if you are building your app with the iOS 14 SDK, but from what I can tell the condition is simply ignored if you are building with an older SDK, at least in Swift.

I could go back the old NSClassFromString or respondsToSelector: approach, but I would prefer to handle this in Swift if it's possible. I also have some cases where there's not really a new class or method to check for—it's just new behavior I want to accommodate. What's the best way to handle these cases?

Replies

You can check for the 5.3 compiler if your goal is to continue using older versions of the SDK (and thus Xcode), but still add functionality that will get enabled When re-compiled with the newer SDK:

Code Block
#if compiler(>=5.3)
if @available(macOS 10.16, *) {
    // use some new API
}
#endif


If you want dynamic checks though, NSClassFromString() is probably your best bet, especially when developing from an SDK that isn’t in the released version of Xcode 😔
I think @dimitri's answer is correct, and if at all possible it's definitely better to use a compiler check or preprocessor flag as a way to prepare in advance for a new SDK. This is safer because APIs can change during betas, and an approach like this will allow the compiler to make sure your code is correct when you build with the newer SDK.

In my case I want to improve how a change in functionality is handled in the current version of my app, building with the current SDK. I was leaning toward using NSClassFromString() or responds(to:), both of which are available in Swift. But after thinking it over I don't think it makes sense to use those unless that's exactly what I need to check for—and in my case, it's not.

I eventually found an NSHipster article titled "Swift System Version Checking" (which the forum won't allow me to link to for some reason). One of several options it suggests is isOperatingSystemAtLeast(_:) which seems like a good choice for handling a change in behavior rather than a change in API:

Code Block
if ProcessInfo.processInfo.isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 14, minorVersion: 0, patchVersion: 0)) {
print("Seems to be iOS 14!")
}

@dimitri #if compiler(>=5.3) this checking runs only in Swift. How can I check in objective-C side code?

  • If you want Objective-C code to only be compiled if linking against the iOS 16 SDK:

    #ifdef __IPHONE_16_0 //...code here #endif
Add a Comment