What’s New in Swift 4?
Learn about what is new in Swift 4. By Eric Cerney.
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?
30 mins
- Getting Started
- Migrating to Swift 4
- API Changes
- Strings
- Dictionary and Set
- Private Access Modifier
- API Additions
- Archival and Serialization
- Key-Value Coding
- Multi-line String Literals
- One-Sided Ranges
- Generic Subscripts
- Miscellaneous
- MutableCollection.swapAt(_:_:)
- Associated Type Constraints
- Class and Protocol Existential
- Limiting @objc Inference
- NSNumber Bridging
- Swift Package Manager
- Still In Progress
- Where to Go From Here?
Note: This tutorial uses the Swift 4 version bundled into Xcode 9 beta 1.
Note: This tutorial uses the Swift 4 version bundled into Xcode 9 beta 1.
Swift 4 is the latest major release from Apple scheduled to be out of beta in the fall of 2017. Its main focus is to provide source compatibility with Swift 3 code as well as working towards ABI stability.
This article highlights changes to Swift that will most significantly impact your code. And with that, let’s get started!
Getting Started
Swift 4 is included in Xcode 9. You can download the latest version of Xcode 9 from Apple’s developer portal (you must have an active developer account). Each Xcode beta will bundle the latest Swift 4 snapshot at the time of release.
As you’re reading, you’ll notice links in the format of [SE-xxxx]. These links will take you to the relevant Swift Evolution proposal. If you’d like to learn more about any topic, make sure to check them out.
I recommend trying each Swift 4 feature or update in a playground. This will help cement the knowledge in your head and give you the ability to dive deeper into each topic. Play around with the examples by trying to expand/break them. Have fun with it!
Note: This article will be updated for each Xcode beta. If you use a different Swift snapshot, the code here is not guaranteed to work.
Note: This article will be updated for each Xcode beta. If you use a different Swift snapshot, the code here is not guaranteed to work.
Migrating to Swift 4
The migration from Swift 3 to 4 will be much less cumbersome than from 2.2 to 3. In general, most changes are additive and shouldn’t need a ton of personal touch. Because of this, the Swift migration tool will handle the majority of changes for you.
Xcode 9 simultaneously supports both Swift 4 as well as an intermediate version of Swift 3 in Swift 3.2. Each target in your project can be either Swift 3.2 or Swift 4 which lets you migrate piece by piece if you need to. Converting to Swift 3.2 isn’t entirely free, however – you may need to update parts of your code to be compatible with new SDKs, and because Swift is not yet ABI stable you will need to recompile your dependencies with Xcode 9.
When you’re ready to migrate to Swift 4, Xcode once again provides a migration tool to help you out. In Xcode, you can navigate to Edit/Convert/To Current Swift Syntax… to launch the conversion tool.
After selecting which targets you want to convert, Xcode will prompt you for a preference on Objective-C inferencing. Select the recommended option to reduce your binary size by limiting inferencing (for more on this topic, check out the Limiting @objc Inference below)
To better understand what changes to expect in your code, we’ll first cover API changes in Swift 4.
API Changes
Before jumping right into additions introduced in Swift 4, let’s first take a look at what changes/improvements it makes to existing APIs.
Strings
String
is receiving a lot of well deserved love in Swift 4. This proposal contains many changes, so let’s break down the biggest. [SE-0163]:
In case you were feeling nostalgic, strings are once again collections like they were pre Swift 2.0. This change removes the need for a characters
array on String
. You can now iterate directly over a String
object:
let galaxy = "Milky Way 🐮"
for char in galaxy {
print(char)
}
Not only do you get logical iteration through String
, you also get all the bells and whistles from Sequence
and Collection
:
galaxy.count // 11
galaxy.isEmpty // false
galaxy.dropFirst() // "ilky Way 🐮"
String(galaxy.reversed()) // "🐮 yaW ykliM"
// Filter out any none ASCII characters
galaxy.filter { char in
let isASCII = char.unicodeScalars.reduce(true, { $0 && $1.isASCII })
return isASCII
} // "Milky Way "
The ASCII example above demonstrates a small improvement to Character
. You can now access the UnicodeScalarView
directly from Character
. Previously, you needed to instantiate a new String
[SE-0178].
Another addition is StringProtocol
. It declares most of the functionality previously declared on String
. The reason for this change is to improve how slices work. Swift 4 adds the Substring
type for referencing a subsequence on String
.
Both String
and Substring
implement StringProtocol
giving them almost identical functionality:
// Grab a subsequence of String
let endIndex = galaxy.index(galaxy.startIndex, offsetBy: 3)
var milkSubstring = galaxy[galaxy.startIndex...endIndex] // "Milk"
type(of: milkSubstring) // Substring.Type
// Concatenate a String onto a Substring
milkSubstring += "🥛" // "Milk🥛"
// Create a String from a Substring
let milkString = String(milkSubstring) // "Milk🥛"
Another great improvement is how String
interprets grapheme clusters. This resolution comes from the adaptation of Unicode 9. Previously, unicode characters made up of multiple code points resulted in a count
greater than 1. A common situation where this happens is an emoji with a selected skin-tone. Here are a few examples showing the before and after behavior:
"👩💻".count // Now: 1, Before: 2
"👍🏽".count // Now: 1, Before: 2
"👨❤️💋👨".count // Now: 1, Before, 4
This is only a subset of the changes mentioned in the String Manifesto. You can read all about the original motivations and proposed solutions you’d expect to see in the future.
Dictionary and Set
As far as Collection
types go, Set
and Dictionary
aren’t always the most intuitive. Lucky for us, the Swift team gave them some much needed love with [SE-0165].
Sequence Based Initialization
First on the list is the ability to create a dictionary from a sequence of key-value pairs (tuple):
let nearestStarNames = ["Proxima Centauri", "Alpha Centauri A", "Alpha Centauri B", "Barnard's Star", "Wolf 359"]
let nearestStarDistances = [4.24, 4.37, 4.37, 5.96, 7.78]
// Dictionary from sequence of keys-values
let starDistanceDict = Dictionary(uniqueKeysWithValues: zip(nearestStarNames, nearestStarDistances))
// ["Wolf 359": 7.78, "Alpha Centauri B": 4.37, "Proxima Centauri": 4.24, "Alpha Centauri A": 4.37, "Barnard's Star": 5.96]
Duplicate Key Resolution
You can now handle initializing a dictionary with duplicate keys any way you’d like. This helps avoid overwriting key-value pairs without any say in the matter:
// Random vote of people's favorite stars
let favoriteStarVotes = ["Alpha Centauri A", "Wolf 359", "Alpha Centauri A", "Barnard's Star"]
// Merging keys with closure for conflicts
let mergedKeysAndValues = Dictionary(zip(favoriteStarVotes, repeatElement(1, count: favoriteStarVotes.count)), uniquingKeysWith: +) // ["Barnard's Star": 1, "Alpha Centauri A": 2, "Wolf 359": 1]
The code above uses zip
along with the shorthand +
to resolve duplicate keys by adding the two conflicting values.
zip
, you can quickly learn about it in Apple’s Swift Documentation
Filtering
Both Dictionary
and Set
now have the ability to filter results into a new object of the original type:
// Filtering results into dictionary rather than array of tuples
let closeStars = starDistanceDict.filter { $0.value < 5.0 }
closeStars // Dictionary: ["Proxima Centauri": 4.24, "Alpha Centauri A": 4.37, "Alpha Centauri B": 4.37]
Dictionary Mapping
Dictionary
gained a very useful method for directly mapping its values:
// Mapping values directly resulting in a dictionary
let mappedCloseStars = closeStars.mapValues { "\($0)" }
mappedCloseStars // ["Proxima Centauri": "4.24", "Alpha Centauri A": "4.37", "Alpha Centauri B": "4.37"]
Dictionary Default Values
A common practice when accessing a value on Dictionary
is to use the nil coalescing operator to give a default value in case the value is nil
. In Swift 4, this becomes much cleaner and allows you to do some awesome in line mutation:
// Subscript with a default value
let siriusDistance = mappedCloseStars["Wolf 359", default: "unknown"] // "unknown"
// Subscript with a default value used for mutating
var starWordsCount: [String: Int] = [:]
for starName in nearestStarNames {
let numWords = starName.split(separator: " ").count
starWordsCount[starName, default: 0] += numWords // Amazing
}
starWordsCount // ["Wolf 359": 2, "Alpha Centauri B": 3, "Proxima Centauri": 2, "Alpha Centauri A": 3, "Barnard's Star": 2]
Previously this type of mutation would need wrapping in a bloated if-let
statement. In Swift 4 it's possible all in a single line!
Dictionary Grouping
Another amazingly useful addition is the ability to initialize a Dictionary
from a Sequence
and to group them into buckets:
// Grouping sequences by computed key
let starsByFirstLetter = Dictionary(grouping: nearestStarNames) { $0.first! }
// ["B": ["Barnard's Star"], "A": ["Alpha Centauri A", "Alpha Centauri B"], "W": ["Wolf 359"], "P": ["Proxima Centauri"]]
This comes in handy when grouping data by a specific pattern.
Reserving Capacity
Both Sequence
and Dictionary
now have the ability to explicitly reserve capacity.
// Improved Set/Dictionary capacity reservation
starWordsCount.capacity // 6
starWordsCount.reserveCapacity(20) // reserves at _least_ 20 elements of capacity
starWordsCount.capacity // 24
Reallocation can be an expensive task on these types. Using reserveCapacity(_:)
is an easy way to improve performance when you have an idea how much data it needs to store.
That was a ton of info, so definitely check out both types and look for ways to use these additions to spice up your code.