Omit Needless Words
In previous iterations of Apple libraries, methods included a name that indicated their return value. Because of the Swift compiler’s type checking, this is much less necessary. The team took a hard look at how to filter out all the noise so that only the signal remains and thus a lot of word repetition has been removed.
The API has gotten smarter about how Objective-C libraries are transformed into native Swift [
SE-0005]:
// old way, Swift 2, followed by new way, Swift 3
let blue = UIColor.blueColor()
let blue = UIColor.blue()
let min = numbers.minElement()
let min = numbers.min()
attributedString.appendAttributedString(anotherString)
attributedString.append(anotherString)
names.insert("Jane", atIndex: 0)
names.insert("Jane", at: 0)
UIDevice.currentDevice()
UIDevice.current()
|
Modernized GCD and Core Graphics
Speaking of old API holdouts, GCD and Core Graphics have both received a much-needed makeover.
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 ctx = UIGraphicsGetCurrentContext()
let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512)
CGContextSetFillColorWithColor(ctx, UIColor.blueColor().CGColor)
CGContextSetStrokeColorWithColor(ctx, UIColor.whiteColor().CGColor)
CGContextSetLineWidth(ctx, 10)
CGContextAddRect(ctx, rectangle)
CGContextDrawPath(ctx, .FillStroke)
UIGraphicsEndImageContext()
// new way, Swift 3
if let ctx = UIGraphicsGetCurrentContext() {
let rectangle = CGRect(x: 0, y: 0, width: 512, height: 512)
ctx.setFillColor(UIColor.blue().cgColor)
ctx.setStrokeColor(UIColor.white().cgColor)
ctx.setLineWidth(10)
ctx.addRect(rectangle)
ctx.drawPath(using: .fillStroke)
UIGraphicsEndImageContext()
}
|
Capitalization on Enumeration Cases
In another reversal from the way you’ve been used to coding Swift,
lowerCamelCase now replaces
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
UIInterfaceOrientationMask.Landscape
UIInterfaceOrientationMask.landscape
NSTextAlignment.Right
NSTextAlignment.right
SKBlendMode.Multiply
SKBlendMode.multiply
|
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 with verbs and nouns. 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. Here are a few of them [
SE-0006]:
customArray.enumerate()
customArray.enumerated()
customArray.reverse()
customArray.reversed()
customArray.sort() // changed from .sortInPlace()
customArray.sorted()
|
Here’s a snippet of them in action:
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() { // "-ed" noun returns a copy
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.
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
|
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
}
|
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 ofNSCalendar and NSDate.
Improvements to Tooling
Swift is a language, and a large part of writing it involves using a development environment – which for Apple developers is likely Xcode! The changes going on with the tooling will impact how you write Swift every day.
Swift 3 fixes bugs in the compiler and IDE features. It also improves the precision of error and warning messages. And as you might expect, with each release, Swift is getting faster in how it runs and compiles:
- By improving string hashing there was a 3x speedup in dictionaries of strings
- By moving objects from the heap to the stack there was a 24x speedup (in some cases)
- The compiler now caches multiple files at once (when doing whole module optimization)
- Code size optimization has reduced the compiled size of Swift code. Apple’s demo Demobots reduced the compiled size to 77% of the original
Xcode is also learning how to think in native Swift:
- When you right-click on an API method like
sort() and jump to its definition, you used to be taken to a cryptic header file. Now, in Xcode 8, you see that sort() is an extension to Array like you would expect.
- Swift Snapshots are like the nightly releases of Swift Evolution. They offer a chance to work with the new syntax before its fully baked into Xcode. Xcode 8 can load and run Swift Snapshots in playgrounds.
The Swift Package Manager
Open source Swift is actually a family of repositories including the language, the core libraries, and the package manager. Together, this suite makes up what we think of as Swift. The
Swift Package Manager defines a simple directory structure for any Swift code that you share and import into projects.
Similar to package managers you may be used to such as Cocoapods or Carthage, Swift’s package manager will download dependencies, compile them, and link them together to create libraries and executables. Swift 3 is the first release to include the Swift Package Manager. There are 1,000 libraries that already support it and in the coming months, you’ll start to see more formatted for it.
Planned Future Features
It was previously mentioned that Swift 3 aims to let you keep your code from version to version in the future by striving to avoid breaking changes. While that is true, there were some loftier, related goals that weren’t reached for this release, namely generics additions and Application Binary Interface (ABI) stability.
The generics additions will include recursive protocol constraints and the ability to make a constrained extension conform to a new protocol (i.e., an array of Equatable elements is Equatable). Before these features are complete, Swift is unable to declare ABI stability.
ABI stabilization will allow applications and libraries compiled with different versions of Swift to be able to be linked and interact with each other. This is a vital step for third party libraries to ship frameworks without providing the source code since new versions of Swift not only require them to update their code, but to rebuild their frameworks as well.
Additionally, ABI stability would remove the need to ship the Swift standard library along with binaries, as is currently the case with iOS and macOS apps created with Xcode. Right now binaries are bundled with 2 MB extra filesize that ensures that they will run on future operating systems.
So to summarize, you can now keep your source code version to version, but the compiled binary compatibility from version to version is not yet here.
Where to Go From Here?
Swift continues to evolve as the community converges on best practices. While still in its infancy, the language has a lot of momentum and a big future. Swift already runs on Linux, and you will likely see it running on servers in addition to devices in the coming years. Designing a language from scratch certainly has its advantages as opportunities to break ABI stability once its locked down will be rare. This is a unique chance to get the language correct without regrets.
Swift is also expanding its reach. Apple is eating their own dogfood. Teams at Apple use Swift on the Music app, Console, picture-in-picture in Sierra, the Xcode Documentation viewer, and the new Swift Playgrounds app for iPad.
Speaking of which, there’s a big push to get non-programmers learning Swift, both on the iPad and through education initiatives.
The takeaway here is that Swift continues its ascent: the names are better, the code reads clearer, and you have the tools to migrate. If you’re inspired to dig deeper, you can watch the
WWDC session videos.
There will surely be more to come by the time Swift 3 is finalized in late 2016. We’ll be staying on top of all the updates here, so keep an eye out for tutorials, book announcements, and videos as we start using the exciting changes.
What parts of Swift 3 are you most excited about? What would you like us to cover first? Let us know in the comments below!
Sign up here with your email
ConversionConversion EmoticonEmoticon