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ă.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

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:

  1. You created a valid pair of dice.
  2. You extended Dice with another initializer that has direct access to its properties.
  3. 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].

In Swift 4.1 you can no longer cheat in dice games!

In Swift 4.1 you can no longer cheat in dice games!

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!