Daniel steinberg whats new in swift 4 header

What's New in Swift 4

Introduction

I’m Daniel Steinberg from Dim Sum Thinking with a quick look at what’s new in Swift 4.

It’s hard to believe that Swift’s only been available for a few years. It’s best to see Swift on a continuum. Swift 2 was Apple’s initial pass at what they thought needed to be added to and changed in the language since its public launch a year before. Swift 3 was the community edition. After open sourcing the language, a lot of changes came from proposals that were submitted by and commented on by the community.

Swift 4 is the release that says, “We need to stabilize the source code.” It also builds on the experience we have using the language for a couple of years on real projects.

Now that we have a better understanding of what Swift wants to be, the proposals adopted in Swift 4 are helping it get there. Swift 5 will be the API Stability release. But there are also long term goals to improve the ownership model, the generics mechanism, and to address concurrency.

So on to what’s new in Swift 4. We’ll talk about the package manager, collections, strings, JSON, access and ownership, generics and types, and quickly look at some of the proposals that were approved, but didn’t make it in. Many of the code examples are from the proposals themselves. The idea is that if there’s something you need to dig into, you can go to the Swift proposal itself and read more.

Package Manager

Let’s start with a very quick look at what’s new in the package manager. Four of the package manager proposals have been part of Swift since 3.1:

  • 082 The introduction of Editable Packages
  • 145 Version Pinning: The addition of package manager features to pin package dependencies.
  • 151 Swift Language Compatibility Version: A new declaration to specify a set of supported Swift language versions
  • 152 Tools Version: Another declaration to specify the Swift Tools version.

In Swift 4 the package manager manifest API has been redesigned (158). You’ll also be able to specify products to be built as either executables or libraries (146). While you’re at it, you’re gonna have to explicitly declare your targets, using some new conventions (162).

Two proposals address developing related packages which might depend on each other, or a new package which might not yet have a version (149 & 150). With these two proposals, you can get right to work without worrying about strict versions.

It’s important to know what the platform you’re building for supports. Instead of testing whether the target is Linux or Mac or iOS proposal 75 has this check for support of desired modules such as UIkit or Cocoa.

Get more development news like this

Remember the pin feature introduced in 3.1? Proposal 175 removes it, and replaces it with a revised dependency resolution mechanism.

Rounding out the package manager additions are a run command to build and run an executable (179), and C/C++ language support (181). That’s the package manager.

Collections

172 One Sided Ranges

One of my favorite little additions to Swift is One Sided Ranges. For example, let’s create a howBig function:

func howBig(is number: Int) -> String {
    switch number {
    case 0...4:
        return "Less than 5"
    case 5...9:
        return "\(number)"
    case 10...100:
        return "Greater than 10"
    default:
        return "Greater than 10"
    }
}

It takes an Int and returns a String. Let’s switch on the Int. If it’s between 0 and 4, we’ll return “Less than 5”. If it’s between 5 and 9 we’ll return the number. If it’s greater than 10 we’ll return “Greater than 10”. So how big is 3 returns “Less than 5”.

We can use one sided ranges to write this without the lower limit: case ...4:. In this case we’re now including negative numbers as well. So how big is -2 returns “Less than 5”. We can also use a less than but not equals as a one sided range: case ..<5:.

Similarly, we can take care of any number greater than 10 using a one sided limit on the other side: case 10...:. The default case is then the numbers from 5 to 9 as those are all that remain, so we can clean up our example a bit. Here’s the final example:

func howBig(is number: Int) -> String {
    switch number {
    case ..<5:
        return "Less than 5"

    case 10...:
        return "Greater than 10"
    default:
        return "\(number)"
    }
}

We can also use one sided ranges in arrays. Here’s an array of strings: let letters = ["a", "b", "c", "d", "e"]. We use a one sided range with an unspecified lower limit so the index starts at 0: letters[...2] returns ["a", "b", "c"].

And here we use an unspecified upper limit, so the index ends at the array count minus one: letters[2...] returns ["c", "d", "e"].

165 Dictionary & Set Enhancements

There are many enhancements to dictionaries and sets. Here are a few dictionary improvements. In this example we start with the string “how now brown cow.”

let source = "how now brown cow"
var frequencies: [Character: Int] = [:]
for c in source.characters {
    if frequencies[c] == nil {
        frequencies[c] = 1
    } else {
        frequencies[c]! += 1
    }
}

We’re going to check for the frequency of the characters. The first time we encounter a character no frequency has been recorded for it, its frequency is nil. So if it’s the first time we’ll initialize the frequency for this value to be 1. Otherwise, if it’s not the first time then we’ll increment the frequency count.

In Swift 4 we can combine these two cases by providing the default value of zero and incrementing the frequency count whether it’s the first time we’re seeing this character or not:

let source = "how now brown cow"
var frequencies: [Character: Int] = [:]
for c in source.characters {
    frequencies[c, default: 0] += 1
}

So if there’s no value, we use the default of 0, and if there is a value we use it.

Let’s create a simple dictionary to use to see other new features:

let numbers = ["one": 1, "two": 2, "three": 3, "four": 4]

Up until now, whenever we filter a sequence the result is an array. In Swift 4, if we filter a dictionary, we get back a dictionary, which is certainly what you prefer:

let evens = numbers.filter { $0.value % 2 == 0 }

["four": 4, "two": 2]

If we map the dictionary we get an array, but often we’d like to apply a function to the values of the dictionary and preserve the key value pairs. Map still returns an array, but dictionary has a new method named mapValues that does just that – it applies the closure to the values while keeping the values together with their keys. Here, I’m increasing each value by one:

let offByOne = numbers.mapValues {$0 + 1 }

["three": 4, "four": 5, "one": 2, "two": 3]

Finally, we can create a dictionary containing items that are grouped somehow. In this case, we grouped the keys from the numbers dictionary by their first letter:

let grouped = Dictionary(grouping: numbers.keys, 
                         by: { $0.characters.first! })

["t": ["three", "two"], "f": ["four"], "o": ["one"]]

154 Provide Custom Collections for Dictionary Keys and Values

The type for a dictionary’s keys and values have changed with every release of Swift. As of Swift 4 there are new collection types named Keys and Values that represent the keys and values of a dictionary.

These three proposals add new methods to the standard library to make collections easier to work with:

  • 045 Add prefix(while:) and drop(while:) to the sdlib
  • 173 Add MutableCollection.swapAt(_:_:)
  • 171 Reduce with inout

Strings

One of my favorite things about strings in Swift 4 is that they’re collections of characters. I know, they used to be collections then they weren’t. Well, now they are again.

163 String Revision

Formally, strings are both BidirectionalCollections and RangeReplaceableCollections. This means that if we look at a previous example we don’t have to ask the source string for its characters, we can just fast enumerate source directly and c is inferred to be a character:

let source = "how now brown cow"
var frequencies: [Character: Int] = [:]
for c in source {
    frequencies[c, default: 0] += 1
}

There were related proposals to make all of this work including the String Index Overhaul (180), and the addition of a Unicode scalars property to character (178).

Because a string is a collection, we can also apply one sided ranges to a string. Here we find the index of a character and use a one sided range to pull out the characters prior to but not including that character. This makes parsing strings much nicer:

let source = "how now brown cow"

if let bIndex = source.index(of: "b") {
    source[..<bIndex]
}

"how now "

An additional part of this proposal is that the resulting substring is not a string, it’s a new type called Substring. Both String and Substring conform to a new string protocol. The whole point of substrings is to improve performance when operating on strings, and a separate proposal adds some affordances for substring performance (183).

168 Multiline String Literals

With Swift 4 we have two convenience additions for dealing with strings allowing us to use strings that might span multiple lines. Here I enclose a poem in a triplet of quotation marks:

let multiline = """
I wrote a poem
that didn't rhyme
Just to exhibit
multiline
"""

Now note that the enclosed string begins on the line following the three open quotation marks. I use a poem here, but you’ll more likely see something like an XML, HTML, JSON or other code listing. The result is the string with newline characters inserted: "I wrote a poem\nthat didn't rhyme\nJust to exhibit\nmultiline".

182 String Newline Escaping

There are other times when we need to start a new line for readability in our code but we don’t want the string to be split at that point. This proposal allows for a newline escaping symbol:

let multiline = """
I wrote a poem \
that didn't rhyme \
Just to exhibit \

multiline
"""

"I wrote a poem that didn't rhyme Just to exhibit multiline"

You can see that the spaces before the symbol are respected. What you can’t really see is that also whitespace characters between the symbol and the newline are ignored.

Multiline strings make it easy to display JSON, but of course display is not the real problem with JSON in Swift.

JSON

In Swift 4, many of the nested ifs, failable inits, typecasting and other clever methods for converting class, struct, and enumeration instances to and from JSON, disappear.

166 Swift Archival and Serialization

The Swift Archival and Serialization proposal is part of the trio of proposals that make it easier to serialize to plist and JSON.

Participating types need to confirm to the Codable protocol. Codable itself is made up of the Encodable and Decodable protocols. Even better, when you declare a type that has properties which are themselves Codable to be Codable, it’s automatically Codable. Otherwise it’s up to you to conform to these protocols yourself.

Access and Ownership

There are a handful of proposals that have to do with access and ownership. The first just makes me laugh.

169 Improve Interaction Between private Declarations and Extensions

The meaning of private has changed in every release of Swift, and this one is no exception. In Swift 2, private meant visible to anything inside the same file.

In Swift 3, the word fileprivate was introduced to describe this meaning and the keyword private was changed to mean that access was restricted to elements in the same context within the same file.

This meant if you had a property in a class that you wanted to access in an extension in the same file, you had to declare that that property was fileprivate. It seems that this was a problem for some, so now private has changed so that types and their extensions in the same file can access private members.

class Sample {
    private let myProperty = "Hello!"
}

extension Sample : CustomStringConvertible {
    var description: String {
        return myProperty
    }
}

160 Limiting @objc Inference

It might be a stretch to include Limiting @objc Inference in the category of Access and Ownership, but it does cover APIs that need to be accessed from Objective-C. Most instances automatically inferring @objc are removed, as they add unnecessary overhead.

Cases that remain include the case where we override a method that was declared with @objc, the case where a method conforms to an @objc protocol, in the case of an @IBOutlet or @IBAction, or if it’s managed by core data, denoted with NSManaged. Similarly, additional cases for which @objc will be inferred are @IBInspectableProperties and GKInspectableAttributes.

141 Availability by Swift Version

In the past we declared that a member was available for a specific iOS version, or some range of iOS versions. As of Swift 4 we can declare availability by Swift version:

@available(swift, obsoleted: 3.1)
class Foo {
    //...
}

164 Remove final support in Protocol Extensions

This is more of a cleanup proposal. Methods and protocol extensions can’t be overridden, so the final keyword has no meaning. In this proposal, its use is prohibited.

Enforce Exclusive Access to Memory

This next proposal is huge. You should take time to read the Ownership Manifesto and to see the portion of the What’s New in Swift session from WWDC that goes over ownership. The manifesto and the session go over many problems in overlapping access of variables.

The high level takeaway for where the Swift ownership model is heading is that you don’t want someone to be writing to a variable while you’re in the process of reading it, and you never want two people to be writing to the same variable.

In other words, the goal is that two accesses of the same variable are not allowed to overlap unless both accesses are reads.

Generics and Types

104 Protocol-Oriented Integers

The Protocol Oriented Integers proposal is designed to clean up Swift’s integer APIs with a new set of protocols that makes them more suited for generic programming. This isn’t going to help you do arithmetic with two different types of Ints, but it does lay the groundwork for that. It also lays the groundwork for but does not implement a BigInt type.

170 NSNumber bridging and Numeric Types

Another collection of number related fixes is addressed in this proposal for NSNumber bridging and Numeric Types. The main goal here is to fix cases in which you might get unexpected behavior when working with numbers.

156 Class and Subtype Existentials

In Objective-C we have the ability to declare that the type of a method parameter is an instance or subclass of one class, at the same time that it conforms to one or more protocols using one of these declarations:

id<Protocol1, Protocol2>

Base<Protocol1, Protocol2>*

We’ve not been able to do this in Swift until now. As of Swift 4 we can specify this conforms like this:

AnyObject & Protocol1 & Protocol2

Base & Protocol1 & Protocol2

So for example we can say that something is a UITextView that also conforms to this protocol and that protocol. The proposal also details the protections the implementation provides that keeps us from running into clashes in requirements.

148 Generic Subscripts

The Generic Subscripts proposal makes it possible for a subscript to provide a generic return type and/or for the subscripts themselves to be generics. This is a small piece of the larger generic manifesto, which will take many more Swift releases to be completed.

142 Permit where Clauses to Constrain Associated Types

Finally, associated types can now have where clauses to more flexibly constrain the associated types.

What about…?

There were 10 proposals that were accepted but not implemented in time to ship with Swift 4.

Given the attention given to Smart KeyPaths at WWDC it was a bit surprising that Smart Keypaths: Better Key-Value Coding for Swift (161) didn’t make it into the Swift 4 release. Two of the generic proposals, Conditional Constraints (143), and Support Recursive Constraints on Associated Types (157) also weren’t finished in time. The second leg of the Codable trio, Swift Encoders (167), was not implemented, and this collection also remains undone:

  • 185 Synthesizing Equatable and Hashable Conformance
  • 174 Change Filter to return and associated type
  • 153 Compensate for inconsistency of @NSCopying’s behavior
  • 042 Flattening the function type of unapplied method references
  • 155 Normalize Enum Case Representation
  • 068 Expanding Swift Self to class members and value types

Swift 5

I suppose that gives us concrete features to look forward to in Swift 5 or beyond, but remember, Swift 5 is committed to ABI stability. This was a goal in Swift 4 and even in Swift 3. Now it’s stated more firmly as a commitment, and the single most important requirement of the Swift 5 release. Expect more work in generics, ownership, and possibly concurrency.

Swift 4 is a powerful and stable language from a developer’s standpoint. I hope you’ve enjoyed this quick look at what’s new in Swift 4.

Next Up: New Features in Realm Obj-C & Swift

General link arrow white

About the content

This content has been published here with the express permission of the author.

Daniel Steinberg

Daniel is the author of the books ‘A Swift Kickstart’ and ‘Developing iOS 7 Apps for iPad and iPhone’, the official companion book to the popular iTunes U series from Stanford University. Daniel presents iPhone, Cocoa, and Swift training and consults through his company Dim Sum Thinking. He is also the host of the CocoaConf Podcast.

4 design patterns for a RESTless mobile integration »

close