Skip to content

Blog Posts

Below are the blog posts I have published since 2015. You can also subscribe to new posts with your favourite news reader.

Mapping Optional Binding to Bool


When displaying an alert in SwiftUI, if the value used to calculate whether the alert is presented is both Optional and does not conform to Identifiable1 it is often recommended to use a separate flag, similar to:

struct ContentView: View {
    @State private var alertText: String?
    @State private var isPresentingAlert = false

    var body: some View {
        Button("Show Alert") {
            self.alertText = "Alert Text"
            self.isPresentingAlert = true
        }
        .alert(isPresented: $isPresentingAlert) {
            Alert(title: Text(alertText!))
        }
    }
}

There are 2 main downsides to this:

  1. alertText is not set back to nil, which may cause bugs and will increase memory usage (even if only a little in this case)
  2. The isPresentingAlert flag needs to be managed

To work around these issues I create a small extension to Binding the allows this same code to be updated to:

struct ContentView: View {
    @State private var alertText: String?

    var body: some View {
        Button("Show Alert") {
            self.alertText = "Alert Text"
        }
        .alert(isPresented: $alertText.mappedToBool()) {
            Alert(title: Text(alertText!))
        }
    }
}

The extension is fairly small and simple:

import os.log
import SwiftUI

extension Binding where Value == Bool {
    /// Creates a binding by mapping an optional value to a `Bool` that is
    /// `true` when the value is non-`nil` and `false` when the value is `nil`.
    ///
    /// When the value of the produced binding is set to `false` the value
    /// of `bindingToOptional`'s `wrappedValue` is set to `nil`.
    ///
    /// Setting the value of the produce binding to `true` does nothing and
    /// will log an error.
    ///
    /// - parameter bindingToOptional: A `Binding` to an optional value, used to calculate the `wrappedValue`.
    public init<Wrapped>(mappedTo bindingToOptional: Binding<Wrapped?>) {
        self.init(
            get: { bindingToOptional.wrappedValue != nil },
            set: { newValue in
                if !newValue {
                    bindingToOptional.wrappedValue = nil
                } else {
                    os_log(
                        .error,
                        "Optional binding mapped to optional has been set to `true`, which will have no effect. Current value: %@",
                        String(describing: bindingToOptional.wrappedValue)
                    )
                }
            }
        )
    }
}

extension Binding {
    /// Returns a binding by mapping this binding's value to a `Bool` that is
    /// `true` when the value is non-`nil` and `false` when the value is `nil`.
    ///
    /// When the value of the produced binding is set to `false` this binding's value
    /// is set to `nil`.
    public func mappedToBool<Wrapped>() -> Binding<Bool> where Value == Wrapped? {
        return Binding<Bool>(mappedTo: self)
    }
}

The extension isn't tied directly to showing an alert or a sheet and can be used in any context, but this is one of the better examples of its usage.

This extension is available on GitHub under the MIT license.

1 If it does conform to Identifiable use alert(item:content:)

Keep Reading

Supporting Multiple Swift Package Versions Without Breaking Compatibility


The Xcode 12 beta includes Swift 5.3 but drops support for iOS 8.x. This means that Swift packages that support iOS 8 will cause a warning:

The iOS deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.0.99.

It's not possible to remove this warning within a project that depends on a Swift package with a deployment target of iOS 8, but it is possible to fix this in the dependency without removing support for iOS 8 for older versions of Swift. There are multiple way this can be accomplished.

Keep Reading

Running UI Tests on Mac Catalyst


While working on the 2.0 update for Gathered I have been trying to develop the app multiple platforms simultaneously. SwiftUI will solve this problem in the future, but I wish to support some OS versions that SwiftUI does not support.

As part of this I have been creating UI tests to test performance, but ran in to an issue when running the UI tests on macOS using Mac Catalyst:

Running tests...
The bundle “PerformanceXCTests” couldn’t be loaded because it is damaged or missing necessary resources. Try reinstalling the bundle.
(dlopen_preflight(...): no suitable image found.  Did find:
	...: code signature in (...) not valid for use in process using Library Validation: mapped file has no Team ID and is not a platform binary (signed with custom identity or adhoc?))
Keep Reading

My Swift Package Manager Release Workflow


I am currently maintaining numerous Swift Packages that don't receive a constant flow of updates, but do receive updates when new Swift updates come out, or as I think of useful additions.

To ensure that I can make some of these less frequent updates without too much friction and with confidence in their correctness I rely heavily on GitHub Actions, which I'll go over in this blog post.

Keep Reading

Capturing More Than `self`


A common pattern when using closures in Swift is to add [weak self] in the captures list to hold a weak reference to self and avoid a retain cycle. This is then often followed by the following:

guard let self = self else { return }

But I often forget that capture lists can capture other variables in the current scope, so I thought I'd highlight some other use cases.

Keep Reading

HashableByKeyPath framework release 1.0.0


Today I have released the 1.0.0 version of a Swift package that aids with adding Equatable and Hashable conformance by using KeyPaths.

The package is available on GitHub.

I created the Swift Playground that sparked this concept in December 2018, so this concept has been rattling around in my brain for a couple of years. The API has changed a lot since the original concept, but the core has stayed the same: a protocol that requires a single function to be implemented that uses KeyPaths to synthesise Equatable and/or Hashable conformance.

Keep Reading

Partial framework release 1.0.0


Today marks 1 year since I released a blog post demonstrating an implementation of Partial in Swift, and it also marks the release of the 1.0.0 version of a Swift package for Partial.

The package is available on GitHub and supports SwiftPM, Carthage, and CocoaPods.

This blog post will go over some of the changes that have been made since the original blog post, my rationale when making certain decisions, and how I have thought about maintenance. If you want to try out Partial and see how it can be used head over to the GitHub page.

Keep Reading

Partial in Swift


Structs are incredibly useful in Swift, especially when representing static read-only data. However, the values of a struct often come from multiple sources, such as view controllers, network requests, and files on disk, which can make the creation of these structs cumbersome.

There are numerous methods to work around this, but each have their downsides. One of these methods is to change the struct to a class and update the properties to vars, but this removes the advantages of read-only structs. Another is to make a "builder" object, but the API of this object must be kept in-sync with the object is wraps.

Partial eliminates these problems by providing a type-safe API for building structs by utilising generics and KeyPaths. Although I learned of the concept of Partial through TypeScript – which [provides Partial as a built-in type][1] – the Swift implementation supports many more use cases.

Keep Reading

Gathered 1.3 Release Notes


Gathered 1.3 has been released and is now available on the App Store. Version 1.3 brings 2 new data sources, app-wide speed and UX improvements, and support for various features added in recent versions of iOS.

This update also has lots of behind-the-scenes changes that will make future updates easier to create and deploy, which – along with my features roadmap – should mean more frequent updates.

I wasn't very happy removing the Heart Rate data source but Apple weren't very happy with the use of HealthKit.

Keep Reading

iOS Share Sheets the Proper Way - Locations


Sharing a location on iOS is something that not a lot of apps need, but after requiring it for my latest app, Scanula, I found that there isn't a good resource explaining how to do it properly. This is the first post in a series of planned posts going over a few of the tips, tricks, and common pitfalls I have found while working with iOS Share Sheets.

Keep Reading

Exploiting University Security for My Own Convenience


This blog post covers an open-source timetable parsing project I released a couple of months ago. It is available at https://timetable.josephduffy.co.uk and the source is available on GitHub. The post won't go too in-depth on the technical side of the project, but rather the story of how I discovered it was possible.

Since starting my studies at the University of Huddersfield I've always wanted an easy way to see my timetable on my phone. The timetable available on the website isn't responsive and relies on POST data to display future weeks timetables, 2 things that don't work great on mobile, especially when the page is kept open in the background.

To get around this I would manually add each of my lectures and practicals to my calendar. These events could be set as recurring, however they would often need removing on specific days (such as during holidays) or have different information on another date, such as a room change. All of this eventually led me think about the famous XKCD Automation comic, so I started work on a method of automating adding it to my calendar.

Keep Reading

It's a Duffy Thing


I recently released a major overhaul for this website. The old website used an old version Node.js and used Ghost to power the blog. I didn't find it very easy to maintain and wanted more flexibility. While the new website may not have the best design, I'm a lot happier with it overall. Along with the rewrite of the website itself, I also gave it a new name: It's a Duffy Thing. This was inspired by a shirt that my Dad bought me.

Keep Reading

Touch ID on the Lock Screen


Touch ID is a wonderful piece of technology, to the point where wouldn't buy an iOS device without it. It had many great uses, such as:

  • Unlock the device
  • Authorise Apple Pay payments
  • Add biometric restrictions within apps

However, I wish to discuss the first of these: unlocking the device.

Keep Reading