Blog

Latest Post

Animating Explicit Changes

In SwiftUI, there are multiple ways to animate changes to your views. In Swift Talk Episode 165, we showed how to animate a shape along a path, by building a custom shape that conforms to the Animatable protocol.

Here’s the relevant animation code:

struct ContentView: View {
    @State var position: CGFloat = 0
    // ...
    var body: some View {
        VStack {
            ZStack {
                // ...
                OnPath(shape: rect, pathShape: Eight(), offset: position)
                    .foregroundColor(.black)
                    .animation(Animation.linear(duration: 5).repeatForever(autoreverses: false))
            }
            .onAppear(perform: {
                self.position = 1
            })
            // ...
        }
    }
}

We wanted to animate the position from 0 to 1 whenever the view appears, and repeat this animation forever, with the shape animating along the path indefinitely. When we run the application, the animation looks like this:

However, there’s a problem with the code. When we rotate the device, it animates in a very strange way:

Currently, our OnPath shape gets animated implicitly whenever anything it depends on changes: the position, its container size, and so forth. To fix this problem, we need to make sure that our animation only applies to changes to the position property.

Instead, we should rewrite our code like this:

struct ContentView: View {
    @State var position: CGFloat = 0
    // ...
    var body: some View {
        VStack {
            ZStack {
                // ...
                OnPath(shape: rect, pathShape: Eight(), offset: position)
                    .foregroundColor(.black)

            }
            .onAppear(perform: {
                withAnimation(Animation.linear(duration: 5).repeatForever(autoreverses: false)) {
                    self.position = 1
                }
            })
            // ...
        }
    }
}

Now, we’re only adding the repeating path animation when the position changes, which only happens when the view appears. Rotating the device no longer influences the infinite animation.

If you’d like to learn alongside us as we experiment with SwiftUI, we have a growing Swift Talk Collection, with a new episode added every week. Episodes 156: A First Look at SwiftUI, 158: The Swift Talk App, and 167: Building a Collection View are free to watch.

To watch the entire SwiftUI collection, subscribe here.

Previous Posts

Defunctionalization

In recent years, the reducer pattern has become more popular in Swift. We see it in UIKit apps, and now in SwiftUI apps.

Consider a simple counter in SwiftUI:

struct ContentView: View {
    @State var count = 0
    var body: some View {
        VStack {
            Text("You clicked \(count) times")
            Button(action: {
                self.count += 1
            }, label: { Text("Click Me") })
            Button(action: {
                self.count = 0
            }, label: { Text("Reset") })
        }
    }
}

For a small program like the one above, the state and its mutations are easy to grasp. There are two mutations: add one to the state, and reset the state to zero. As the size of your program grows, and mutations proliferate through your views, reducer-based refactoring becomes more appealing.

In a reducer-based style, you look at all the different mutations in your program, and create an enum with one case per mutation. For example, refactoring the program above, we could write the following enum:

enum Action {
    case add(Int)
    case reset
}

The second part to a reducer is the “reduce” function (the name is a bit confusing, as it doesn’t have much to do with reduce in the Swift Standard Library). We’ll write this as a mutating function apply on the state:

extension Int {
    mutating func apply(_ action: Action) {
        switch action {
        case .add(let number): self += number
        case .reset: self = 0
        }
    }
}

The technique of taking a program and replacing higher-order functions, such as the button’s actions, with enum cases is called defunctionalization, a term coined by John Reynolds, the inventor of the technique, in his 1972 paper Definitional interpreters for higher-order programming languages.

Interestingly, we can use a similar pattern for something very different: representing links in a web application. In our Swift Talk backend, we have a file called Routes.swift which contains a Route enum. We have a function to turn a Route value into a regular link (for example, Route.collection("swiftui") becomes /collections/swiftui), as well as a function that tries to turn a link into a Route value. Instead of an apply function like above, we have a function called interpret which takes a Route, executes any relevant mutations, and renders the resulting HTML.

This pattern—a Route enum combined with the interpret function—is very similar to the reducer above; it is another type of defunctionalized program. In the case of a web server, its utility doesn’t just lie in taming complexity, it makes it possible to describe continuations in a type-safe way (in this case, as an enum case with associated values, rather than a stringly typed link).

Defunctionalizing a program is an almost mechanical task:

  • Create an Action enum (or Route, or whatever is a good name for your domain).
  • Create an apply function that switches over the enum.
  • For each function that you want to defunctionalize, add a case to the enum, and replace the function with a call to apply. If there are any free variables, add associated values to the enum case. Finally, move the original code into the apply function.

While defunctionalization can be done automatically by a compiler, we typically defunctionalize only part of a program, such as the actions in a GUI, or the web server routes.

Defunctionalization isn’t limited to routes and UI actions:

  • In the Swift Talk backend, we have a ScheduledTask enum that represents a task to be executed at a later time. This enum value is stored in the database along with the scheduled date, and the task gets executed when it’s due.
  • Danvy and Nielsen, in their 2001 paper Defunctionalization at Work, show that we can use defunctionalization to transform continuation based programs into stack-based programs. For example, they turn a functional parser into a state-based parser, much as you would write in C.

We use this technique throughout our Swift Talk episodes, often without making it explicit. As an exception, Episode 62 discusses the reducer pattern in application, using it to improve the testability of a typical view controller.

To learn more about our Swift Talk backend, our introduction episode is free to watch. We recently removed most of our dependencies, including the entire Javascript build stack. The backend is open-source, and you can read the source code on GitHub.

SwiftUI: Paths vs. Shapes

In SwiftUI, there are two similar ways to draw shapes:

  • A Path is a struct that contains the outline of a shape. It’s very similar to the CGPath/CGMutablePath classes from Core Graphics.
  • A Shape is a protocol with a single requirement: a method path(in rect: CGRect) -> Path.

A path is a list of lines, curves and other segments that all have absolute positions. A shape doesn’t know its size up front: the framework calls path(in:) once the final size is known. While a path is absolute, a shape can choose to adjust its path to the given rect.

To draw a resizable vector asset in code, we can create a Shape, and then use the given rect to draw an absolute path. Let’s start with the code for an absolutely-sized balloon-like path:

let balloon = Path { p in
    p.move(to: CGPoint(x: 50, y: 0))
    p.addQuadCurve(to: CGPoint(x: 0, y: 50),
                   control: CGPoint(x: 0, y: 0))
    p.addCurve(to: CGPoint(x: 50, y: 150),
               control1: CGPoint(x: 0, y: 100),
               control2: CGPoint(x: 50, y: 100))
    p.addCurve(to: CGPoint(x: 100, y: 50),
               control1: CGPoint(x: 50, y: 100),
               control2: CGPoint(x: 100, y: 100))
    p.addQuadCurve(to: CGPoint(x: 50, y: 0),
                   control: CGPoint(x: 100, y: 0))
}

balloon.boundingRect // (0, 0, 100, 150)

Interestingly, because Path conforms to View, we can directly draw this in a SwiftUI application:

struct ContentView: View {
    var body: some View {
        balloon
    }
}

2019 08 20 01

By default, the path is filled with the current foreground color. Like a CGPath, we can also apply an affine transform to the path, giving us a new Path:

let balloon2x: Path = balloon.applying(CGAffineTransform(scaleX: 2, y: 2))
balloon2x.boundingRect // (0, 0, 200, 300)

2019 08 20 02

By using the Shape protocol, we can have much more control over paths. For example, we could make a Balloon shape that automatically fills the entire rect (for simplicity, we ignore the rect’s origin):

struct Balloon: Shape {
    func path(in rect: CGRect) -> Path {
        let bounds = balloon.boundingRect
        let scaleX = rect.size.width/bounds.size.width
        let scaleY = rect.size.height/bounds.size.height
        return balloon.applying(CGAffineTransform(scaleX: scaleX, y: scaleY))
    }
}

2019 08 20 03

You can easily turn this into a more generic Fit struct that takes the path as a parameter.

The other APIs are very similar between Path and Shape. For example, we can stroke a Path or a Shape, which gives us back either a new Path or a new Shape, respectively. The moment we apply a fill to the path or shape, the resulting type is some View, which means that we “lose” the fact that we were dealing with a path or shape. For example, it’s not possible to apply a fill and then a stroke, as stroke is only available on paths and shapes.

2019 08 20 04

In conclusion: paths and shapes are almost the same. When you need an absolute drawing, you can use either one, but when you want to adjust the path to the available size, use a Shape.

Swift Talk Episode 164 explores SwiftUI paths and shapes in more depth, as we build an animated loading indicator for our Swift Talk app. The first episode is free to watch.

To watch the entire SwiftUI collection, subscribe here.

Conforming Tuples to Protocols

Tuples in Swift are a great way to quickly combine two or more pieces of data. For example, when working on an SVG parsing library, we had two doubles that we wanted to combine. In some cases it’s very helpful to create a nominal type, such as a Point:

struct Point {
    var x: Double
    var y: Double
}

In other cases, we can’t always come up with a good name. Perhaps we’re working on something more abstract, or the two values could be interpreted in different ways: in one case they might be points, in another they’re interpreted as sizes. Instead of a nominal type, we’d use a structural type such as a tuple:

struct MyModel {
    var pairs: [(Double, Double)]
}

The major disadvantage of using a tuple to model data — as we do above with (Double, Double) — is that a tuple can’t conform to a protocol. This means we can’t just add Equatable, Hashable, or Codable conformance using the implementation we’d otherwise get for free. If we had written our pairs property as [Point] instead, this would be much easier.

However, all is not lost. We can write a type Pair<A, B> that works in the same way as a tuple, but also adds conditional conformance. Here’s one possible implementation:

struct Pair<A, B> {
    var one: A
    var two: B
    init(_ one: A, _ two: B) {
        self.one = one
        self.two = two
    }
}

Now we can conditionally conform Pair to all kinds of protocols:

extension Pair: Encodable where A: Encodable, B: Encodable { }
extension Pair: Decodable where A: Decodable, B: Decodable { }
extension Pair: Equatable where A: Equatable, B: Equatable { }
extension Pair: Hashable where A: Hashable, B: Hashable { }

We can even conform Pair to SwiftUI’s VectorArithmetic protocol, so that it can be used with animatableData.

Of course, a Pair doesn’t have all the same syntactic sugar as a tuple, but it can be a quick alternative in case we want a structural type that can conform to Codable (or any other protocol):

struct MyModel {
    var pairs: [Pair<Double, Double>]
}

Until Swift adds conditional conformance to tuples, this is a workaround that has served us well in a few projects.

Many of our projects feature in Swift Talk, our weekly pair programming video series. The most recent collection explores SwiftUI, as we build a client app for our Swift Talk backend.

To learn with us, subscribe here.

Removing Dependencies

Over the last few weeks, we’ve been cleaning up the dependencies in our Swift Talk backend. Now that we’re done, we finally feel like we’re on top of the code base again. No more dark, dusty corners!

In this post, we’ll outline how the code base has developed over time, and the changes we’ve made to clean it up.

We started developing the Swift Talk backend almost three and a half years ago. To begin with, we hired a contractor to build the site using Ruby on Rails. After we launched, we continued to make small changes ourselves, and hire other contractors for larger things.

When we decided to rewrite the backend in Swift last fall, our goal was to get the new backend up and running as quickly as possible. For the most part, we replicated what the Rails backend was doing before. To focus on building the most interesting parts of the site, we included third party libraries for dealing with PostgreSQL, XML, and crypto functions. We also kept all the CSS and Javascript infrastructure around, serving the same .css and .js files as before, just from our Swift server.

A few things bothered us about the state of this code base:

  1. Some of the Swift dependencies felt unnecessary, given the little code it would take us to directly interface with the underlying libraries.

  2. We didn’t understand the Javascript build stack (lots of node modules, browserify, and babel), and the deprecation warnings in the JS build process were starting to accumulate.

  3. We ended up shipping huge CSS (2 MB uncompressed) and Javascript (7.3 MB uncompressed) files, although they were doing very little to enhance the functionality of the site. For example, the subscription form was built using React, which adds a massive amount of code for a small subsection of the site.

With regard to the Swift dependencies, we ended up writing our own lightweight wrapper around libpq, similar to the wrapper we demonstrated on Swift Talk. Our wrapper is approximately 300 lines of code, almost half of which are straightforward mechanical conversions between Swift types and their representations in PostgreSQL. We’re happy to maintain this small piece of code against a mature library like libpq ourselves. The benefit is that we can easily fix warnings that arise with new Swift versions, fix bugs as we encounter them, or make changes to the API so that it better suits our needs.

We also removed our dependency on BlueCryptor, a Swift wrapper around CommonCrypto on macOS and libcrypto on Linux. Since we were only using it to create md5 hashes for our assets, we wrote our own md5 wrapper around the native crypto libraries, with approximately 30 lines of code.

Before, we used Perfect-XML to parse XML on Linux, as Foundation’s XML parsing gave us strange crashes. In recent releases, these issues have been fixed, and we were able to drop the dependency and use Foundation instead.

On the client side, we slowly dropped more and more Javascript dependencies, until we were able to remove the entire Javascript build stack. There’s no single application.js anymore, instead, we have a few inline scripts for pages that need it. For example, we ended up re-writing the payment form without React, which took about 150 lines of vanilla Javascript code. We maintained the spirit of the React-based code: our custom Javascript also keeps track of a state object and re-renders the potentially affected DOM nodes when the state changes.

We took a long hard look at the CSS, and also managed to remove almost all dependencies.

Taking stock after the refactoring, we cut down the CSS and Javascript sizes to a fraction of what they were before: the uncompressed CSS is 199KB, and there are only a few bits of inline Javascript. This makes for a faster site and — at least in our case — much happier developers 😀.

If you’d like to check out the backend, the source code is on GitHub.