Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Support multiple iOS SDK versions when nullability of a protocol changes

The MCSessionDelegate protocol has changed in iOS 11 from

- (void)                    session:(MCSession *)session
 didFinishReceivingResourceWithName:(NSString *)resourceName
                           fromPeer:(MCPeerID *)peerID
                              atURL:(NSURL *)localURL
                          withError:(nullable NSError *)error;

to

- (void)                    session:(MCSession *)session
 didFinishReceivingResourceWithName:(NSString *)resourceName
                           fromPeer:(MCPeerID *)peerID
                              atURL:(nullable NSURL *)localURL
                          withError:(nullable NSError *)error;

This causes that, when implementing this delegate in Swift, using

func session(_ session: MCSession,
             didFinishReceivingResourceWithName resourceName: String,
             fromPeer peerID: MCPeerID,
             at localURL: URL?,
             withError error: Error?) {}

won't compile on Xcode 8; and

func session(_ session: MCSession,
             didFinishReceivingResourceWithName resourceName: String,
             fromPeer peerID: MCPeerID,
             at localURL: URL,
             withError error: Error?) {}

won't compile on Xcode 9.

In both cases Xcode shows this error:

Parameter of 'session(_:didFinishReceivingResourceWithName:fromPeer:at:withError:)' has different optionality than required by protocol 'MCSessionDelegate'


How to make it compile on both versions?

like image 562
Ricardo Sanchez-Saez Avatar asked Jun 09 '17 21:06

Ricardo Sanchez-Saez


2 Answers

I don't see why this is a problem.

If you build with Xcode 8, you can use the old method signature, build you app and submit it to the AppStore. The app will be built against the iOS10 SDK, and will run on iOS10 and iOS11 devices.

When you switch to Xcode 9, you can switch to the new method signature, and (when Xcode 9 is out of beta) submit to the AppStore. The app is built against iOS11 SDK and will run on iOS11 devices.

The only difficulty is that brief period when you might want to use both Xcode 8 (to release app updates now) and Xcode 9 (preparing for app releases after iOS11 is released). You'd need to have a separate iOS11 branch in your git repo - but you'll be doing that anyway, right?

like image 90
Matthew Avatar answered Oct 31 '22 10:10

Matthew


To improve code with mix & match of XCode versions , you can put check for swift versions like

#if swift(>=2.3)
let specifier = url.resourceSpecifier ?? ""
#else
let specifier = url.resourceSpecifier
#endif

But here’s a little helper that might be useful ..found on http://radex.io/xcode7-xcode8/:

func optionalize<T>(x: T?) -> T? {
    return x
}

I know, it’s a little weird. Perhaps it will be easier to explain if you first see the result:

let URL = optionalize(url) ?? "" // works on both versions!

We’re taking advantage of Optional lifting to get rid of ugly conditional compilation at call site. See, what the optionalize() function does is it turns whatever you pass in into an Optional, unless it’s already an Optional, in which case, it just returns the argument as-is. This way, regardless if the url is optional (Xcode 8) or not (Xcode 7), the “optionalized” version is always the same.

(To explain in more detail: in Swift, Foo can be considered a subtype of Foo?, since you can wrap any value of Foo in an Optional without loss of information. And since the compiler knows about this, it allows you to pass a non-optional in place of an optional argument — lifting Foo into Foo?.)

like image 1
Ellen Avatar answered Oct 31 '22 12:10

Ellen