What’s New in Swift 3?

Check out what’s new in Swift 3 – Swift’s first major release as an open source project. By Ben Morrow.

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

Modernized GCD and Core Graphics

Speaking of old API holdouts, GCD and Core Graphics have both received a much-needed makeover.

swift-trimming-shears-square

Grand Central Dispatch is used for many threading tasks such as long calculations or to communicate with a server. By moving activity to a different thread, you prevent locking up the user interface. The libdispatch library was written in the C programming language and has always used a C style API. The API has now been reimagined in native Swift [SE-0088]:

// old way, Swift 2
let queue = dispatch_queue_create("com.test.myqueue", nil)
dispatch_async(queue) {
    print("Hello World")
}

// new way, Swift 3
let queue = DispatchQueue(label: "com.test.myqueue")
queue.async {
  print("Hello World")
}

Similarly, Core Graphics was written in C and in the past used awkward function calls. Here’s how the new way looks [SE-0044]:

// old way, Swift 2
let context = UIGraphicsGetCurrentContext()
let startAngle: CGFloat = 0.0
let endAngle = CGFloat(2 * M_PI)
let strokeWidth: CGFloat = 1.0
let radius = self.frame.midX - strokeWidth
let center = CGPointMake(self.frame.midX, self.frame.midY)
CGContextSetStrokeColorWithColor(context, UIColor.redColor().CGColor)
CGContextSetLineWidth(context, strokeWidth)
CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0)
CGContextDrawPath(context, kCGPathStroke)

// new way, Swift 3
guard let context = UIGraphicsGetCurrentContext() else {
  return
}
let startAngle: CGFloat = 0.0
let endAngle = 2 * CGFloat.pi
let strokeWidth: CGFloat = 1.0
let center = CGPoint(x: self.frame.midX, y: self.frame.midY)
let radius = self.frame.midX - strokeWidth
context.setStrokeColor(UIColor.red.cgColor)
context.setLineWidth(strokeWidth)
context.setFillColor(UIColor.clear.cgColor)
context.addArc(center: center,
               radius: radius,
               startAngle: startAngle,
               endAngle: endAngle,
               clockwise: false)
context.drawPath(using: .stroke)

Capitalization on Enumeration Cases

In another reversal from the way you’ve been used to coding Swift, lowerCamelCase now replaces UpperCamelCase for enumeration cases. This makes them more consistent with other properties – or values [SE-0006]:

// old way, Swift 2, followed by new way, Swift 3

UIStatusBarStyle.LightContent
UIStatusBarStyle.lightContent

SKLabelVerticalAlignmentMode.Center
SKLabelVerticalAlignmentMode.center

Optional<String>.None
Optional<String>.none

UpperCamelCase is now reserved solely for names of types and protocols. While this may take some getting used to, the Swift team had really good reasoning in their strive for consistency.

Methods that Return or Modify

The standard library is also getting more consistent in method naming. You choose a name based on the side effects or the actions taken. The rule of thumb is that if it includes a suffix like “-ed” or “-ing” then think of the method as a noun. A noun method returns a value. If it doesn’t have the suffix, then it is most likely an imperative verb. These “verb” methods perform the action on referenced memory. This is also known as modifying in place. There are several pairs of methods that follow this noun/verb convention in the standard library. Arrays showcase the trend well. You can use enumerate()/enumerated(), reverse()/reversed(), and sort()/sorted(). Previously sort() was known as sortInPlace(). The verbs modify the original array and the “-ed” nouns return a copy of the array. Here’s a snippet of those methods in action [SE-0006]:

var ages = [21, 10, 2] // variable, not constant, so you can modify it
ages.sort() // modified in place, value now [2, 10, 21]

for (index, age) in ages.enumerated() { // copied into a dictionarry
  print("\(index). \(age)") // 1. 2 \n 2. 10 \n 3. 21
}

Function Types

Function declarations and function calls have always required parentheses around their parameters:

func f(a: Int) { ... }

f(5)

However, when you use a function type as a parameter itself, you might write something like this:

func g(a: Int -> Int) -> Int -> Int  { ... } // old way, Swift 2

You probably notice that it’s fairly difficult to read. Where do the parameters end and the return types begin? With Swift 3 the correct way to define this function is [SE-0066]:

func g(a: (Int) -> Int) -> (Int) -> Int  { ... } // new way, Swift 3

Now the parameter lists are surrounded by parentheses and followed by the return type. Things are clearer, and consequently, the function type is easier to recognize. Here’s a more robust comparison:

// old way, Swift 2
Int -> Float
String -> Int
T -> U
Int -> Float -> String

// new way, Swift 3
(Int) -> Float
(String) -> Int
(T) -> U
(Int) -> (Float) -> String

API Additions

While the biggest update to Swift 3 has been the modernization of the existing APIs, there is much more the Swift community has been hard at work at – including several useful additions to the Swift API as well.

Accessing the Containing Type

When you define a static property or method, you have always called them on the type directly:

          
CustomStruct.staticMethod()          

If you are writing code in the context of a type, you still need to include the name of the type to call a static method on the type. To make this a bit cleaner, you can now call Self to get the containing type. The capital ‘S’ refers to the type of self, whereas the lowercase ‘s’ refers to the instance of self.
me-gustaman
Here’s how it works in action [SE-0068]:

          
struct CustomStruct {          
 static func staticMethod() { ... }          
 func instanceMethod() {          
 Self.staticMethod() // in the body of the type          
 }          
}          
let customStruct = CustomStruct()          
customStruct.Self.staticMethod() // on an instance of the type          

Note:This feature will be added after Swift 3.1, but it is not currently available in Xcode 8.

Inline Sequences

sequence(first:next:) and sequence(state:next:) are global functions that return infinite sequences. You give them an initial value or a mutable state and they will lazily apply a closure [SE-0094]:

for view in sequence(first: someView, next: { $0.superview }) {
    // someView, someView.superview, someView.superview.superview, ...
}

You can constrain the sequence by using the prefix manipulator [SE-0045]:

for x in sequence(first: 0.1, next: { $0 * 2 }).prefix(while: { $0 < 4 }) { 
  // 0.1, 0.2, 0.4, 0.8, 1.6, 3.2
}

Note:This feature is added to Swift 3.1, but it is not currently available in Xcode 8.

Miscellaneous Odds and Ends

  • #keyPath() works like #selector() and helps you vanquish typos in stringly typed APIs
  • You now call pi on the type you intend to use it as: Float.pi, CGFloat.pi. And most of the time the compiler can infer the type: let circumference = 2 * .pi * radius [SE-0067]
  • The NS prefix has been removed on old foundation types, you can now use Calendar, Date instead of NSCalendar and NSDate.