18

In Objective-C, if I wanted to use a specific class that's only present in a new version of iOS, I would do something like this:

if( [UIBlurEffect class] ) {
  // do something with UIBlurEffect
}
else {
  // gracefully fallback to old behavior
}

However, the equivalent Swift:

if UIBlurEffect.self != nil {
  let blur: UIBlurEffect = UIBlurEffect(...)
  // ...
else {
  // ...
}

// also occurs with NSClassFromString("UIBlurEffect")

doesn't have the same functionality.

If run on an environment where NSNewFeature is available, everything is fine. But if the class isn't defined, I get a link error when starting the application:

dyld: Symbol not found: _OBJC_CLASS_$_UIBlurEffect

So how do I do weak linking in Swift?

Edit Added UIBlurEffect as specific example.

Bill
  • 44,502
  • 24
  • 122
  • 213
  • possible duplicate of [What is the Swift preprocessor equivalent to iOS version check comparison?](http://stackoverflow.com/questions/24166919/what-is-the-swift-preprocessor-equivalent-to-ios-version-check-comparison) – user102008 Jul 06 '14 at 05:53
  • I guess you should use optional, something like if let featureAvailable = NSNewFeature? {} else {} . But I'm pretty new to swift – Andrea Jul 06 '14 at 07:41
  • Sorry NSNewFeature.self – Andrea Jul 06 '14 at 07:48
  • 3
    @user102008 Not a dupe - I want to conditionally use individual classes – Bill Jul 06 '14 at 12:18
  • Should it not be [NSNewFeature class] rather than [NSNewFeatureClass class] ? or Just try the other syntax: Class cls = NSClassFromString (@"NSRegularExpression"); if (cls) { // Create an instance of the class and use it. } else { // Alternate code path to follow when the // class is not available. } – Tommie C. Jul 06 '14 at 14:51
  • It's a made-up name. Either way. – Bill Jul 06 '14 at 14:52
  • @Bill: But it's the same problem. In both questions, the issue is that having the class in the code causes an exception when the app starts, even when you don't use it at runtime. – user102008 Jul 08 '14 at 22:37
  • This seems to be fixed in Xcode 6 beta 6 – user102008 Aug 19 '14 at 06:34
  • I don't think Beta6 is available yet, right? – Bill Aug 19 '14 at 13:53

2 Answers2

7

Seems like I've figured out what you can do

  1. I used NSClassFromString() to check if class is available on device, i.e.

    if NSClassFromString("UIBlurEffect") {
        let blur = UIBlurEffect(...)
        //...
    }
    else {
        //...
    }
    
  2. It's needed to make UIKit.framework (or another corresponding framework) optional. If you create Swift-based application in XCode6-BetaX, all the frameworks wouldn't be explicitly added to the link build phase so you need to go to your target settings, add UIKit.framework as a linked framework (in 'Link Binary With Libraries' section) and to change its status to Optional. This step does the trick and I've managed to run version specific code without a problem.

Update: You don't need to make it optional anymore, since Xcode 6 beta 6 (via @user102008)

Update 2: You can't actually perform implicit if statement checks for nil (since Xcode 6 Beta 5). You need to assert it like that:

    if NSClassFromString("UIBlurEffect") != nil {
        let blur = UIBlurEffect(...)
        //...
    }
    else {
        //...
    }

(via @daniel-galasko)

2

Just to add my two cents since I ended up using this to my immediate dismay upon distributing the app to our testers. There is a bug in the Swift compiler (Xcode <= 6.1.1) whereby when building in Release mode the compiler actually doesn't return nil when calling NSClassFromString.

To check me simply change your configuration to Release and see how she crashes.

I ended up having to explicitly check for iOS versions or a simple respondsToSelector call in other place.

So where my code looked like this:

    if NSClassFromString("UIVisualEffectView") != nil {
        //use the blur safely
    } else {
        //abandon blur! try something safer
    }

I ended up having to use this instead

switch UIDevice.currentDevice().systemVersion.compare("8.0.0", options: NSStringCompareOptions.NumericSearch) {
        case .OrderedSame, .OrderedDescending:
            //go wild with blur
        case .OrderedAscending:
            //blur shall not pass here!
        }

This question dealt with the same issue - UIAlertView is crashing app on iOS 7

Community
  • 1
  • 1
Daniel Galasko
  • 23,617
  • 8
  • 77
  • 97