What’s New in Swift 4.1?
Swift 4.1 is here! What does it mean for you? In this article, you’ll learn about the most significant changes introduced in Swift 4.1 and the impact they will have on your code. By Cosmin Pupăză.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
What’s New in Swift 4.1?
25 mins
- Getting Started
- Language Improvements
- Conditional Conformance
- Convert Between Camel Case and Snake Case During JSON Encoding
- Equatable and Hashable Protocols Conformance
- Hashable Index Types
- Recursive Constraints on Associated Types in Protocols
- Weak and Unowned References in Protocols
- Index Distances in Collections
- Structure Initializers in Modules
- Platform Settings and Build Configuration Updates
- Build Imports
- Target Environments
- Miscellaneous Bits and Pieces
- Compacting Sequences
- Unsafe Pointers
- New Playground Features
- Where to Go From Here?
Structure Initializers in Modules
Adding properties to public
structs could lead to source-breaking changes in Swift 4. For this tutorial, make sure the Project Navigator is visible in Xcode by going to View\Navigators\Show Project Navigator. Next, right-click on Sources and select New File from the menu. Rename the file DiceKit.swift. Replace its contents with the following block of code:
public struct Dice {
public let firstDie: Int
public let secondDie: Int
public init(_ value: Int) {
let finalValue: Int
switch value {
case ..<1:
finalValue = 1
case 6...:
finalValue = 6
default:
finalValue = value
}
firstDie = finalValue
secondDie = 7 - finalValue
}
}
The struct's initializer makes sure both dice have valid values between 1 and 6. Switch back to the playground and add this code at the end of it:
// 1
let dice = Dice(0)
dice.firstDie
dice.secondDie
// 2
extension Dice {
init(_ firstValue: Int, _ secondValue: Int) {
firstDie = firstValue
secondDie = secondValue
}
}
// 3
let newDice = Dice(0, 7)
newDice.firstDie
newDice.secondDie
Here's what you did with this code:
- You created a valid pair of dice.
- You extended
Dice
with another initializer that has direct access to its properties. - You defined an invalid pair of dice with the struct's new initializer.
In Swift 4.1, cross-target initializers should call the default one. Change your extension on Dice
to:
extension Dice {
init(_ firstValue: Int, _ secondValue: Int) {
self.init(abs(firstValue - secondValue))
}
}
This change makes structs behave like classes: cross-module initializers must be convenience initializers in Swift 4.1 [SE-0189].
Platform Settings and Build Configuration Updates
Swift 4.1 adds some much-needed platform and build features for code testing:
Build Imports
In Swift 4, you tested if a module was available on a certain platform by checking the operating system itself; for example:
#if os(iOS) || os(tvOS)
import UIKit
print("UIKit is available on this platform.")
#else
print("UIKit is not available on this platform.")
#endif
UIKit
is available on iOS and tvOS, so you imported it if the test succeeded. Swift 4.1 further simplifies this by letting you check for the module itself instead:
#if canImport(UIKit)
print("UIKit is available if this is printed!")
#endif
In Swift 4.1, you use #if canImport(UIKit)
to confirm a certain framework is available for importing [SE-0075].
Target Environments
When writing Swift 4 code, the most well-known way to check whether you were running on a simulator or a physical device was by checking both the architecture and operation system:
#if (arch(i386) || arch(x86_64)) && (os(iOS) || os(tvOS) || os(watchOS))
print("Testing in the simulator.")
#else
print("Testing on the device.")
#endif
If your architecture was Intel-based and your operating system was iOS, tvOS or watchOS, you were testing in the simulator. Otherwise, you were testing on the device.
This test was very cumbersome and was also very non-descriptive of the issue at hand. Swift 4.1 makes this test much more straightforward; just use targetEnvironment(simulator)
[SE-0190] like so:
#if targetEnvironment(simulator)
print("Testing in the simulator.")
#endif
Miscellaneous Bits and Pieces
There are a few other updates in Swift 4.1 that are worth knowing:
Compacting Sequences
In Swift 4, it was fairly common to use flatMap(_:)
to filter out nil
values from a sequence:
let pets = ["Sclip", nil, "Nori", nil]
let petNames = pets.flatMap { $0 } // ["Sclip", "Nori"]
Unfortunately, flatMap(_:)
was overloaded in various ways and, in that specific scenario, the flatMap(_:)
naming wasn't very descriptive of the action taken.
For these reasons, Swift 4.1 introduces a rename of flatMap(_:)
to compactMap(_:)
to make its meaning clearer and unique [SE-0187]:
let petNames = pets.compactMap { $0 }
Unsafe Pointers
Swift 4 used temporary unsafe mutable pointers to create and mutate unsafe mutable buffer pointers:
let buffer = UnsafeMutableBufferPointer<Int>(start: UnsafeMutablePointer<Int>.allocate(capacity: 10),
count: 10)
let mutableBuffer = UnsafeMutableBufferPointer(start: UnsafeMutablePointer(mutating: buffer.baseAddress),
count: buffer.count)
Swift 4.1 lets you work with unsafe mutable buffer pointers directly, using the same approach as with unsafe mutable pointers [SE-0184]:
let buffer = UnsafeMutableBufferPointer<Int>.allocate(capacity: 10)
let mutableBuffer = UnsafeMutableBufferPointer(mutating: UnsafeBufferPointer(buffer))
New Playground Features
Swift 4 allowed you to customize type descriptions in Xcode playgrounds:
class Tutorial {}
extension Tutorial: CustomPlaygroundQuickLookable {
var customPlaygroundQuickLook: PlaygroundQuickLook {
return .text("raywenderlich.com tutorial")
}
}
let tutorial = Tutorial()
You implemented CustomPlaygroundQuickLookable
for Tutorial
to return a custom quick-look playground description. The description’s type in customPlaygroundQuickLook
was limited to PlaygroundQuickLook
cases. This is no longer the case (pun intended) in Swift 4.1:
extension Tutorial: CustomPlaygroundDisplayConvertible {
var playgroundDescription: Any {
return "raywenderlich.com tutorial"
}
}
You implement CustomPlaygroundDisplayConvertible
this time. The description’s type is Any
now, so you can return anything from playgroundDescription
. This simplifies your code and makes it more flexible [SE-0198].
Where to Go From Here?
You can download the final playground using the Download Materials link at either the top or bottom of this tutorial.
Swift 4.1 polishes up some Swift 4 features in preparation for more serious changes that will be coming in Swift 5 later this year. These include ABI stability, improved generics and strings, new memory ownership and concurrency models and more.
If you're feeling adventurous, head over and look at the Swift standard library diffs or to the official Swift CHANGELOG where you can read more information about all changes in this version. You can also use this to keep an eye out for what's coming in Swift 5!
If you're curious about what changes are coming in Swift 5 and beyond, we also recommend that you check out Swift Evolution proposals, where you can see which new features, changes and additions are being proposed. If you're really keen, why not give feedback on one of the current proposals under review or even pitch a proposal yourself!
What do you like or dislike about Swift 4.1 so far? Let us know in the forum discussion below!