Custom Fonts on iOS

March 19, 2017

On one of my side projects, I wanted to experiment with custom fonts while still respecting font size adjustments for accessibility. I’m using storyboards for this project, so also want to be able set font styles there.

Here’s a screenshot of the app in progress:

Example screenshot showing a variety of custom fonts

In this post I’ll share the steps to add custom fonts to an app in Xcode.

Get Licensed Fonts

Like other intellectual property we need a license to use a font in an app. Just because we have the files for a font doesn’t mean we have permission to use the font in our app. There are a variety of fonts available under the Open Font License from Google Fonts. Many foundries and aggregators also offer fonts that can be licensed for in-app use.

For this example, I’ll be using fonts from the Museo family by exljbris. (Full disclosure, I currently have licenses to use these fonts locally and on the web but haven’t bought an app license yet since I’m not close to shipping.)

Add Font Files to Xcode Project

Once we have the .ttf or .otf files for our font, the next step is adding them to our Xcode project.

I like to use a separate Fonts folder in my project structure. This makes it easier to update the shipping fonts in the app. Copy the font files into the folder, like so:

Example project directory structure with fonts

Next drag the Fonts folder from Finder into the Xcode sidebar under the Resources group for your project. When you drop the folder, Xcode will prompt you whether to create a group or a folder reference. Choose folder reference and you’ll be able to adjust the included fonts in Finder while making minimal changes to the Xcode project.

Also be sure to check the main target for your app so the Fonts folder and its content will be copied to your app bundle.

Adding a folder as folder reference in Xcode

Edit Copy Resources Build Phase

Next we’ll double check that Xcode updated the Copy Resources build phase correctly.

In your Xcode project, select your main app target and go to the Build Phases section. Tip open the Copy Bundle Resources build phase. You should see that your new Fonts folder is in the list. (If it’s missing, click the “+” button at the bottom of the phase and choose the folder from the list.)

Checking the Copy Bundle Resources build phase

Add Info.plist Keys

There’s just one more configuration step before we can start using our custom fonts. We have to tell iOS about the fonts by editing the app’s Info.plist.

Add the UIAppFonts key with an array of strings as the value. Each string should be the relative path to a custom font file in your app bundle. Since we copied the Fonts folder and its contents, we need to include that in the path.

Here’s what I added to my Info.plist for the eight font files I’m including in my app:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict><key>UIAppFonts</key>
    <array>
        <string>Fonts/MuseoSans-300.otf</string>
        <string>Fonts/MuseoSans-500.otf</string>
        <string>Fonts/MuseoSans-500Italic.otf</string>
        <string>Fonts/MuseoSans-700.otf</string>
        <string>Fonts/MuseoSlab-300.otf</string>
        <string>Fonts/MuseoSlab-300Italic.otf</string>
        <string>Fonts/MuseoSlab-500.otf</string>
        <string>Fonts/MuseoSlab-500Italic.otf</string>
    </array>
</dict>
</plist>

Using the Custom Fonts

To use a custom font in code we instantiate it by name. For example:

let size: CGFloat = 17
let font = UIFont(name: "MuseoSlab-500", size: size)
label.font = font ?? UIFont.systemFont(ofSize: size)

There are two things to watch out for here.

Finding the postscript name of a font

Next Time

Getting our fonts into the app bundle and onto the screen is rewarding, but our app isn’t complete if we don’t support accessibility.

In the next post I’ll share an extension on UIFont that lets us adapt Apple’s UIFontTextStyles to custom fonts so we can respect our users’ font size adjustments.

I’ll also share an AppearanceManager class with extensions on UIViewController and UIView that let us easily propagate font size changes without any additional code in our individual view controllers and views.


Clyde the Glide

March 5, 2017

A couple of weeks ago I made an outrageous impulse purchase that’s proving to be a great decision. It’s good to be lucky.

Some background: I used to bike to work fairly regularly. I have a solid commuter bike and rain gear to manage Seattle’s winters. Given our hills and my metabolism, bike commuting meant a shower at either end of the commute. Between that and the ride, I was spending nearly an hour each way. When I started distance running regularly, I gradually biked less, until I eventually left my bike in the garage most of the time and ultimately started paying for monthly car parking at the office.

Fast forward a year and I heard Horace Dediu talking about electric bikes on the Critical Path podcast. It struck me that an electric bike would speed up my bike commute, let me forgo the extra showers — no more breaking a sweat on Seattle hills — and leave me enough time and energy to satisfy my running habit.

Over the Presidents’ Day weekend, I decided to stop by a local electric bike shop and take a test ride. The staff at Seattle Electric Bike knows how to make a sale. For my test ride they picked out a couple of likely bikes and rode along with me on a little tour of the neighborhood. Once I was comfortable with operating the bike, they took me to the base of one of the steepest hills nearby and had me ride up it. This is a hill that’s a workout to walk up, let alone bike. With electric assist, it was pure joy biking up the hill. I had to laugh out loud at how much fun it was. I knew I was hooked.

I did some due diligence and test rode four different bikes from two different shops. I made sure to ride the Stromer that Horace favors, though it felt awkward to me. Ultimately I decided on the bike that I’d ridden first, the Felt Sport E 85-HP.

Clyde the Glide

Clyde the Glide, as dubbed by Christina

This bike feels almost exactly like the Marin Sausalito hybrid I’ve been riding for twenty years. The stiff aluminum frame goes exactly where I tell it to. It also telegraphs every bump in the road, but I’m used to that and appreciate the sense of control. That said, a suspension seat post is likely to be on my Christmas wish list.

The electric assist on the Felt is a mid-engine Bosch drive. This drive senses the amount of torque the rider is applying and multiplies it. There are five settings ranging from a 40% boost in Eco all the way to 240% in Turbo. There’s no separate throttle; you have to pedal to move. The trade-off is that, unlike throttled e-bikes that are capped at 20 mph, a torque-assist e-bike is road legal with assist up to 28 mph. In practice, I’m able to climb the biggest hills on my commute at 20 mph without straining and can easily sustain 25+ mph on the flats. I get 25-30 miles on a charge, enough for a couple of days of commuting plus errands. The battery pops off the bike with the turn of a key, so I can bring it in the house to charge. A full charge takes about 3 hours and costs pennies with the Northwest’s cheap hydro power.

I figure the bike will pay for itself in 18 months based on savings in parking, gas, and ReachNow rentals (when Lisa needs our car). Of course, that’s dependent on me actually sticking with it. After two weeks, that’s looking like a good bet. I’ve driven our car just twice since getting the bike. Once to go skiing in the mountains and once when we had snow on the ground in the city.

And there’s a bonus. I’m actually enjoying my commute again. Instead of sitting in stop-and-go traffic on 99 for 15–45 minutes each way, I have a consistent 25 minute commute that’s fun. I’ve actually started getting to work earlier because I’m so eager to hop on the bike in the morning. Geeky? Yes, but at my age I’ve learned to accept that.


Complete and Await Reply Script, Version 1.2

January 22, 2017

Via email, Rachel Hopkin shared a cool feature request for my Complete and Await Reply script. Now the script can automatically set a defer date on the follow-up tasks it creates. If you run OmniFocus from the Forecast perspective, this is a great way to clear an item from today’s list after you’ve reminded someone about it.

This feature is off by default, but it’s easy to enable. See the instructions on the download page.

Share and enjoy!


Finder and Terminal are Friends

January 10, 2017

As developers, we tend to spend a lot of time typing in Terminal windows. Even so, I often find it more helpful to browse directories and files in Finder. I have three little hacks that simplify moving between the two modes.

pwdf

The first hack is a shell function that logs the path of the directory shown by the front-most Finder window.

# pwdf: echoes path of front-most window of Finder
pwdf ()
{ 
   currFolderPath=$( /usr/bin/osascript <<"   EOT"
       tell application "Finder"
           try
               set currFolder to (folder of the front window as alias)
           on error
               set currFolder to (path to desktop folder as alias)
           end try
           POSIX path of currFolder
       end tell
   EOT
   )
   echo "$currFolderPath"
}

I picked up the basic gist of this from a Mac OS X Hints post by Cameron Hayes in the long ago. A much belated thank you to Cameron.

I use zsh as my shell, so this function is declared in my ~/.zshrc. (I think the same function declaration would work in .bashrc, but it’s been six years since I used bash, so please correct me if I’m wrong.)

Add this function declaration and restart Terminal. Then open a Finder window, switch to Terminal, and type pwdf. The path to the Finder window’s folder will be logged:

curt@Fozzie-Bear% pwdf
/Users/curt/Documents/SourceDirs/curtclifton.net/

Where this gets really useful is wrapping the command in backticks to embed the result in other commands. For example, we can count the number of lines in all Swift files in the current Finder window:

curt@Fozzie-Bear% wc -l "`pwdf`"*.swift
      48 /Users/curt/Documents/SourceDirs/curtclifton.net/BlogGenApp/BlogGenApp/AppDelegate.swift
     170 /Users/curt/Documents/SourceDirs/curtclifton.net/BlogGenApp/BlogGenApp/BlogGenModel.swift
     488 /Users/curt/Documents/SourceDirs/curtclifton.net/BlogGenApp/BlogGenApp/SwiftyUserDefaults.swift
     177 /Users/curt/Documents/SourceDirs/curtclifton.net/BlogGenApp/BlogGenApp/ViewController.swift
      22 /Users/curt/Documents/SourceDirs/curtclifton.net/BlogGenApp/BlogGenApp/main.swift
     905 total

cdf

One use of pwdf comes up enough that I created an alias.

alias cdf='cd "`pwdf`"'

Add this to your .zshrc, being careful to get all the quotes and ticks leaning the right directions. Restart Terminal and then type cdf at a prompt to change your working directory to that of the front-most Finder window. I find this super handy when I’ve browsed through directories in a Finder window and want to work some Terminal magic on the files therein. Just ⌘-Tab to Terminal, type cdf, and go to town.

open .

Sometimes I want to move in the opposite direction, opening a Finder window on the directory that I’m working within in Terminal. The built-in Mac system command open solves this problem. If you pass a directory as the argument to open, it opens a Finder window. So, to start browsing your current working directory just:

curt@Fozzie-Bear% open .

Sometimes little hacks like these can make a big difference over time. And I get a real kick out of the fact that pwdf combines zsh and AppleScript.

What are your favorite little hacks? What do you like about them?


Variations on a Theme

January 4, 2017

In Weak References and Type Erasure I wrote about using closures, weak capture, and an isLivePredicate to combine type erasure and weak references.

The goal was to store a heterogeneous collection of generic Signal<Value> objects while letting them deallocate when client code was done with them. My final implementation used a SignalHolder struct with a couple of closures inside. One closure extracted a value from the current state of the data model and passed that value to a weakly captured generic signal. The other closure weakly captured the same signal and returned whether the signal was still non-nil. I used that isLivePredicate machinery to discard signal holders when their signals deallocated.

I had a nagging doubt that my solution was too clever. James Savage and Joe Groff replied on Twitter with a couple of other approaches that I thought were worth sharing.

Using NSHashTable

James suggested using NSHashTable to eliminate the need for my isLivePredicate machinery. We still need a signal holder for type erasure, but if we can tie the lifetime of the signal holder to that of the signal, we get removal of expired signal holders for free.

To tie the lifetimes together, we can make Signal hold a strong reference to its associated signal holder:

public class Signal<Value> {
    fileprivate var keepAliveReference: SignalHolderClass?

    // … continued as in previous post …
}

Because we’re going to put signal holders in an NSHashTable, we need to make them class instances instead of structs. They also lose the isLivePredicate machinery, but are otherwise unchanged:

class SignalHolderClass {
    private let caller: (DataStorage) -> Void

    init<Value>(
        signal: Signal<Value>,
        matching matcher: @escaping (DataStorage) -> Value?)
    {
        caller = { [weak signal] storage in
            guard let signal = signal else { return }
            if let value = matcher(storage) {
                signal.update(to: value)
            }
        }
    }

    func update(from storage: DataStorage) {
        caller(storage)
    }
}

Inside our data model, signalHolders becomes a hash table:

private let signalHolders = NSHashTable<SignalHolderClass>(options: [.objectPointerPersonality, .weakMemory])

NSHashTable has set semantics. The first option to NSHashTable’s initializer says that we want to compare objects by pointers. That’s the right semantics here, and thankfully so, because there’s no good way to do equality comparison on the contents of SignalHolderClass instances — closures aren’t Equatable.

The second option to the initializer says that the signal holders should be held weakly. The only strong reference to a signal holder is from the signal that it wraps.

The addSignal(_:, matching:) method is essentially unchanged in James’s approach, except that NSHashTable uses add() instead of append():

func addSignal<Value>(
    _ signal: Signal<Value>,
    matching matcher: @escaping (DataStorage) -> Value?)
{
    let holder = SignalHolderClass(signal: signal, matching: matcher)
    signalHolders.add(holder)
}

By eliminating the isLivePredicate machinery, the didSet handler for storage is straightforward:

var storage: DataStorage {
    didSet {
        for holder in signalHolders.allObjects {
            holder.update(from: storage)
        }
    }
}

The management of object lifetimes in this approach is more Cocoa-like. The pattern of cyclic references — a strong reference from Signal to SignalHolderClass and a weak reference back — is meat-and-potatoes stuff to any Cocoa developer familiar with the delegate pattern. One slight downside is that signalHolders now stores an object type instead of a value-typed Swift collection, but that’s really just an issue for purists. The collection is private to the data model instance and is never shared.

Unfortunately, there’s one deal breaker for my actual project. The Signal class is in a different framework than my data model and SignalHolderClass. So the keepAliveReference we added to Signal above would have to be implemented in an extension using a computed property and associated objects. Alternatively, we could jump through some hoops to move SignalHolderClass into the same framework as Signal without creating a mutual dependency via DataStorage, but I suspect that complexity is greater than the savings of eliminating the isLivePredicate machinery.

Using a Protocol

Joe approached the problem from the opposite direction. Instead of eliminating the isLivePredicate machinery, he suggested using a protocol to simplify the type erasure.

First we make SignalHolder a protocol:

private protocol SignalHolder {
    var isLive: Bool { get }
    func update(from storage: DataStorage)
}

This lets the signal holder storage in our data model just be an array of protocol witnesses:

private var signalHolders: [SignalHolder] = []

Next we make the old signal holder struct generic:

private struct SignalHolderImpl<Value>: SignalHolder {
    private let matcher: (DataStorage) -> Value?
    private weak var signal: Signal<Value>?

    init(
        signal: Signal<Value>,
        matching matcher: @escaping (DataStorage) -> Value?)
    {
        self.matcher = matcher
        self.signal = signal
    }

    var isLive: Bool { return signal != nil }

    func update(from storage: DataStorage) {
        guard let signal = signal else { return }
        if let value = matcher(storage) {
            signal.update(to: value)
        }
    }
}

Instead of storing type-erased closures, SignalHolderImpl directly stores the matching function and a weak reference to the signal. The bodies of the old caller and isLivePredicate closures move into the update(from:) method and isLive computed property respectively.

The addSignal(_:, matching:) method is unchanged from my implementation, as is the didSet handler for storage.

The downside of this approach is that we have to introduce an additional protocol and make the signal holder generic. That’s additional type system complexity. The huge win is that we stop using closures for type erasure. I’m certain that the protocol-oriented approach will be more familiar than nested closures to future developers coming to this code (no matter how much the old Schemer in me likes function composition).

It may also be the case that bouncing through the protocol witnesses to update all the signal holders is less performant than my closure-based approach. That’s not really a concern at all in this code. The number of active signals is limited in my project.

Epilogue

Practically speaking, I think all three approaches to this problem have about the same complexity. Maybe this is as simple as a solution can be here. Based on Swiftiness and the practical concerns regarding separate frameworks, I plan to convert my project to Joe’s SignalHolder protocol approach.

Thanks to James and Joe for their ideas and conversation. It’s good to be reminded to use both the tools that came before and the new hotness when approaching a problem.