Upgrading To Swift 4.2Jun 25, 2018 · 4 minute read
If you are upgrading to Xcode 10 and migrating to Swift 4.2 you are likely to see a number of errors because Swift 4.2 has obsoleted and renamed constants and types. It is not as bad as the great renaming of Swift 3 but there is still some maintenance and busy work to do to get back to a clean build.
Open an Xcode 9 project in Xcode 10 and it will warn you that a conversion to Swift 4.2 is available:
You can also manually change the Swift Language Version in the build settings for a target:
Xcode 10 uses Swift 4.2 by default for new projects. Apple has also stated that this will be the last Xcode release to support Swift 3 mode. So if you plan on maintaining Swift 3 code for a while you may want to hang onto a copy of Xcode 10.
One word of warning from the release notes if you do the conversion to Swift 4.2 with an early Xcode 10 beta:
Swift 4.2 gates important enhancements to the SDK, with some of those enhancements landing in a later beta. While all the new APIs and language features are available without migration, developers must migrate to incorporate the enhancements. Migrating early will identify issues that can be corrected in later betas, but will forego automatic migration of those later SDK changes. (39498127)
So if you run the migrator with an early beta you may have to manually migrate language features that ship in a later beta:
Workaround: Wait until a later beta to try migration or re-run the migrator with non-migrated sources.
The Swiftification of UIKit
The WWDC session by Josh Shaffer on What’s New in Cocoa Touch summarises the main changes. They are not as dramatic as the Swift 3 migration but are a further application of the Swift API guidelines to UIKit and other core frameworks.
In particular there has been a more thorough audit and cleanup of global types, constants and functions:
Global types are now nested under the appropriate class. For example the application state type
UIApplicationState is now nested under the
// Swift 4.1 let appState = UIApplicationState.active // Swift 4.2 let appState = UIApplication.State.active
For me, a bigger improvement is the nesting of global constants under their appropriate types. So
My favourite of these changes are the notification names which I struggled to remember I needed to prefix with
Notification.Name after the last renaming. So
// Swift 4.1 NotificationCenter.default.addObserver(self, selector: #selector(didChangeTextSize(_:)), name: Notification.Name.UIContentSizeCategoryDidChange, object: nil) // Swift 4.2 NotificationCenter.default.addObserver(self, selector: #selector(didChangeTextSize(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil)
What I want from these renaming exercises is that it makes it easier for autocomplete to help me. Typing “contentcategory” in that last example leads me to
UIContentSizeCategory which then leads me to the notification name. Time will tell if I find this easier or not.
Another example for the application state notifications.
// Swift 4.1 NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: Notification.Name.UIApplicationWillEnterForeground, object: nil) // Swift 4.2 NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
Some odd global functions are now more cleanly nested as methods under the appropriate type. So to inset a
CGRect with a
UIEdgeInsets used the clumsy
UIEdgeInsetsInsetRect(CGRect,UIEdgeInsets) in Swift 4.1.
This becomes a member function of
CGRect in Swift 4.2 making for cleaner code that autocomplete can help with:
// Swift 4.1 let container = CGRect(x: 0, y: 0, width: 100, height: 100) let margin = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) let content = UIEdgeInsetsInsetRect(container, margin) // Swift 4.2 let container = CGRect(x: 0, y: 0, width: 100, height: 100) let margin = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) let content = container.inset(by: margin)