This is a follow-up to Going Solo . 6 months ago I made the decision to dive in to indie iOS dev full time. And whilst it's been a fantastic experience, I've come to realize that it's not for me (not yet, anyway!). I don't think I knew how much I enjoyed working with others, as part of a team, before making this move, however it's something I've truly missed. I'm so fortunate for being able to try…
TrailPro My new app, TrailPro, is now available on the App Store ! It's entirely free (and ad-free), and available now for iOS. TrailPro is a fitness app for trail runners , and you could think of it as a cross between a conventional running app (Strava, Runkeeper, etc), and a hiking app (AllTrails, Komoot, etc). It sits at the end of the chain: you can record your workouts in your preferred app,…
Happy 2024! In this series of posts I'm going to be running through the implementation of a lightweight SQLite wrapper, from scratch, in Swift. Part 1: Opening a Connection Part 2: Binding & Fetching Values Part 3: Pooling Connections Part 4: Setup & Migrations Our Pool is now fully-functional, however there's one piece of functionality missing: how do we "set up" the schema of our database, and…
Happy 2024! In this series of posts I'm going to be running through the implementation of a lightweight SQLite wrapper, from scratch, in Swift. Part 1: Opening a Connection Part 2: Binding & Fetching Values Part 3: Pooling Connections Part 4: Setup & Migrations In the last two parts of the series, we got a single SQLite connection up and running, allowing us to execute queries, bind parameterized…
Happy 2024! In this series of posts I'm going to be running through the implementation of a lightweight SQLite wrapper, from scratch, in Swift. Part 1: Opening a Connection Part 2: Binding & Fetching Values Part 3: Pooling Connections Part 4: Setup & Migrations In the previous post we managed to open a SQLite connection, but what use is a connection to a database if you can't insert values or…
Happy 2024! In this series of posts I'm going to be running through the implementation of a lightweight SQLite wrapper, from scratch, in Swift. Part 1: Opening a Connection Part 2: Binding & Fetching Values Part 3: Pooling Connections Part 4: Setup & Migrations So why do this at all? There are a bunch of high quality packages that wrap SQLite, however I've always found it useful to have a strong…
I was recently designing an API with the following constraints: It's marked as @MainActor , so can only be executed on the main actor. It takes a value (a V , but the type doesn't matter right now). Within the API, it does a bunch of stuff across concurrency domains, however it only ever interacts with the value on the main actor . Picture this: @MainActor func handle<V>( _ value: V ) { Task .…
Following some discussion on Mastodon as a follow-up to my previous post, we concluded that it might not be a great idea to perform blocking I/O (i.e. SQLite operations) on the cooperative thread pool used by actors by default. Unfortunately I can't find this rule documented anywhere (would love to know if it is). The closest advisory is that Task s should always be able to make forward progress…
Whilst working on a lightweight SQLite wrapper for Swift, I've had a need to distribute workloads between a pool of actors. Specifically: I have a Connection actor, representing a single read or read/write SQLite connection. To support multiple concurrent reads, I have multiple Connection s. I have a .read API, which should choose a Connection before dispatching the work onto it. Here's a rough…
As part of my recent adventures into modern Swift concurrency, I've been looking to replicate a pattern that I've used throughout other projects I've worked on: observation. This can be boiled down to I want to know when some data has changed, so I can react to it . "React" in this sense could be "reload the data and repopulate the view", for example. Changes could come from all manner of places:…
From 2011 to 2014 I released 5 apps onto the App Store, with the first four now removed by Apple for being "outdated". RIP 🪦. The last one however must have escaped Tim Cook's scythe, and sat there untouched until a few months back. That was "01", a fun little game I built to help with practising binary-decimal conversions in your head. It had a pretty simple premise: given a couple of decimal…
A follow-up to this post is available here . TL;DR: a few weeks ago I quit my job, and I'm now officially self-employed. My plan is to spend the next two years (at least) as an independent iOS / Apple-platforms developer, with the aim of building and launching a sustainable product. I started developing for iOS back in 2010, releasing 5 apps in the years through high school and university: a todo…
There's often a need to coalesce a number of executions of some function over a period of time. The classic use cases include: Combining a number of rapid collection view updates: If you're loading a bunch of different data to support a collection view, you may want to combine the results of individual loads in order to keep the total number of main-thread collection view updates down. Handling…
Say you have a collection of elements, and you want to group them into a number of sets (that are each disjoint). One example could be grouping nodes within a graph that are connected by a path of edges. There are a number of different data structures you could use to do this, but one which I find particularly elegant is a disjoint set . Yes, the plurality is weird here. I tend to just go with…
I've been working on a small app to burn through the quarantine blues recently (more details maybe coming soon), and I wanted to setup a nice auto-versioning system to manage CFBundleVersion . CFBundleVersion ? If you check in your info.plist , you'll see CFBundleVersion , and CFBundleShortVersionString . What's the difference? CFBundleShortVersionString is the user-visible version number. This…
Expressing a range of values is an extremely common concept when programming, which is why I love that Swift includes a robust collection of types to represent ranges as part of its standard library. No more ad-hoc utility types for ranges with slightly different semantics scattered around your codebase, or having to choose between a number of third-party implementations ( cough Java). Swift…
Swift is a language where it's possible to write quite a bit of magic . What do I mean by that? I usually use the term to describe constructs that appear to be built-in, but are actually built by us . Swift's support for things like extensions on standard library types, property wrappers, first-class functions, and function builders, make it possible to abstract away a large amount of boilerplate,…
The first Xcode 11.4 beta was released on Thursday, along with a bundled version of Swift 5.2. There are a number of changes in Swift 5.2, however the ability to use key path expressions in the place of certain functions ( SE-0249 ) allows for some interesting constructions. So what is this change? In short, wherever you can use (T) -> V , you can also now use the key path expression \T.value .…
Following on from my previous article around thread safety , you might be tempted to take advantage of the property wrapper functionality introduced in Swift 5.1 and build an @Atomic property wrapper, allowing you to quickly wrap a property and make it thread safe. Let me tell you why this isn't a good idea. Perhaps your property wrapper would look like the following: @propertyWrapper public class…
Let's say that we want to process a bunch of values. The operations that we'll be performing are fairly intensive (and independent for each value), so we'd like to do this in parallel. We could use DispatchQueue.concurrentPerform for this, with the index of each iteration acting as the value that we want to operate on. var results: [ Int : Data ] = [:] DispatchQueue . concurrentPerform…
Value Types In Swift, structs are value types. What does that mean in practice? Let's consider the following: struct Recipe { var name: String var instructions: String var image: UIImage } let a = Recipe ( name: "Omelette" , instructions: "Break some eggs" , image: UIImage (named: "omelette" )!) var b = a // Copy! When initializing b from a , copying a , the struct is copied in its entirety,…
Last week my recent post surprisingly reached #1 on Hacker News, staying there for about 6 hours. The time period from then until it slid off the front page about a day later bought unprecedented traffic to this site, warranting a quick write-up. Along with Hacker News , the article was also reasonably well shared around on Twitter (200+ Tweets), Reddit ( /r/programming , and #1 on /r/math ), and…
I received this lamp as a birthday present last year. It's a great little thing - you can move the individual Tetris pieces around to form whatever shape you wish, and once connected they all individually light up thanks to conducting strips around the edges of each segment. Leaving the obvious Tetris connection behind for a second though, one thing that's always irritated me is my inability to…
In our standard base-10 counting system, we use the digits 0 through 9 to write numbers. This makes sense and seems natural to us, as it's what we're used to and it's what we were taught in school. However, base-10 isn't some magical system that the universe is built on, and there's no special rule that states that it's the counting system we have to use. It makes just as much sense to count…