What's new in Swift 4.1?
- Transfer
Xcode 9.3 and Swift 4.1 are finally no longer beta! This release contains the long-awaited improvements to the standard library and the language itself. If you are not following the Swift Evolution process , then this article is for you.
In this article, you will learn about the most significant changes introduced in Swift 4.1 .
This article requires Xcode 9.3, so make sure that this version of Xcode is installed.
Swift 4.1 is compatible with the source code of Swift 4, so new features do not violate your code if you have already migrated your project to Swift 4 using Swift Migrator in Xcode.
In the following sections, you will see related tags, such as [SE-0001]. These are Swift Evolution offer numbers . I added a link to each proposal so that you can delve into the full details of each specific change. I recommend that you try the functions in practice using the Playground, in order to better understand what exactly will change in your work.
To get started, launch Xcode 9.3 and select File ▸ New ▸ Playground . Choose iOS as the platform and Blankas a template. Name and save it as you wish. To get the most out of this article, try practicing every feature on the Playground.
Note: If you missed what was changed in Swift 4 and are going to catch up? No problems! Check out Swift 4 by reading What's New in Swift 4 .
This release has a number of language improvements, including conditional matching, recursive restrictions on related types in protocols, and more.
Conditional matching allows protocol matching for generic types, where type arguments satisfy certain conditions [SE-0143] . This is a powerful feature that makes code more flexible. You can see how it works with a few examples.
In Swift 4, you can compare arrays, dictionaries, and options if their elements comply with the Equatable protocol . This worked perfectly fine for basic scenarios such as:
Using the == operator to verify equality in these examples was quite fair, since Int is Equatable in Swift 4. However, comparing option sets is a common situation that you might encounter in Swift 4, since options are not compliant with the Equatable protocol . Swift 4.1 corrects this problem using conditional matching, allowing you to compare additional types with those that underlie Equatable :
Int? is Equatable in Swift 4.1, so the == operator works for [Int?], [String: Int?] and Int ?? .
A similar problem was solved when comparing arrays (for example, [[Int]]). In Swift 4, you can compare arrays of sets (for example, [Set]), since the sets conform to the Equatable protocol . Swift 4.1 solves this, because arrays (and dictionaries), as well as their underlying values, are Equatable .
Typically, Optional , Array, and Dictionary in Swift 4.1 now conform to the Equatable and Hashable protocols , whenever their base values or elements conform to these protocols.
Here is an example of how conditional matching works in a standard library. Then you implement this in your code.
We are now using conditional matching to create our own musical instrument group. Add the following block of code to the Playground .
Here is what this code does step by step:
Then declare the tool group:
Here is what you do step by step:
Then create your favorite tool groups and compare them:
In this code snippet, you create two Keyboards and a Guitar along with their respective Bands. Then you compare the Bands directly, thanks to the conditional matching that you defined earlier.
In Swift 4.1, arrays, dictionaries, sets, and add-ons conform to the Codable protocol if their elements also conform to this protocol. Add the following code to the Playground:
Do you use this code for encode [Student] , [String: Student] , Set and Student? . This code works well in Swift 4.1 since Student is Codable , which makes these collection types also Codable appropriate .
Swift 4.1 allows you to convert CamelCase properties to snake_case keys during JSON parsing:
When you create an encoder object, you set the keyEncodingStrategy property to .convertToSnakeCase . Looking at the console, you should see:
You can also convert back from snake_case to CamelCase while working with JSON:
This time, for the keyDecodingStrategy property , you assign the value .convertFromSnakeCase .
Swift 4 required you to write boilerplate code so that the structures conform to the Equatable and Hashable protocols :
Using this code, you implement == (lhs: rhs :) and hashValue to support both Equatable and Hashable . You can compare Country objects , add them to Set’s and even use them as keys for a dictionary:
Swift 4.1 adds default implementations for the structures corresponding to Equatable and Hashable , since all its properties are also Equalable and Hashable [ SE-0185 ].
This greatly simplifies your code, which can simply be rewritten as:
Enumerations with related values also require additional code to work with Equatable and Hashable in Swift 4:
You used enumeration cases to write implementations == (lhs: rhs :) and hashValue . This allowed you to compare blog posts and use them in sets and dictionaries:
Unlike Hashable , the size of this code is significantly smaller in Swift 4.1, thanks to the implementations of Equatable and Hashable :
You just saved yourself from working with 20 lines of template code!
Key paths could use indexes if the index parameter type was Hashable in Swift 4. This allowed them to work with double arrays; eg:
You use keyPath to get the current Swift version number from swiftVersions .
Swift 4.1 adds Hashable matching to all subscript types in the standard library [ SE-0188 ]:
By index, the first letter of the string is returned. It works because String index types are Hashable in Swift 4.1.
Swift 4 did not support defining recursive constraints on related types in protocols:
In this example, you have identified the type associated with the SmartPhone, but it might be useful to limit it to Phone, since all smartphones are phones. Now it is possible in Swift 4.1 [ SE-0157 ]:
You use where to restrict both Version and SmartPhone so that they are the same as the phone.
Swift 4 supports weak and non- unowned protocol properties:
You tuned the instrument in a specific key and pitch. The step may have been zero, so you will simulate it as weak in the Tune protocol.
But both weak and unowned are almost meaningless if they are defined in the protocol itself, so Swift 4.1 deletes them, and you will receive a warning using these keywords in the protocol [ SE-0186 ]:
Swift 4 used IndexDistance to declare the number of elements in the collection:
The typeOfCollection (_ :) method returns a tuple that contains the type and amount of the collection. You can use it for any collections, such as arrays, dictionaries or collections; eg:
You can improve the return type of a function by restricting IndexDistance to Int with a where clause :
Swift 4.1 replaces IndexDistance with Int in the standard library, so in this case you do not need the where clause [ SE-0191 ]:
Adding properties to public structures can lead to initial changes in Swift 4. In this article, make sure Project Navigator is visible in Xcode by going to View \ Navigators \ Show Project Navigator. Then right-click “Sources” and select “New File” from the menu. Rename the DiceKit.swift file . Replace its contents with the following code block:
The structure initializer ensures that both dice have valid values between 1 and 6. Return to the Playground and add this code at the end:
Here is what you did with this code:
In Swift 4.1, cross-target initializers must cause a default value. Change the Dice extension to:
This change causes structures to behave like classes: cross-module initializers must be convenience initializers in Swift 4.1 [ SE-0189 ].
In Swift 4.1, you can no longer cheat the dice game!
Swift 4.1 adds some of the necessary platform and build features for code testing:
In Swift 4, you tested a module if it is available on a specific platform, defining the operating system itself: for example:
UIKit is available on iOS and tvOS , so you imported it if the test was successful. Swift 4.1 simplifies this process by letting you check the module itself:
In Swift 4.1, you use #if canImport (UIKit) to confirm that a specific structure is available for import [ SE-0075 ].
When writing code in Swift 4, the most famous way to check code execution on a simulator or physical device was to check the architecture and operating system:
Was your processor architecture based on Intel, and your operating system - iOS, tvOS or watchOS, you tested in the simulator. Otherwise, you tested the device.
This test was very cumbersome, and it also did not fully describe the type of errors. Swift 4.1 makes this test easier; just use targetEnvironment (simulator) [SE-0190] as follows:
There are a few other updates to Swift 4.1 that are worth knowing:
In Swift 4, it was fairly common to use flatMap (_ :) to filter nil values from a sequence:
Unfortunately, flatMap (_ :) was overloaded in various ways and, in this particular scenario, the assignment of flatMap (_ :) did not describe the actions taken very well.
For these reasons, Swift 4.1 introduces the renaming of flatMap (_:) to compactMap (_ :) to make its meaning more clear and unique [ SE-0187 ]:
Swift 4 used temporary unsafe mutable pointers to create and modify unsafe mutable buffer pointers:
Swift 4.1 allows you to work with unsafe mutable buffer pointers directly, using the same approach as with unsafe mutable buffer pointers [ SE-0184 ]:
Swift 4 allowed you to customize type descriptions in Playground Xcode:
You have implemented CustomPlaygroundQuickLookable for the Tutorial and return a short description. The description type in customPlaygroundQuickLook was limited to PlaygroundQuickLook cases . such a pun in Swift 4.1 is no longer there:
This time you are implementing CustomPlaygroundDisplayConvertible . The description type is Any , so you can return anything from the playgroundDescription. This simplifies your code and makes it more flexible [ SE-0198 ].
Swift 4.1 enhances some of the features of Swift 4 in preparation for the more serious changes that will appear in Swift 5 this year. These include ABI stability, improved generics and strings, new patterns of memory ownership and concurrency, and much more.
If you feel like an adventurer, go and look at the standard Swift library or the official Swift CHANGELOG website , where you can read more information about all the changes in this version.
If you are interested in what changes will be in Swift 5, we also recommend that you familiarize yourself with the Swift Evolution offers , where you can see new features, changes and additions.
In this article, you will learn about the most significant changes introduced in Swift 4.1 .
This article requires Xcode 9.3, so make sure that this version of Xcode is installed.
First steps
Swift 4.1 is compatible with the source code of Swift 4, so new features do not violate your code if you have already migrated your project to Swift 4 using Swift Migrator in Xcode.
In the following sections, you will see related tags, such as [SE-0001]. These are Swift Evolution offer numbers . I added a link to each proposal so that you can delve into the full details of each specific change. I recommend that you try the functions in practice using the Playground, in order to better understand what exactly will change in your work.
To get started, launch Xcode 9.3 and select File ▸ New ▸ Playground . Choose iOS as the platform and Blankas a template. Name and save it as you wish. To get the most out of this article, try practicing every feature on the Playground.
Note: If you missed what was changed in Swift 4 and are going to catch up? No problems! Check out Swift 4 by reading What's New in Swift 4 .
Language improvements
This release has a number of language improvements, including conditional matching, recursive restrictions on related types in protocols, and more.
Conditional Compliance
Conditional matching allows protocol matching for generic types, where type arguments satisfy certain conditions [SE-0143] . This is a powerful feature that makes code more flexible. You can see how it works with a few examples.
Conditional matching in the standard library
In Swift 4, you can compare arrays, dictionaries, and options if their elements comply with the Equatable protocol . This worked perfectly fine for basic scenarios such as:
// Arrays of Int
let firstArray = [1, 2, 3]
let secondArray = [1, 2, 3]
let sameArray = firstArray == secondArray
// Dictionaries with Int values
let firstDictionary = ["Cosmin": 10, "George": 9]
let secondDictionary = ["Cosmin": 10, "George": 9]
let sameDictionary = firstDictionary == secondDictionary
// Comparing Int?
let firstOptional = firstDictionary["Cosmin"]
let secondOptional = secondDictionary["Cosmin"]
let sameOptional = firstOptional == secondOptional
Using the == operator to verify equality in these examples was quite fair, since Int is Equatable in Swift 4. However, comparing option sets is a common situation that you might encounter in Swift 4, since options are not compliant with the Equatable protocol . Swift 4.1 corrects this problem using conditional matching, allowing you to compare additional types with those that underlie Equatable :
// Array of Int?
let firstArray = [1, nil, 2, nil, 3, nil]
let secondArray = [1, nil, 2, nil, 3, nil]
let sameArray = firstArray == secondArray
// Dictionary with Int? values
let firstDictionary = ["Cosmin": 10, "George": nil]
let secondDictionary = ["Cosmin": 10, "George": nil]
let sameDictionary = firstDictionary == secondDictionary
// Comparing Int?? (Optional of Optional)
let firstOptional = firstDictionary["Cosmin"]
let secondOptional = secondDictionary["Cosmin"]
let sameOptional = firstOptional == secondOptional
Int? is Equatable in Swift 4.1, so the == operator works for [Int?], [String: Int?] and Int ?? .
A similar problem was solved when comparing arrays (for example, [[Int]]). In Swift 4, you can compare arrays of sets (for example, [Set]), since the sets conform to the Equatable protocol . Swift 4.1 solves this, because arrays (and dictionaries), as well as their underlying values, are Equatable .
let firstArrayOfSets = [Set([1, 2, 3]), Set([1, 2, 3])]
let secondArrayOfSets = [Set([1, 2, 3]), Set([1, 2, 3])]
// Will work in Swift 4 and Swift 4.1
// since Set is Equatable
firstArrayOfSets == secondArrayOfSets
let firstArrayOfArrays = [[1, 2, 3], [3, 4, 5]]
let secondArrayOfArrays = [[1, 2, 3], [3, 4, 5]]
// Caused an error in Swift 4, but works in Swift 4.1
// since Arrays are Equatable in Swift 4.1
firstArrayOfArrays == secondArrayOfArrays
Typically, Optional , Array, and Dictionary in Swift 4.1 now conform to the Equatable and Hashable protocols , whenever their base values or elements conform to these protocols.
Here is an example of how conditional matching works in a standard library. Then you implement this in your code.
Conditional compliance in practice
We are now using conditional matching to create our own musical instrument group. Add the following block of code to the Playground .
// 1
class LeadInstrument: Equatable {
let brand: String
init(brand: String) {
self.brand = brand
}
func tune() -> String {
return "Standard tuning."
}
static func ==(lhs: LeadInstrument, rhs: LeadInstrument) -> Bool {
return lhs.brand == rhs.brand
}
}
// 2
class Keyboard: LeadInstrument {
override func tune() -> String {
return "Keyboard standard tuning."
}
}
// 3
class Guitar: LeadInstrument {
override func tune() -> String {
return "Guitar standard tuning."
}
}
Here is what this code does step by step:
- The LeadInstrument class conforms to the Equatable protocol. It has a specific brand and a tune () method that you will use to tune the tool.
- You override the tune () method in the Keyboard class to return the default settings for the object.
- You do the same for the Guitar class.
Then declare the tool group:
// 1
class Band {
let name: String
let lead: LeadInstrument
init(name: String, lead: LeadInstrument) {
self.name = name
self.lead = lead
}
}
// 2
extension Band: Equatable where LeadInstrument: Equatable {
static func ==(lhs: Band, rhs: Band) -> Bool {
return lhs.name == rhs.name && lhs.lead == rhs.lead
}
}
Here is what you do step by step:
- You create a class of Band type - LeadInstrument. Each group has a unique name (lead) and lead instrument (main instrument).
- You use where to make the Band conform to the Equatable protocol, just like LeadInstrument does certain things. This is where conditional matching manifests itself - you can assign compliance to the Equatable protocol for the generic LeadInstruments.
Then create your favorite tool groups and compare them:
// 1
let rolandKeyboard = Keyboard(brand: "Roland")
let rolandBand = Band(name: "Keys", lead: rolandKeyboard)
let yamahaKeyboard = Keyboard(brand: "Yamaha")
let yamahaBand = Band(name: "Keys", lead: yamahaKeyboard)
let sameBand = rolandBand == yamahaBand
// 2
let fenderGuitar = Guitar(brand: "Fender")
let fenderBand = Band(name: "Strings", lead: fenderGuitar)
let ibanezGuitar = Guitar(brand: "Ibanez")
let ibanezBand = Band(name: "Strings", lead: ibanezGuitar)
let sameBands = fenderBand == ibanezBand
In this code snippet, you create two Keyboards and a Guitar along with their respective Bands. Then you compare the Bands directly, thanks to the conditional matching that you defined earlier.
Conditional match in JSON parsing
In Swift 4.1, arrays, dictionaries, sets, and add-ons conform to the Codable protocol if their elements also conform to this protocol. Add the following code to the Playground:
struct Student: Codable, Hashable {
let firstName: String
let averageGrade: Int
}
let cosmin = Student(firstName: "Cosmin", averageGrade: 10)
let george = Student(firstName: "George", averageGrade: 9)
let encoder = JSONEncoder()
// Encode an Array of students
let students = [cosmin, george]
do {
try encoder.encode(students)
} catch {
print("Failed encoding students array: \(error)")
}
// Encode a Dictionary with student values
let studentsDictionary = ["Cosmin": cosmin, "George": george]
do {
try encoder.encode(studentsDictionary)
} catch {
print("Failed encoding students dictionary: \(error)")
}
// Encode a Set of students
let studentsSet: Set = [cosmin, george]
do {
try encoder.encode(studentsSet)
} catch {
print("Failed encoding students set: \(error)")
}
// Encode an Optional Student
let optionalStudent: Student? = cosmin
do {
try encoder.encode(optionalStudent)
} catch {
print("Failed encoding optional student: \(error)")
}
Do you use this code for encode [Student] , [String: Student] , Set and Student? . This code works well in Swift 4.1 since Student is Codable , which makes these collection types also Codable appropriate .
Converting between CamelCase and Snake_Case when working with JSON
Swift 4.1 allows you to convert CamelCase properties to snake_case keys during JSON parsing:
var jsonData = Data()
encoder.keyEncodingStrategy = .convertToSnakeCase
encoder.outputFormatting = .prettyPrinted
do {
jsonData = try encoder.encode(students)
} catch {
print(error)
}
if let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
When you create an encoder object, you set the keyEncodingStrategy property to .convertToSnakeCase . Looking at the console, you should see:
[
{
"first_name" : "Cosmin",
"average_grade" : 10
},
{
"first_name" : "George",
"average_grade" : 9
}
]
You can also convert back from snake_case to CamelCase while working with JSON:
var studentsInfo: [Student] = []
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
studentsInfo = try decoder.decode([Student].self, from: jsonData)
} catch {
print(error)
}
for studentInfo in studentsInfo {
print("\(studentInfo.firstName) \(studentInfo.averageGrade)")
}
This time, for the keyDecodingStrategy property , you assign the value .convertFromSnakeCase .
Compliance and Compatibility of Equatable and Hashable Protocols
Swift 4 required you to write boilerplate code so that the structures conform to the Equatable and Hashable protocols :
struct Country: Hashable {
let name: String
let capital: String
static func ==(lhs: Country, rhs: Country) -> Bool {
return lhs.name == rhs.name && lhs.capital == rhs.capital
}
var hashValue: Int {
return name.hashValue ^ capital.hashValue &* 16777619
}
}
Using this code, you implement == (lhs: rhs :) and hashValue to support both Equatable and Hashable . You can compare Country objects , add them to Set’s and even use them as keys for a dictionary:
let france = Country(name: "France", capital: "Paris")
let germany = Country(name: "Germany", capital: "Berlin")
let sameCountry = france == germany
let countries: Set = [france, germany]
let greetings = [france: "Bonjour", germany: "Guten Tag"]
Swift 4.1 adds default implementations for the structures corresponding to Equatable and Hashable , since all its properties are also Equalable and Hashable [ SE-0185 ].
This greatly simplifies your code, which can simply be rewritten as:
struct Country: Hashable {
let name: String
let capital: String
}
Enumerations with related values also require additional code to work with Equatable and Hashable in Swift 4:
enum BlogPost: Hashable {
case tutorial(String, String)
case article(String, String)
static func ==(lhs: BlogPost, rhs: BlogPost) -> Bool {
switch (lhs, rhs) {
case let (.tutorial(lhsTutorialTitle, lhsTutorialAuthor), .tutorial(rhsTutorialTitle,
rhsTutorialAuthor)):
return lhsTutorialTitle == rhsTutorialTitle && lhsTutorialAuthor == rhsTutorialAuthor
case let (.article(lhsArticleTitle, lhsArticleAuthor), .article(rhsArticleTitle, rhsArticleAuthor)):
return lhsArticleTitle == rhsArticleTitle && lhsArticleAuthor == rhsArticleAuthor
default:
return false
}
}
var hashValue: Int {
switch self {
case let .tutorial(tutorialTitle, tutorialAuthor):
return tutorialTitle.hashValue ^ tutorialAuthor.hashValue &* 16777619
case let .article(articleTitle, articleAuthor):
return articleTitle.hashValue ^ articleAuthor.hashValue &* 16777619
}
}
}
You used enumeration cases to write implementations == (lhs: rhs :) and hashValue . This allowed you to compare blog posts and use them in sets and dictionaries:
let swift3Article = BlogPost.article("What's New in Swift 3.1?", "Cosmin Pupăză")
let swift4Article = BlogPost.article("What's New in Swift 4.1?", "Cosmin Pupăză")
let sameArticle = swift3Article == swift4Article
let swiftArticlesSet: Set = [swift3Article, swift4Article]
let swiftArticlesDictionary = [swift3Article: "Swift 3.1 article", swift4Article: "Swift 4.1 article"]
Unlike Hashable , the size of this code is significantly smaller in Swift 4.1, thanks to the implementations of Equatable and Hashable :
enum BlogPost: Hashable {
case tutorial(String, String)
case article(String, String)
}
You just saved yourself from working with 20 lines of template code!
Hashable Index Types
Key paths could use indexes if the index parameter type was Hashable in Swift 4. This allowed them to work with double arrays; eg:
let swiftVersions = [3, 3.1, 4, 4.1]
let path = \[Double].[swiftVersions.count - 1]
let latestVersion = swiftVersions[keyPath: path]
You use keyPath to get the current Swift version number from swiftVersions .
Swift 4.1 adds Hashable matching to all subscript types in the standard library [ SE-0188 ]:
let me = "Cosmin"
let newPath = \String.[me.startIndex]
let myInitial = me[keyPath: newPath]
By index, the first letter of the string is returned. It works because String index types are Hashable in Swift 4.1.
Recursive restrictions on related types in protocols
Swift 4 did not support defining recursive constraints on related types in protocols:
protocol Phone {
associatedtype Version
associatedtype SmartPhone
}
class IPhone: Phone {
typealias Version = String
typealias SmartPhone = IPhone
}
In this example, you have identified the type associated with the SmartPhone, but it might be useful to limit it to Phone, since all smartphones are phones. Now it is possible in Swift 4.1 [ SE-0157 ]:
protocol Phone {
associatedtype Version
associatedtype SmartPhone: Phone where SmartPhone.Version == Version, SmartPhone.SmartPhone == SmartPhone
}
You use where to restrict both Version and SmartPhone so that they are the same as the phone.
Weak and not busy links in protocols
Swift 4 supports weak and non- unowned protocol properties:
class Key {}
class Pitch {}
protocol Tune {
unowned var key: Key { get set }
weak var pitch: Pitch? { get set }
}
class Instrument: Tune {
var key: Key
var pitch: Pitch?
init(key: Key, pitch: Pitch?) {
self.key = key
self.pitch = pitch
}
}
You tuned the instrument in a specific key and pitch. The step may have been zero, so you will simulate it as weak in the Tune protocol.
But both weak and unowned are almost meaningless if they are defined in the protocol itself, so Swift 4.1 deletes them, and you will receive a warning using these keywords in the protocol [ SE-0186 ]:
protocol Tune {
var key: Key { get set }
var pitch: Pitch? { get set }
}
Index Distances in Collections
Swift 4 used IndexDistance to declare the number of elements in the collection:
func typeOfCollection(_ collection: C) -> (String, C.IndexDistance) {
let collectionType: String
switch collection.count {
case 0...100:
collectionType = "small"
case 101...1000:
collectionType = "medium"
case 1001...:
collectionType = "big"
default:
collectionType = "unknown"
}
return (collectionType, collection.count)
}
The typeOfCollection (_ :) method returns a tuple that contains the type and amount of the collection. You can use it for any collections, such as arrays, dictionaries or collections; eg:
typeOfCollection(1...800) // ("medium", 800)
typeOfCollection(greetings) // ("small", 2)
You can improve the return type of a function by restricting IndexDistance to Int with a where clause :
func typeOfCollection(_ collection: C) -> (String, Int) where C.IndexDistance == Int {
// тот же код, что и в приведенном выше примере
}
Swift 4.1 replaces IndexDistance with Int in the standard library, so in this case you do not need the where clause [ SE-0191 ]:
func typeOfCollection(_ collection: C) -> (String, Int) {
// тот же код, что и в приведенном выше примере
}
Structure initializers in modules
Adding properties to public structures can lead to initial changes in Swift 4. In this article, make sure Project Navigator is visible in Xcode by going to View \ Navigators \ Show Project Navigator. Then right-click “Sources” and select “New File” from the menu. Rename the DiceKit.swift file . Replace its contents with the following code block:
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 structure initializer ensures that both dice have valid values between 1 and 6. Return to the Playground and add this code at the end:
// 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 is what you did with this code:
- You have created a valid pair of dice.
- You added Dice through another initializer that has direct access to its properties.
- You have identified an invalid pair of dice with a new structure initializer.
In Swift 4.1, cross-target initializers must cause a default value. Change the Dice extension to:
extension Dice {
init(_ firstValue: Int, _ secondValue: Int) {
self.init(abs(firstValue - secondValue))
}
}
This change causes structures to behave like classes: cross-module initializers must be convenience initializers in Swift 4.1 [ SE-0189 ].
In Swift 4.1, you can no longer cheat the dice game!
Platform Settings and Configuration Updates
Swift 4.1 adds some of the necessary platform and build features for code testing:
Build Imports
In Swift 4, you tested a module if it is available on a specific platform, defining 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 was successful. Swift 4.1 simplifies this process by letting you check the module itself:
#if canImport(UIKit)
print("UIKit is available if this is printed!")
#endif
In Swift 4.1, you use #if canImport (UIKit) to confirm that a specific structure is available for import [ SE-0075 ].
Target environments
When writing code in Swift 4, the most famous way to check code execution on a simulator or physical device was to check the architecture and operating 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
Was your processor architecture based on Intel, and your operating system - iOS, tvOS or watchOS, you tested in the simulator. Otherwise, you tested the device.
This test was very cumbersome, and it also did not fully describe the type of errors. Swift 4.1 makes this test easier; just use targetEnvironment (simulator) [SE-0190] as follows:
#if targetEnvironment(simulator)
print("Testing in the simulator.")
#endif
Miscellaneous Bits and Pieces
There are a few other updates to Swift 4.1 that are worth knowing:
Compacting Sequences
In Swift 4, it was fairly common to use flatMap (_ :) to filter 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 this particular scenario, the assignment of flatMap (_ :) did not describe the actions taken very well.
For these reasons, Swift 4.1 introduces the renaming of flatMap (_:) to compactMap (_ :) to make its meaning more clear and unique [ SE-0187 ]:
let petNames = pets.compactMap {$ 0}
Unsafe Pointers / Unsafe Pointers
Swift 4 used temporary unsafe mutable pointers to create and modify unsafe mutable buffer pointers:
let buffer = UnsafeMutableBufferPointer(start: UnsafeMutablePointer.allocate(capacity: 10),
count: 10)
let mutableBuffer = UnsafeMutableBufferPointer(start: UnsafeMutablePointer(mutating: buffer.baseAddress),
count: buffer.count)
Swift 4.1 allows you to work with unsafe mutable buffer pointers directly, using the same approach as with unsafe mutable buffer pointers [ SE-0184 ]:
New Playground Features
Swift 4 allowed you to customize type descriptions in Playground Xcode:
class Tutorial {}
extension Tutorial: CustomPlaygroundQuickLookable {
var customPlaygroundQuickLook: PlaygroundQuickLook {
return .text("raywenderlich.com tutorial")
}
}
let tutorial = Tutorial()
You have implemented CustomPlaygroundQuickLookable for the Tutorial and return a short description. The description type in customPlaygroundQuickLook was limited to PlaygroundQuickLook cases . such a pun in Swift 4.1 is no longer there:
extension Tutorial: CustomPlaygroundDisplayConvertible {
var playgroundDescription: Any {
return "raywenderlich.com tutorial"
}
}
This time you are implementing CustomPlaygroundDisplayConvertible . The description type is Any , so you can return anything from the playgroundDescription. This simplifies your code and makes it more flexible [ SE-0198 ].
What next?
Swift 4.1 enhances some of the features of Swift 4 in preparation for the more serious changes that will appear in Swift 5 this year. These include ABI stability, improved generics and strings, new patterns of memory ownership and concurrency, and much more.
If you feel like an adventurer, go and look at the standard Swift library or the official Swift CHANGELOG website , where you can read more information about all the changes in this version.
If you are interested in what changes will be in Swift 5, we also recommend that you familiarize yourself with the Swift Evolution offers , where you can see new features, changes and additions.