Monday, July 31, 2017

Support multiple iOS SDK versions when nullability of a protocol changes

Leave a Comment

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?

2 Answers

Answers 1

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?.)

Answers 2

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?

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment