Blog

Latest Post

Advanced Swift Fourth Edition, with Videos!

Advanced swift hero 1500w

Advanced Swift

The fourth edition of Advanced Swift is out!

Fully updated for Swift 5, the book has been rewritten and extended, including a new chapter on Enums, and a video companion series as an optional upgrade. As with the previous edition, the full text is available as a Swift Playground.

We have completely rewritten the chapters on Structs and Classes, Generics, and Protocols. New content and revisions have been added to the chapters on Strings, Collection Protocols, and Error Handling. The chapter order has also been improved: Collection Protocols now comes later, to smooth out the learning curve.

We also welcome Florian Kugler as a co-author, joining Ole Begemann, Ben Cohen, and Chris Eidhof.

With Video

We’re bringing the video bundle pioneered by App Architecture to Advanced Swift.

Where the book excels as a medium for conceptual discussion, the videos approach the subject from a different angle: they show how those techniques apply to practical problems.

In five separate episodes, we demonstrate C Interoperability, String Parsing, Collection Protocols, Functions, and Encoding & Decoding Graphs. Using live-coding, pair programming, and a conversational style, the videos invite you to think through the concepts with us, as we apply them.

For a more in-depth look, see our full table of contents.

Who Is The Book For?

For those new to Advanced Swift, the book ranges from low-level programming, such as string internals and wrapping a C library, to high-level abstractions, such as programming with generics and protocols. It intends to answer many of the “How do I do this?” or “Why does Swift behave like that?” questions that we often see asked.

If you’re an experienced (though not necessarily expert) programmer, such as an existing Apple-platform developer, this book is for you. It’s also for those coming from other languages such as Java or C++ who want to bring their knowledge of Swift to the same level as their “go-to” language. It’s also suitable for new programmers who have already started on Swift, have grown familiar with the basics, and are looking to take things to the next level.

In short, if you’re reading Apple’s The Swift Programming Language and want to dig deeper, this book is for you.

Availability

The book and videos are available today, directly from our site. The Ebook includes PDF, ePub, and mobi versions. If you prefer a physical copy, we have a paperback edition. All versions come with full sample code.

If you already own an Ebook edition of Advanced Swift, this is a free update.

The videos are exclusive to the Ebook + Videos edition, and can be purchased as a separate upgrade if you already own the book.

All our books are available as bundles: see the complete collection.

Sales and checkout are managed by Gumroad. If you need assistance please contact their help team. Sales for the physical copy are fulfilled by Amazon.

 

Best from Berlin

Chris, Ole, and Florian

 

Previous Posts

Swift Tip: Wrapper Functions

For this year’s update to Advanced Swift, we’re adding the option for a video bundle (just like we did for App Architecture). One of the videos in the bundle shows how we wrap Cairo, a library for drawing vector graphics.

In our wrapper, we need to do the following:

let surface = try SVGSurface(size: CGSize(width: 800, height: 600))
let context = try Context(surface)
context.drawSomething()
surface.finish()
print(surface.data)

Before we can access the data of the context, we should call surface.finish(). And after we call surface.finish(), we really shouldn’t be drawing using context anymore. Instead of putting this into the documentation, we can also provide a different API that prevents us from making this mistake:

func svg(size: CGSize, _ draw: (Context) -> ()) throws -> Data {
    let surface = try SVGSurface(size: size)
    let context = try Context(surface)
    draw(context)
    surface.finish()
    return surface.data
}

Now when we want to create an SVG, we call the function with our drawing code:

let data = try svg(CGSize(width: 800, height: 600)) { context in
    context.drawSomething()
}

In the sample above, we can’t access the surface directly, and we can’t forget to call finish() – this is taken care of by the wrapper.

We can use the same technique in many places. For example, in Core Graphics (which happens to be similar to Cairo), there’s an API that saves the graphic state of a CGContext, and another one that restores it. Calls to these APIs should be balanced. Rather than calling them directly, again we can write a wrapper function that balances the calls for us:

extension CGContext {
    func withLocalGraphicsState(_ draw: () -> ()) {
        saveGState()
        draw()
        restoreGState()
    }
}

You can use similar APIs whenever you’re dealing with resources that need balanced calls: opening and closing a file, locking and unlocking a shared resource, or pushing and popping some state. We use this pattern throughout our projects. It’s much nicer to move these possible points of failure out of our domain specific code, and into helper code that we can test and verify once.

The video bundle will be released shortly, alongside the new edition of Advanced Swift. If you already own the Ebook, the edition update will be free. The videos will be optional, as a paid upgrade. Watch our Twitter for an announcement!

Swift Tip: Protocols vs. Values

Last week, we compared enums and protocols in terms of extensibility. This week we’ll explore how regular values can be used instead of a protocol, and how the two approaches differ in terms of extensibility. We learn that a type can conform to a protocol at most once, whereas you can have many “instances” of regular values.

In the tiny networking library we built for Swift Talk, we use a simple struct to describe a resource that can be loaded from the network:

import Foundation

struct Resource<A> {
    var request: URLRequest
    var parse: (Data) throws -> A
}

This struct contains all the information needed to make the request (in the URLRequest value), as well as a function that knows how to turn the data from the network into an A, whatever that type might be.

An alternative approach would be to define a protocol to which types loadable from the network can conform:

protocol Loadable {
    static var request: URLRequest { get }
    init(parsing data: Data) throws
}

This protocol defines the same requirements as the Resource struct above, just in a slightly different form: a static property for the URLRequest, and a throwing initializer to turn data into an instance of conforming types.

Let’s say we want to load a list of countries from a webserver with either one of these approaches. First, we need a Country type:

struct Country: Codable {
    var alpha2Code: String
    var name: String
    var population: Int
}

Since Country conforms to Codable, we can use the JSONDecoder to turn the incoming data into Country values. To avoid writing this boilerplate code for each JSON endpoint, we can define an initializer on the Resource struct for types that are Decodable:

extension Resource where A: Decodable {
    init(get url: URL) {
        self.init(request: URLRequest(url: url)) { data in
            try JSONDecoder().decode(A.self, from: data)
        }
    }
}

For the protocol approach, we can add the same convenience with a default implementation of the init(parsing:) initializer in case the type conforms to Decodable:

extension Loadable where Self: Decodable {
    init(parsing data: Data) throws {
        self = try JSONDecoder().decode(Self.self, from: data)
    }
}

To load the countries using the Resource approach, we first have to create a resource describing the endpoint. Then we use a custom load method on URLSession to load the data (for the full code see this gist).

let countries = Resource<[Country]>(get: URL(string: "https://restcountries.eu/rest/v2/all")!)
URLSession.shared.load(countries) { print($0) }

To do the same with the protocol based approach, we have to conform Array to Loadable if its elements are of type Country:

extension Array: Loadable where Element == Country {
    static let request = URLRequest(url: URL(string: "https://restcountries.eu/rest/v2/all")!)
}

URLSession.shared.load([Country].self) { print($0) }

Unfortunately, here we run into a limitation of protocols: each type can conform to a protocol at most once. For example, we can’t conform Array again for a different type of element. We might try to conform Array when its elements are Loadable:

extension Array: Loadable where Element: Loadable {
    static let request = ...?
}

However, since Loadable types need to specify the URL they can be loaded from, this approach doesn’t work either. We can’t specify a URL for a generic array of Loadable elements.

Being able to conform to protocols only once is also a problem for protocols such as Codable. For example, if Apple ever provides conformance for CLLocationCoordinate2D, it has to pick a single representation. In the API we’ve used above, a location coordinate is represented as an array of numbers, but we have also used APIs where it’s represented as {lat: 39, lon: 22} or {latitude: 39, longitude: 22}. JSONDecoder solves this problem by providing options for common variations, like date formats. However, if the decoder doesn’t have support for a format you need, we have to resort to using a wrapper type, as we’ve discussed in a previous post.

When designing your own APIs, think twice about whether it would make sense to conform a type multiple times. If yes, try using values like the Resource struct rather than protocols.

You can read much more about protocols in our book, Advanced Swift. A newly expanded edition is almost ready for release, and the update will be free for everyone who already owns the Ebook. 👍

Swift Tip: Enums vs. Protocols

This week, we’ll look at how protocols and enums are extensible in different ways. In a previous post, we discussed the expression problem as it relates to classes and structs, and there are several similarities.

In many cases, we can model data as either an enum or a protocol.

Consider the following:

enum Shape {
    case circle(boundingBox: CGRect)
    case rectangle(rect: CGRect)
    case combined(top: Shape, bottom: Shape)
}

extension Shape {
   func draw(in context: CGContext) {
      // ...
   }
}

We can extend the enum in two orthogonal directions: we can add new methods (or computed properties), or we can add new cases. Adding new methods won’t break existing code. Adding a new case, however, will break any switch statement that doesn’t have a default case.

To model this using protocols, we would create a struct for every case, then put the draw method in the protocol:

protocol Drawable {
    func draw(in context: CGContext)
}

struct Rectangle: Drawable {
    let rect: CGRect
    func draw(in context: CGContext) {
        // ...
    }
}

struct Ellipse: Drawable {
    let boundingBox: CGRect
    func draw(in context: CGContext) {
        // ...
    }
}

struct Combined<A, B> {
    let top: A
    let bottom: B
}

extension Combined: Drawable where A: Drawable, B: Drawable {
    func draw(in context: CGContext) {
        bottom.draw(in: context)
        top.draw(in: context)
    }
}

When we consider extensibility, the protocol approach allows us to add new conformances without breaking existing code. However, when we add a new requirement to the protocol, this does break existing code (except in those cases where we can provide a default implementation).

Looking at Swift’s standard library, we can see these extensibility tradeoffs manifested in the types. Result and Optional are defined as enums; both the standard library and our own code can add extensions without having to worry about breaking things. However, we can’t add new cases to the enum. If the standard library implementers were to do so, everyone’s code would break.

Likewise, Collection and Sequence are defined as protocols. Both the standard library and our own code can easily add new “cases” (that is, conform our own types) without breaking existing code. However, we can’t change the protocol ourselves. The standard library implementers have to be very careful about changes. If they were to add another requirement to Collection, for example, that could break every custom collection conformance outside of the standard library.

In most cases, the decision to use an enum or a protocol should be pretty clear. However, when it’s unclear, and especially when you’re writing a framework, consider how you want the type to be extended. Needless to say, there are often other options too!

To learn more about building extensible libraries, watch our public Swift Talk episode, Enums vs. Classes, recorded with Brandon Kase.

The new edition of our book, Advanced Swift, is almost ready for release. It includes a new chapter on enums, and a revised chapter on protocols, all updated for Swift 5 — you can read an excerpt here.

Swift Tip: Reading from Standard Input/Output

The open-source Markdown Playgrounds app we’ve been documenting on Swift Talk uses the Swift REPL to execute Swift code blocks. This means that we’re reading the standard output of the REPL process to show the results:

stdOut = Pipe()
process = Process()
process.launchPath = "/usr/bin/swift"
process.standardOutput = stdOut
process.launch()

token = NotificationCenter.default.addObserver(forName: .NSFileHandleDataAvailable, object: stdOut.fileHandleForReading, queue: nil) { _ in
    let handle = note.object as! FileHandle
    // Read the available data ...
    handle.waitForDataInBackgroundAndNotify()
}

stdOut.fileHandleForReading.waitForDataInBackgroundAndNotify()

We reach the important part of this process when we start reading data from the file handle. The REPL output is UTF-8 encoded, so the data we’re reading consists of UTF-8 code units. Since we’re receiving the data in chunks, we’re unable to assume that the chunks end on valid character boundaries.

For example, if we naively turn the available data into a string, we might run into issues like this:

148 1@2x

To avoid such issues, we have to buffer the incoming data and only transform it into a string when we’re sure that the data doesn’t end on an incomplete character. For our use case, we can determine this by waiting for newline characters.

We can abstract this logic into a simple struct:

struct REPLBuffer {
    private var buffer = Data()

    mutating func append(_ data: Data) -> String? {
        buffer.append(data)
        if let string = String(data: buffer, encoding: .utf8), string.last?.isNewline == true {
            buffer.removeAll()
            return string
        }
        return nil
    }
}

We can append to this buffer each time the file handle has data available. If the data ends on a newline, it returns the string — otherwise it just returns nil:

var buffer = REPLBuffer()
token = NotificationCenter.default.addObserver(forName: .NSFileHandleDataAvailable, object: stdOut.fileHandleForReading, queue: nil, using: { _ in
    let handle = note.object as! FileHandle
    if let string = buffer.append(handle.availableData) {
        print(string)
    }
    // ...
})

This pattern isn’t just useful for reading the standard output of a REPL process, you can use it whenever you’re reading from a file handle where the data might be incomplete.

The Markdown Playgrounds macOS app is open-source, you can check out the code or try it for yourself. We give a five minute overview of the app at the beginning of Swift Talk 145 (a public episode).

To support our work, subscribe to Swift Talk, or give a subscription as a gift.