Building Glyph: A Year With SwiftUI

July 02, 2020

One of the first frontend frameworks I used was Ember when it was first released in 2012. Its goal at the time was to be the “Rails for the frontend”, but the MVC pattern never seemed to fit quite right with frontend development. Over the last decade it's been exciting to watch new frontend frameworks like Angular, Vue, and React challenge that approach and eventually converge on the current functional, declarative, views-as-a-function-of-state approach that has become the dominant trend for UI programming on the web.

When Apple announced SwiftUI and Combine last year, it seemed like they finally had an answer to the recent trends in UI programming. I was eager to dive in and conveniently was starting a new iOS project to build a hand-drawn animation app for the iPad, so over the last year I’ve built a fairly complex app entirely in SwiftUI and thought I would share my experience.

Pros

Declarative Syntax

The declarative API is fantastic, it makes sense to write UI code in terms of what we should see as opposed to what should happen (imperatively). It’s easy to see the benefit here by comparing a table in UIKit versus SwiftUI:

struct TableView: View {
  @State private var tasks: [String] = ["Check email", "Buy groceries"]

  var body: some View {
    VStack {
      Button(action: { self.tasks.append(“New Task”) }) { Text(“Add New Task”) }
      List(tasks, id: \.self) { task in
        Text(task)
      }
    }
  }
}

The equivalent in UIKit is so much more code and boilerplate that it would require a post by itself, but here’s an example from StackOverflow for reference.

Layout System

UIKit has a powerful API for precise, responsive layouts but it’s complicated to do programmatically. On the web, grids have become a popular pattern for handling layout logic whether you’re using a framework or the native CSS grid property. SwiftUI takes a different stack-based approach to layouts, and after getting over the inital unfamiliarity I like it so much that I’m considering adopting it for web layouts too and building some kind of stack-based CSS framework. In Glyph, the workspace layout looks like this:

image

And the layout code looks like:

ZStack {
  // SwiftUI automatically centers contents
  CanvasView()

  VStack(spacing: 0) {
    TopBarView()
    TimelineView()
    // This spacer pushes the timeline and bar to the top
    Spacer()
  }

  HStack {
    DockView()
    // This spacer pushes the dock to the left
    Spacer()
  }
}

The combination of stacks and spacers makes it fairly intuitive to build out responsive layout logic. Compared to grids, it's much easier to see the layout behind the code.

Universal UI Code

For frontend designers and developers trying to deal with building UI that runs across dozens of devices, the goal of “write once, run anywhere” has been the panacea of UI development. If anyone had the motivation, talent, and platform control to achieve this goal it would be Apple. And I believe they have been largely successful with SwiftUI.

I spent the last year building and testing my app on the iPad, so I decided to take Mac Catalyst for a test drive and see how well my code translated to a Mac app. Apple’s claim is that all you need to do is check a box in your build settings and your app should run on a Mac, and as it turns out…

image

It Just Worked! With a few caveats:

  • There’s no pencil support for a Mac app, so I had to change the gesture settings
  • Some aspects of the UI don’t make sense in a Mac app. Why is the timeline on the top? This makes sense on the iPad where you don’t want your wrist to tap buttons while you’re drawing, but it should probably be moved down for the Mac app.
  • Some Metal APIs are not available on macOS

As great as SwiftUI does at universal UI code, it demonstrates that there is still a large amount of code that needs to be specific to the device because of the spectrum of different sizes, inputs, accessories, and hardware performance of different devices.


There’s a lot more to like about SwiftUI, some honorable mentions would be:


  • The new Combine framework that gives a lot of powerful tools for asynchronous code and state management.
  • XCode has a new “canvas preview” feature that allows you to view and interact with your SwiftUI views alongside your code. This is terrific when it works, but unfortunately most of my views crash or take too long in the canvas preview so in practice it was easier to just view them in the simulator.

I did want to cover some of the downsides of SwiftUI, but this post ended up being longer then expected so stay tuned for a followup.