liamOS An iOS development blog

Building Swift Packages as a Universal Binary

So following Apple’s announcement during WWDC 2020 that they’ll be transitioning the Mac away from Intel processors to Apple Silicon it’s now time for everybody to get their software ready.

The transition this time can be considered somewhat easier for most people, especially those who are already supporting arm64 on iOS but there is still work to be done to ensure that tooling and pre-compiled distributions support both architectures ready for when Mac using Apple Silicon are made publicly available. If you haven’t already seen it, a lot of this is covered in the Port your Mac app to Apple Silicon WWDC Session Video.

If you’re using Xcode to compile your command line tools then things are pretty simple as long as you are setting the ARCHS build setting to $(ARCHS_STANDARD) (the default). In Xcode 12, this value is described as Standard Architectures (64-bit Intel and ARM) but if you’re using Swift Package Manager to build and distribute your binary or library, there is no such option.

Instead, starting in Swift Package Manager for Swift 5.3 (Xcode 12), the swift-build executable has now introduced the --arch option (apple/swift-package-manager#2787).

Building a Universal Binary

Firstly, make sure that you are using the correct version of Xcode/Swift:

$ xcrun swift build --version
Swift Package Manager - Swift 5.3.0

Note: If this is not Swift 5.3 or greater, use xcode-select -s to switch to the Xcode 12 beta.

Now, when compiling your package, specify both architectures to compile a Universal Binary:

$ xcrun swift build -c release --arch arm64 --arch x86_64

To verify that your built binary contains both architectures, you can use the lipo -info command to inspect a binary and confirm:

$ lipo -info .build/apple/Products/Release/swiftlint
Architectures in the fat file: .build/apple/Products/Release/swiftlint are: x86_64 arm64

And there you have it, building your Swift Package as a Universal Binary is as simple as that!

Firebase Remote Config and Swift

So you’ve seen Firebase Remote Config and decided that it would go great in your Swift project? You’re right, it will but if like me you’ve noticed that the design of the SDK doesn’t play very well with all of your other beautiful looking strictly typed Swift code then this post is for you.

Note: I don’t cover setting up Firebase in this post, just using FIRRemoteConfig in a configured project. Check out one of these guides for help with the setup part.

Lets take this basic example:

let count = FIRRemoteConfig.remoteConfig()["maximum_item_count"].numberValue?.intValue ?? 10

There are a few problems here:

  • Damn it doesn’t look great.
  • numberValue returns an optional.
  • The key/value pattern means that you can’t guarantee that you’ve not made a typo or updated the key name somewhere else.
  • Because of the optional, you either have to force unwrap or have a fallback.
  • Carelessly force unwrapping is never a good thing and defeats the object of Swift’s type safety.
  • Having a fallback defeats the object of the nice default values that you specify upon initialising Remote Config.
  • Littering all your classes with import FirebaseRemoteConfig will probably be a pain to undo once you decide to move away from Firebase.

It would be a lot nicer if we could do something like this instead:

let count = Config.shared.maxItemCount

Config.swift

The interface for my Config class is pretty simple:

import Foundation
import FirebaseRemoteConfig

final class Config {

    /// The shared instance of config to use
    static let shared: Config = Config()

    /// The maximum number of items that are allowed in this mystery app
    let maxItemCount: Int

    /// The initialiser is private as intended use is via the `shared` static property.
    private init() {

        ...
    }
}

The idea is simple: Initialise all the properties on the shared instance after performing the initial fetch and then trigger another fetch after so that we can be ready to load any changes the next time the app launches.

This is intentional to ensure that the values in Config are all fetched from a consistent data source (i.e to avoid accidentally reading one default value before calling activateFetched and then another remote value after the fetch completed).

As a result, the initialiser looks like this:

// 1. Configure for dev mode if we need it, otherwise a 1 hour expiration duration
let remoteConfig = FIRRemoteConfig.remoteConfig()
#if DEBUG
    let expirationDuration: TimeInterval = 0
    remoteConfig.configSettings = FIRRemoteConfigSettings(developerModeEnabled: true)!
#else
    let expirationDuration: TimeInterval = 3600
#endif

// 2. Set our default values and keys
remoteConfig.setDefaults([
    "maximum_item_count": 42 as NSNumber
])

// 3. Activate any fetched values before we read anything back
remoteConfig.activateFetched()

// 4. Now set the properties on config based on what we have currently
self.maxItemCount = remoteConfig["maximum_item_count"].numberValue!.intValue

// 5. Perform the next fetch so that it's ready when we re-launch
remoteConfig.fetch(withExpirationDuration: expirationDuration) { status, _ in
    print("[Config] Fetch completed with status:", status, "(\(status.rawValue))")
}

Here is a breakdown of what we are doing:

  1. If the app is running in debug mode, I enable dev mode and disable the expirationDuration so that the config refreshes each time. This is very handy during development but will get you throttled server side if you release something like that to production.
  2. Set the default keys and values. I’ve opted to do this in code and not by using a plist so that I can have visibility of all the keys and values later on when I fetch them.
  3. Activate any fetched parameters from the previous launch before we attempt to read them.
  4. Read the fetched or default parameters back and set them as instance variables.
  5. Perform a fetch asynchronously to get any changes that we can then activate the next time we launch the app.

There are still a few non-swifty looking bits here because I’m not using any form of constants to define the duplicate usage of maximum_item_count and I’m also force unwrapping the value however it does come with the following upsides:

  • Only requires a single unit test to ensure that any of the force unwrapping isn’t causing a crash.
  • All the non-swifty looking code is isolated in a single file instead of across my entire project.
  • I could easily update the Config class in the future to completely remove the dependancy of Firebase from my project if I wanted to.
  • The rest of my code looks fabulous (kinda).

The complete class can be found here if you wish to grab a copy. Enjoy!


Note

Due to the nature of Swift, the static shared property won’t be initialised until you try to access it. This means that it might be useful doing something like the following in your AppDelegate if you want to ensure that the next fetch is performed as soon as possible:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

    FIRApp.configure()
    _ = Config.shared

    return true
}

I’m sure that could be done in a nicer way but I’ll let you figure that out :)

Using Small Caps in UIKit

After watching the WWDC 2016 Session 803 video on Typography and Fonts I decided that this would be a good place to start with the whole blog posts thing.

It turns out that fonts are actually very complex things but to keep this short and sweet, I’m just going to talk about one thing that caught my eye.

Small Caps

Usually you only ever come across uppercase or lowercase letters however, small caps bring an additional member to this group. They’re essentially a smaller version of uppercase letters that almost1 align with the lowercase letters in a font.

A comparison of each glyph type Good: The text in the centre is rendered using the same font but with the small caps feature enabled.

You might be wondering why you don’t just use a smaller point size instead? Well if you like your interfaces to be pixel perfect (don’t we all?) then you will notice that after you found the appropriate point size, other features of the font such as the weight and spacing are also adjusted meaning that your two fonts will look different next to each other.

Why should you use small caps you ask? Bad: The text in the centre is the same font but 10 points smaller than the other labels.

Small caps allow the font designer to actually add an additional glyph specifically designed to be used in this case. This means that the font is correctly optimised resulting in something that looks a little like the first picture rather than the second. A minor difference but a good one.

This does however mean that in order to use small caps, the font designer must have explicitly supported this feature.

Use Cases

Small caps are designed to be subtle and can come in very handy when you’re trying to perfect your designs. The case study from Apple demonstrates how they are used on the Apple TV to distinguish a title in a table without it drawing the users attention away from the actual content.

An example used within the Apple TV The Director, Cast and Writer headings use small caps to keep the text size and alignment consistent with the rest of the content.

Another use could be to offer a subtle hierarchy of information. For example, if you wanted to display a number but did not want to emphasise the text next to it then small caps could be used to offer a nicer alternative to just using lowercase letters.

Example if a 12 hour clock An example could be showing the time in a 12 hour format.

Implementation

Small caps can be enabled on a font by enabling their relative Font Feature. The documentation for this is a bit patchy but for actual information around the available font features you can visit the fonts section of the developer site.

To actually take advantage of the font features in code you can do this at a fairly high level by modifying a UIFontDescriptor (The same also applies for NSFontDescriptor on macOS).

Lets take a look at a simple implementation:

let systemFont = UIFont.systemFont(ofSize: 24.0, weight: UIFontWeightLight)
let smallCapsDesc = systemFont.fontDescriptor.addingAttributes([
    UIFontDescriptorFeatureSettingsAttribute: [
        [
            UIFontFeatureTypeIdentifierKey: kUpperCaseType,
            UIFontFeatureSelectorIdentifierKey: kUpperCaseSmallCapsSelector
        ]
    ]
])
let font = UIFont(descriptor: smallCapsDesc, size: systemFont.pointSize)

Here is a breakdown of the above code:

  1. Get an existing font descriptor from a font of our choice.
  2. Create a new font descriptor adding additional attributes via the addingAttributes(_:) method.
  3. Specify the additional font features we would like via the UIFontDescriptorFeatureSettingsAttribute attribute key.
  4. Create a new font object with the new font descriptor and the original point size.

The UIFontDescriptorFeatureSettingsAttribute attribute is in a bit of a weird structure however it’s simple once you understand it.

An array of dictionaries representing non-default font feature settings. Each dictionary contains UIFontFeatureTypeIdentifierKey and UIFontFeatureSelectorIdentifierKey.

So we essentially want an array of dictionaries containing both the feature selector and type identifier. These values map back to the values referenced in the TrueType Font Feature documentation I linked to earlier.

You can also find the provided constants in <CoreText/SFNTLayoutTypes.h>. Note that there is no nice link between feature types and their supported selectors so you just have to read the comments within the header.

Tips & Tricks

Different Combinations

There are actually a couple of ways to use small caps and there are some things to note because you might actually want to use them in a different way to get your desired outcome.

Looking back into the SFNTLayoutTypes.h header, you will see that there is both a kUpperCaseType and kLowerCaseType feature. This gives you two different ways in that you can apply small caps and it essentially means you either make all uppercase letters into small caps or make all lowercase letters into small caps.

By mixing the input text and the feature type that you use, you will get a different output. I’ve put together the table to show the differences.

A comparison between the different options

As you can see from the above example, you will need to choose the correct combination of input text along with the feature type that you decide to use. For example, you will probably never want to use a capitalised string in conjunction with kUpperCaseType.

Numbers and Punctuation

Numbers and punctuation will also be treated as uppercase letters when a small caps feature is applied meaning they are also shrunk down. This can be useful in some cases but if you don’t want to apply small caps to these then you need to make sure you use an NSAttributedString where you only apply the small caps font to the parts you wish to modify.

Extension

You might have noticed that code example above to achieve small caps is kind of bloated when you compare it to a one line UIFont initialiser. Below you can find a sample extension I’ve put together to make this a little bit simpler.

public extension UIFont {

    /// Helper method to create a UIFont with updated attributes applied to the UIFontDescriptor
    ///
    /// - parameter attributes: The new attributes to apply to the fontDescriptor
    ///
    /// - returns: A UIFont object with the new attributes appended to the receivers fontDescriptor
    func addingAttributes(_ attributes: [String : Any] = [:]) -> UIFont {

        return UIFont(descriptor: fontDescriptor.addingAttributes(attributes), size: pointSize)
    }


    /// Returns a UIFont object based on the receiver with small caps applied to upper case letters
    var addingUpperCaseSmallCaps: UIFont {

        return addingAttributes([

            UIFontDescriptorFeatureSettingsAttribute: [
                [
                    UIFontFeatureTypeIdentifierKey: kUpperCaseType,
                    UIFontFeatureSelectorIdentifierKey: kUpperCaseSmallCapsSelector
                ]
            ]
        ])
    }

    /// Returns a UIFont object based on the receiver with small caps applied to lower case letters
    var addingLowerCaseSmallCaps: UIFont {

        return addingAttributes([

            UIFontDescriptorFeatureSettingsAttribute: [
                [
                    UIFontFeatureTypeIdentifierKey: kLowerCaseType,
                    UIFontFeatureSelectorIdentifierKey: kLowerCaseSmallCapsSelector
                ]
            ]
        ])
    }
}

If you have any suggestions to improve the extension then please leave a comment on the gist.


  1. Apple say that the small caps glyph should be slightly larger than the lowercase alternative however they are exactly the same in the San Francisco font from what I can see. 

Introduction

The start of something new

I’ve just spent the last four days in Aberystwyth, Wales at iOS Dev UK. Something I’ve done two years in a row now and I’ve loved every minute of it.

Back in 2015 on the 4 hour train journey back to Bristol I told myself that I wanted to start getting involved with the community more. The idea was that I would start a blog to share things I learn as I go about my day to day job as an iOS Developer.

Fast forward a year and I find myself on the exact same journey home with the exact same feeling I had last year. This time I’m forcing myself to do something about it so here I am starting a blog. I’m pretty new to all of this so you’ll probably find that it’s going to be a bit crap to start with but hey, we’ve all got to start somewhere right? Feedback is welcome!

So anyway. I’ve got a day off work today and I’m about to embark on another 3 hour train journey to visit some family over the weekend so now is the perfect time to get an actual development related blog post up right? Wish me luck.

About Me

My name is Liam Nichols, I’m currently a Senior iOS Developer at Rockpool Digital in Bristol. I also twitter as @liamnichols_ on the Internet from time to time.

After finding this book in my local Library back sometime around 2011, I found myself bodging apps together when I wasn’t busy stacking shelves at Asda. By doing this, I was able to work my way into a real job as an iOS Developer across the country in Bristol and with the support of some great people, I’m still here over 3 and a half years later and loving every minute of it.

That should probably do it for now but if for some reason you want to find out more then feel free to tweet me or email me at liam.nichols.ln@gmail.com.