“Offload unused apps” just offloaded YouTube while I was watching YouTube (I had briefly locked the device, was gone when I unlocked). Has also been offloading gmail all the time despite it being small and regularly used. Didn’t offload a 3GB game I downloaded a few weeks ago but never used.

My days of restricing how much I use the computer in the evening by leaving the charger in the office and waiting for the battery to die are over now that I’ve got an M1.

I built a modern style UICollectionView with a diffable data source for the first time and it felt so good. I liked the demos at WWDC (2019?) but the code didn’t immediately make me want to move away from the classic style. Glad I gave it another go.

I’m self-isolating with Covid. I don’t have the energy to complete any of my half-done apps, but I have just enough to start some news ones!

I spent the evening remotely collaborating on a Swift playground with 3 other developers, coding simultaneously in a live shared IDE.

It was fun—I think this is the future.

We were of course in Microsoft’s IDE, not Apple’s.

Selected Xcode versions and their download size

Version Size
4.6.3 1.59GB
5.1 2.11GB
6.4 2.61GB
7.3.1 4.7GB
8.3 4.20GB
9.4.1 4.91GB
10.3 5.6GB
11.7 7.59GB
12.5.1 10.98GB
13 9.98GB

Haven’t touched it in a while but getting back to my authenticator app this evening. Planning to keep it simple from here and get it shipped soon.

I hadn’t noticed this feature before, warning you when deleting an app with an active subscription is a nice touch.

Managing and deleting subscriptions has gotten a lot easier in recent years too, which only makes me more likely to sign up to them - smart move.

Twitter Phone Number Creepiness

I created a Twitter account to test setting up 2FA using the new authenticator app I’m working on. After the initial setup, turning on 2FA (my app worked!) and sending a tweet, twitter told me I might be a robot and had me complete a captcha and enter my phone number so it could send a verification text.

I didn’t see any indication that the phone number would be used for anything besides this verification but I immediately noticed that Twitter was recommending people to follow based on that phone number (I’m certain the email address I signed up with couldn’t be associated with those people in any way.)

I’m not surprised that Twitter wants phone numbers and uses them to build their social graph but requiring your phone number and not making it clear what it’s going to do with it strikes me as especially creepy, Facebook-level creepy.

Incorporating SwiftUI Views in a UIKit Layout

Mixing some SwiftUI in with your UIKit app is a great way to start using it for real without having to commit to fundamentally changing how you build your app. Doing so requires some boilerplate and when what you want to start with is a single view that only makes up a small part of a screen it might seem overly onerous.

This almost put me off, but I saw it through and turned the boilerplate into an extension on UIViewController which will add your SwiftUI View on top of a placeholder UIView that has been laid out with constraints (in my use case, from a storyboard).

extension UIViewController {
    func addSwiftUIView<Content>(_ swiftUIView: Content, for placeholder: UIView) where Content: View {
        let swiftUIController = UIHostingController(rootView: swiftUIView)

        guard let swiftUIView = swiftUIController.view else { return }
        swiftUIView.translatesAutoresizingMaskIntoConstraints = false
        swiftUIController.didMove(toParent: self)
        swiftUIView.constrain(to: placeholder)

My Swift Extension (Anti?) Pattern

I’ve become aware of how frequently I create extensions in Swift. I previously viewed them as something to use sparingly, like when there was something missing from a built-in type that was needed frequently - but now I regularly write them for once-off use.

This is the example that got me thinking about it today:

extension UInt8 {
    static func entireRange() -> ClosedRange<UInt8> {
        return UInt8.min...UInt8.max

Which exists only to be used in this other extension:

extension Data {
    static func randomBytes(size: Int) -> Data {
        var byteArray = [UInt8]()

        for _ in 0..<size {
            byteArray.append(UInt8.random(in: UInt8.entireRange()))

        return Data(byteArray)

Which exists to be called once in some test code that will eventually be replaced when working with the actual data.

I leave the extensions in context in the files where they’re being used rather than in a specific file for the extension, or a collection of helpers and I find the resulting code reads wonderfully.

Every authenticator app I’ve used has annoyed me in some way so I’ve started making my own.

The first annoyance I’m working on removing is having to wait around to get the next code when the timer’s running down.

Started a new project with SwiftUI about half an hour ago but I just backed out of it and started again with UIKit. There’s a lot I really like about SwiftUI but my head is in a making things place and not a learning things place today.

Moving iosapp.dev hosting to micro.blog. Had fun hosting it on my own server for the last while but looking forward to being able to post more frequently with this new setup - including shorter posts, like this one.

You'd Be Surprised What People Don't Know

Have you ever thought about trying to explain something, but it’s so obvious that you assume everyone already knows?

Have you ever thought that maybe there are lots of things you’ve never even though about explaining, because, well they’re even more obvious than that?

I’ve been thinking about this lately and I know for sure I can answer yes to the first question, and almost certainly even more so to the second. I’ve avoided unnecessary explanations - success! Except I’ve come to realise that a great many of the explanations I avoided, code comments I didn’t leave, articles I didn’t write, were missed opportunities.

As Daniel Jalkut put it in a recent episode of Core Intuition, sometimes we know Rome too well. In his metaphor a lifelong resident of a city is less suited to writing a guide for visitors than a more recent arrival whose freshly acquired knowledge is less ingrained. He was talking about marketing his product, but it immediately resonated with me for a slightly different reason: bringing new developers on to an old codebase. The one or two people who’ve been working on the app for the last 5 years won’t know what a new developer is going to struggle with, they may not spot that the fact that you have to do x,y and z before you call some particular function is non obvious.

With recent events there has been lots of sharing and explaining about a couple of things that I thought were pretty obvious. Hand washing, and exponential growth. It turns out a lot of people don’t know that soap is the thing that cleans your hands (not hot water or antibacterials) and that you don’t have to double things very many times before the numbers get huge. I’m really glad that people have been sharing because it wasn’t until I really thought about it that it made sense that not everyone knows this already and that I just happened to know them too well. It turns out my obsession with computers and taking chemistry in secondary school with a teacher that now that I think about it was way too excited about soap than is normal made me uncommonly equipped for current events.

I’ve learned that it’s important to examine how we’ve come to know something, and to consider if it’s reasonable to assume others will have had similar opportunity to come by this knowledge when deciding if something is worth explaining, commenting on or sharing. I’ve resolved to document earlier, comment better, and in general share more. I’m also planning to make newcomers central to my efforts to fill in documentation deficits and improve developer onboarding on older apps.

Hopefully at least one person reading this didn’t already know it all :)

Parenting Advice for New Programmers

I’d like to share some parenting advice if you’ll humour me.

My Mam shared this with me the day my first son was born and I think it applies to new programmers as much as it does to new parents.

These are both fields of endeavour filled with experts, some actual experts but more often self-imagined. 1

You will find yourself receiving advice from these experts, a lot. Some of it you may have actually solicited, more often though someone will have wandered into your conversation or overheard your child cry from a nearby restaurant table.

The advice is simply this: say thank you, and move on.

You’re free to ignore the advice if it is irrelevant or you know it to be wrong. The expert might be well meaning, so assume they are. The expert might be actual, but they almost definitely aren’t.

My Mam made clear I could apply this to her future advice, and every now and then I do. And you should apply it to mine because though I usually do mean well, I’m rarely an actual expert.

  1. Those people that believe the way that happened to work out well for them in their one specific situation is the one right way to do it for everyone in every situation.

Detecting When Your App Gets Backgrounded using Combine

The introduction of the Combine Framework provides a new (reactive) way to respond to system events such as your app entering the background.

In this example I have a CALayer subclass running some CABasicAnimations that need to be paused when the app gets backgrounded and resumed when the app becomes active again. In the layer’s init we attach a subscriber to the default notification centre’s publisher for the particualr events that we’re interested in. When attaching the subscriber we provide a closure that will be executed every time a new event arrives.

let bgSubscriber = NotificationCenter.default
   .publisher(for: UIApplication.willResignActiveNotification)
   .sink {
      _ in


let foregroundSubscriber = NotificationCenter.default
   .publisher(for: UIApplication.didBecomeActiveNotification)
   .sink {
      _ in


Let’s dig in a little to what’s going on here because it may not be obvious if you’re not familiar with Combine or NotificationCenter.

NotificationCenter predates Combine as a mechanism for broadcasting information (notifications) to interested observers. Every app gets a default notification centre which is used to broadcast system events such as those in the example here. Without Combine you can register to observe notifications and provide a selector to define what function to call when a notification is received.

With the introduction of Combine however, NotificationCenter added the publisher(for:object:) method which returns a Publisher that will emit values when notifications are broadcast.

A Publisher is how Combine represents a sequence of values over time that can be subscribed to. By calling sink(receiveValue:) we create a subscriber that will receive every value from the publisher.

The true power and utility of Combine isn’t seen in this example, but even still I don’t think it’s overkill to use Combine to provide a modern Swifty replacement for old fashioned notification observers and selectors.

A Note on Memory Management

To complete this specific example there’s a little more work to do. In my app I create many of these custom CALayer instances and at various points they get removed and new ones created in their place. With the code above as it is we’re preventing the layers from being deallocated when they’re no longer being used (because we captured self in the closure passed in to the sink call). To fix this problem we can keep a reference to the subscribers

private var appEventSubscribers =  [AnyCancellable]()

   appEventSubscribers = [foregroundSubscriber, bgSubscriber]


so that we can cancel them when the layer gets removed from its parent.

override func removeFromSuperlayer() {
        appEventSubscribers.forEach {


iPad OS or: Names Are Important

When I attended WWDC for the first time in 2010 the iPad was still brand new and not yet available in Ireland. Many had dismissed it as just a big iPhone, others (me, at least) had hyped it as A BIG IPHONE.

It ran iPhone OS version 3.2.

As we queued up before the Monday morning keynote we were all expecting the next iPhone and the next version of iPhone OS and we speculated about what new features we’d see. There was one seemingly minor announcement though, that nobody was expecting: the next version of the operating system would be called iOS 4, dropping the ‘iPhone’ to accommodate iPad. It made sense at the time and initially my only reaction was delight at how they managed to do it mid presentation, with nobody slipping up and using the new name before the announcement and nobody using the old one afterwards. In the years that followed though, as the ‘just a big iPhone’ opinions persisted and many developers put forth minimal effort in making their iPhone apps work on iPad, I came to think that not creating a separate identity for the iPad’s operating system was a missed opportunity, one that they finally rectified this year by renaming iPad’s operating system to iPad OS.

Perhaps making it incredibly clear that iPhone developers could make iPad apps made the previous OS naming scheme worthwhile up to a point but I believe making it clearer that iPad has its own OS, with its own interface, its own set of interactions and idioms that are distinct from iPhone will be a bigger benefit to software on the platform. OS naming is now consistent across all of Apple’s mobile devices - they are all based off iOS but Apple Watch runs watch OS, Apple TV runs tv OS and iPad runs iPad OS. These operating systems are not distinct in terms of technology or how we develop for them or who can develop for them, the distinction rather is in how the user interacts with them. Hopefully (and I believe it will) for iPad, this will result in more differentiated iPad software that takes advantage of its unique and powerful features.

Passing a closure to a UIButton

I’m tired of @objc #selector (nonsense: ) muddying up my Swift code. This most commonly rears its ugly head when dealing with buttons. Why can’t we just provide a button with a closure to execute when someone taps it? 1

// 😩
button.addTarget(self, action: #selector(myButtonHandler), for: .touchUpInside)

// 😍

Well now you can! I made a small UIButton subclass that provides a swifty facade for adding target/actions for events and otherwise behaves exactly like a regular old UIButton (to be clear, selectors are still doing the business under the hood.)

Full source on gist.

  1. The reasons are simple and boring and because we're still working with Objective-C frameworks from the 1890s (that for the record, I still love), but that wasn't the point of this post.

No, Apple Does Not Share Your FaceID Data

The notch full of sensors on iPhone X enables Face ID to capture

accurate face data by projecting and analyzing over 30,000 invisible dots to create a depth map of your face and also captures an infrared image of your face. A portion of the A11 Bionic chip’s neural engine — protected within the Secure Enclave — transforms the depth map and infrared image into a mathematical representation and compares that representation to the enrolled facial data.

Meanwhile, from the same notch, third party developers can access

a coarse 3D mesh geometry matching the size, shape, topology, and current facial expression of the user’s face.

These are 2 different things.

For more see Apple’s support article on Face ID and their developer documentation on ARKit Face Tracking.

The Apple Watch Platform

I’ve a phone in my pocket most of the time, Alexa is always waiting for me in the kitchen and I spend hours every day in front of an old fashioned PC but my watch is with me all day long wherever I go. Sometimes it’s the only computation available and almost always it’s the least intrusive.

Apple Watch, The Computer That's Always There

As good as Apple Watch is though, it has so far failed as an app platform. Apple Watch is built on the same technology that runs iPhone, and the same tools that developers use to make iPhone apps are used to make Apple Watch apps, so why are there so few Apple Watch apps, and why are so many really bad?

The original watch hardware was very limited, and app support even more so. Apps actually ran on your phone and were sort of beamed onto the watch’s screen. If you managed to find the apps on the terrible honeycomb grid they loaded really slowly and performed terribly. A lot of developers and users were instantly turned off of third party apps, but the watch got by with the excellent built in notifications and fitness tracking functionality.

Each release of watchOS and every hardware revision has seen incremental improvements to third party app support: apps actually running natively on the watch, custom watch face complications, new capabilities, better performance, and a better way of launching apps (a list!). But the great new app platform imagined when the watch was first announced has still to arrive and many apps on your Apple Watch today likely still date back to the original release.

I don’t know what it will take for the Apple Watch platform to become as successful as the Apple Watch but I don’t think the capability of the device or the OS is holding it back at this stage (though WatchKit does leave a lot to be desired.) I’d like to see watch apps completely decoupled from iPhone apps (they run on the watch now, but are still delivered as extensions of iPhone apps) and they need to have more ways to integrate with or at least appear on the watch face. At least then we might finally be able to rule out finding, installing and launching watch apps as the reason for there not being very many good ones.

Why Can't We Just Pay for Free Unlimited iCloud Storage?

Over the past few years Apple has proven that they’re willing to try charging higher prices for iPhone. Just a couple of years ago the 6S plus was priced from $749, a year later the 7 plus was available from $769 and now the 8 plus is on sale from $799. Meanwhile, the market has shown it’s happy to pay those prices and I suspect it will prove so once more with the impending $999 iPhone X.

What I’d like to see next year is for Apple to charge us even more money for phones that don’t cost them anything extra to produce, and here’s why:

The experience of figuring out that you might need an iCloud subscription, figuring out how much space you might need, paying for it, dealing with the inevitable failures to renew when your card expires or your balance is low, and getting warnings about backups failing is awful. I’d love to see Apple try to figure out the cost of providing all new iPhone users with unlimited (with an asterisk that says there’s actually some limits) iCloud storage and build it into the price of the phone.

I pay Apple $35.88 for iCloud storage each year, I’d happily pay $99 more for the phone instead.

Audio Degapinator - The Poor Dev’s Smart Speed

I’ve been listening to podcasts with Overcast’s Smart Speed feature turned on for long enough to have saved 55 hours of not listening to the silences between every podcast host’s thoughts.

I decided to spend 1 of those hours today making my own very simple, very limited, but surprisingly effective AVAudioPlayer version of that feature. I’ll explain below how it works, but you can check out the full Swift iOS source (there’s not much to it) on GitHub: Audio Degapinator on GitHub.

AVAudioPlayer offers features for audio level metering:

/* metering */
open var isMeteringEnabled: Bool /* turns level metering on or off. default is off. */

open func updateMeters() /* call to refresh meter values */

open func peakPower(forChannel channelNumber: Int) -> Float /* returns peak power in decibels for a given channel */

open func averagePower(forChannel channelNumber: Int) -> Float /* returns average power in decibels for a given channel */

And for adjusting playback, including:

open var rate: Float /* See enableRate. The playback rate for the sound. 1.0 is normal, 0.5 is half speed, 2.0 is double speed. */

My code then:

  • turns metering on
  • updates meters with a timer
  • checks if there is currently silence playing using averagePower
    • increases the playback rate 2x until the silence ends

I tested using the latest episode of ATP and Debug episode 49. In both cases the silences were noticeably reduced and, to my ear, sounded completely natural. I listened to the entire episode of Debug and it had shaved off a little over 3 minutes by the end.

This was a fun little project, it’s the first time I’ve looked at anything related to audio playback on iOS in quite a while and it was super interesting … I fear I may just have to write my own podcast app now.

Simulating Universal Gravitation with SpriteKit

Gravity in SpriteKit is a single planet sort of gravity. By that I mean that it applies a single force to all bodies in the simulation - basically everything falls down. But what if you wanted a mutliple planet sort of gravity, can that be achieved in SpriteKit?

The answer is yes, and it turns out it’s it doesn’t take a whole lot of code to get some fun and quite realistic results.

Screen capture. Small circular nodes orbit a larger central node like planets around a star.

In this SpriteKit app, dragging on the screen creates a new ‘planet’.

How it Works

First we turn off gravity as it normally applies in a SpriteKit scene and then on every tick we apply Newton’s law of universal gravitation to all the nodes in the physics simulation.

F=G m1.m2/r^2

That is for every pair of nodes, apply a force to each one that is equal to the product of their masses (and the universal gravitational constant), divided by the distance between them, squared.

There are some tweaks to the above formula to make the numbers a bit easier to deal with (i.e. smaller) and to make creating stable systems a bit easier, but sticking exactly to the formula above and plugging in some realistic numbers things work pretty much as you’d expect.

In addition to simulating gravity, I’m also combining planets that pass close to each other and adding trails to trace their paths and giving new planets random colour. It all results in a surprisingly fun and addictive little toy so even if you’re not interested in the code just build and run it on your iPhone (or watch, or Mac) and enjoy!

Get the code at github.com/mbehan/fgmmr.

Detecting Which Complication Launched Your WatchKit App

One of the joys of working with watchOS, much like it was working with iPhone OS many years ago, is the enforced simplicity. Free from worrying about about the unending device combinations and configurations and the unlistible features and extension points of modern iOS the constraints of a limited SDK focus your creativity. Simple, robust, yet still delightful interfaces flow from your fingertips, designers designs are readily translated to working product.

Sadly though, we’re not content for long. Just like in the early days of iPhone OS, you soon find yourself wanting to do just a tiny bit more than Apple has made available, and so focus and delight makes way to our more common friend, the ugly hack. Today’s feature that just couldn’t wait for a proper API is: detecting which watch face complication launched my app.

How It Works

When your app is launched in response to the user tapping a complication, the handleUserActivity method of your WKExtensionDelegate is called. You’re given a userInfo dictionary, and this is where we’d hope to find the details of which complication had launched us. Sadly though there’s no CLKComplicationFamilyKey to let you know the user tapped the circual small rather than the utilitarian large to lauch the app, but there is something we can use, the CLKLaunchedTimelineEntryDateKey. This gives us the exact date and time that the complication was created at. By remembering exactly when we created which complication then we can figure out which complication resulted in the app being launched and acting accordingly.

The Code

// 1.
class ComplicationTimeKeeper{
    static let shared = ComplicationTimeKeeper()
    var utilitarianLarge : Date?
    var utilitarianSmall : Date?
    var circularSmall : Date?
    var modularLarge : Date?
    var modularSmall : Date?

// 2. in your CLKComplicationDataSource
func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping ((CLKComplicationTimelineEntry?) -> Void)) { 
     // Call the handler with the current timeline entry
     switch complication.family {
     case .utilitarianLarge:
         let date = Date()
         ComplicationTimeKeeper.shared.utilitarianLarge = date
         let template = CLKComplicationTemplateUtilitarianLargeFlat()
         template.textProvider = CLKSimpleTextProvider(text:"Something")
         let timelineEntry = CLKComplicationTimelineEntry(date: date, complicationTemplate: template)
     default: handler(nil)

// 3. in your WKExtensionDelegate
func handleUserActivity(_ userInfo: [AnyHashable : Any]?) {
    guard let userInfo = userInfo, let timelineDate = userInfo[CLKLaunchedTimelineEntryDateKey] as? Date else{
    if let utilLarge = ComplicationTimeKeeper.shared.utilitarianLarge, timelineDate.compare(utilLarge) == .orderedSame {
         WKExtension.shared().rootInterfaceController?.pushController(withName: "SomeController", context: nil)

In 1, we create a singleton (no shameful hack is complete wihtout one) to track when our various complications were made.

In 2, we setup the utilitarian large complication and store the creation date, just add more cases to the switch statement for other complication families that you are supporting.

Finally in 3 we check what time the complication that launched the app was created and check which one it was and launch the relavant interface controller.


The code above has a couple of limitations that you may need to work around. First it doesn’t take Time Travel into account so if your app supports that each complication may have more than one corresponding datetime. Secondly (though in practice I haven’t seen this be an issue) I don’t see why two complications couldn’t have clashing datetimes, for that you could add a method to ComplicationTimeKeeper that returns the next unique date.

It’s Time For Complications

Apple made much of the value of complications at this years WWDC. Having originally not allowed you to make your own in watchOS 1, to allowing you but telling you its only if you really have something super important that gets updates throughout the day in watchOS 2, now this year they told us we really need to have a complication even if its just an icon to launch your app. It seems they’ve noticed, as anyone who has worn Apple Watch for any reasonable amount of time will tell you, that complications are the best way to access the functionality of an app. But everything they talked about at WWDC was about having a complication, singular. You can support multiple complication families, but you can only have one of each and they are treated as different views of a single feature, showing more data when you’ve the room, but not really doing anything different.

Ideally, we’d have the ability to provide multiple complications for each complication family. If that was the case you could have a watch face with each complication slot filled by the same applicaiton, each showing something else (the built in world clock complication can already do this, but nothing else) and crucially each performing a different function of your app when they’re tapped. I wouldn’t be surprised if this is something that is eventually supported in WatchKit, but for now at least we can ugly hack our way to using different complication families to provide different functionality.