By day: iOS at an AI unicorn šŸ¦„ @meetgranola By night: I write Jacob's Tech Tavern šŸŗ

London
Every week, I’ll send you ludicrously in-depth articles about: - Swift concurrency - Agentic engineering​ - iOS performance​ - Advanced SwiftUI - Under-the-hood Swift internals​ New subscribers also get a free eBook: The Complete Swift Concurrency Toolkit. Cheers! šŸŗ blog.jacobstechtavern.com/we…
1
7
7,574
Swift Subprocess shines when we write complex automation scripts to orchestrate lots of tools together, such as reading files, fetching from the network, and processing media. In this more complex pipeline, we’ve refactored all the actual process invocations into separate little helper functions. (Screenshot #1) In this final tool, we start with a list of URLs in thumbnails.txt, download each file into a tmp directory + convert them to PNGs (Screenshot #2), then stitch them all together into a single mp4 video using ffmpeg (Screenshot #3). Neat, right? The identical version in Bash (Screenshot #4) will get you a much better score at code-golf, but you tell me which version feels more maintainable if you want to extend the behaviour in the future. Subscribe to my blog to accelerate your iOS career with new posts every week! šŸš€ join.jacobstechtavern.com
2
9
663
Apple Animation Through the Ages: 2019 Facebook’s React Native introduced cross-platform app compilation using standard React syntax in 2015. Shortly after, in 2017, Google introduced Flutter, which used the Dart language to allow React-style UI development across web, Android, and iOS. Both approaches came packed with developer-friendly ergonomics like hot-reloading UI. This competition, coupled with the uncomfortable platform split across iPhone (UIKit), Mac (AppKit), and Apple Watch (WatchKit), native app development was beginning to look like a bad long-term bet for new engineers. SwiftUI was announced as the future of Apple platform UI development in 2019, spawning years of arguments about whether it was production-ready (it is). SwiftUI was the declarative framework to create UI across all Apple platforms. It was built on top of Core Animation and Metal foundations and made animation far easier than ever before.
8
975
autoreleasepool in practice: reducing peak memory footprint and fixing a crash in my new indie project Here’s an autoreleasepool I actually used yesterday to solve a crash when performing a SwiftData migration in my shiny new app (coming out next month!). I was moving away from (insanely inefficient) raw data storage towards storing file-path references in data. To migrate without losing all my data, I performed the raw data to image to compressed file conversion on the fly, in a loop. Originally, there was a giant memory spike that crashed my app out, because 50 huge 48MB UIImage bitmaps were held in application memory. With an autoreleasepool block, we solve the crash by letting go of each bitmap with each loop iteration, allowing the migration to run without a hitch. Consider using autorelease pool whenever you are looping through many reference-counted objects. I have recently used this for processing many images together into a video, or processing, transforming, and persisting many separate Core Data objects at once. Read the blog post here šŸ‘» blog.jacobstechtavern.com/p/…
1
1
14
976
Me when I first noticed that $1.04 was being deducted off a $6 subscription
5
1,255
NFC, or near-field communication, is a family of short-range wireless standards that operate at 13.56MHz radio frequencies. It’s a specific branch of radio frequency identification, or RFID. You use it every day you pay with contactless, Apple Pay, or even download an App Clip! NFC is very useful for reading and writing low-bandwidth information at centimetre-scale, i.e. deliberate, range. But NFC doesn’t just use the device radio. And it works even if one partner is un-powered. It does something genius, using Faraday’s law of induction. Get it? Right, for those that don’t have their second-year electromagnetism course fresh in their mind, this law basically shows that you can generate an electric current, at a distance, by moving a magnetic field through a coil. It’s only how the global power grid works. In an NFC interaction, the powered reader/writer produces a changing magnetic field that produces a voltage across a wire coil in the passive NFC tag. This drives a current and powers the chip, allowing it to accept a signal and read/write data. This magnetic field, as well as oscillating at the 13.56MHz NFC frequency, also encodes information in its amplitude, so powering and writing happen simultaneously! Now let’s take a look at how to implement NFC in your own apps using CoreNFC. Learn how iOS talks to your hardware in ā€œNFC, BLE, UWB, WTF?!ā€ 🧐 blog.jacobstechtavern.com/p/…
1
3
720
I got hold of NFC tags, a programmable BLE chip, and an old iPhone 13 mini (I didn’t want to shell out Ā£69 for a dedicated UWB chip) Once we’ve mastered short-range wireless communication technology, I’m going to put all these pieces of hardware together to implement the player-to-player communication system from Dark Souls and Elden Ring… in real life. This involves writing hidden messages on the ground that users can search for, unveil, and read to get a handy tip (or perhaps a not-so-handy one). Users can then leave their own messages for the next players! Learn how iOS talks to your hardware in ā€œNFC, BLE, UWB, WTF?!ā€ 🧐 blog.jacobstechtavern.com/p/…
6
1,010
This is worth the $15/month alone
2
26
2,423
As there's a lot of anti‑cross‑platform sentiment, this is actually a very good point: From what I understand, the most tangible advantage of something like React Native isn’t just that you can ship on both platforms, but the developer experience and tooling like Expo is far beyond what native forces on you. For example, instant web deployments with hot reloading as you code logic
I built the same four-screen app six ways — native, React Native, Flutter, Compose Multiplatform — with agents doing the work. Some teams dismiss native's cost too quickly. In my tests, it was the slowest by a wide margin — up to 3.5Ɨ the agent minutes.
4
17
3,805
Firebase had a catastrophic outage yesterday, but that's not why I'm too scared to use it - I'm anxious about recursion on Firebase Cloud Functions and getting a huge unexpected bill: 10 Document write triggers a could function 20 Cloud function writes a document 30 GOTO10
4
761
Stumbled upon an absolutely crazy AVFoundation API today
6
146
11,408
Advanced Accessibility in SwiftUI: Using accessibilityRepresentation to help the screenreader In some situations, you might have a really complicated UI, such as a stylised timer element which graphically ticks down. VoiceOver is smart, but it isn’t going infer anything useful from a fancy collection of unexplained views. This is where the accessibilityRepresentation modifier comes in really useful. It allows you to replace a view’s VoiceOver UI with a fully customised set of views that are only visible to the screenreader. This was particularly useful for my latest indie project, Check ’em, where a user’s 2FA codes were displayed with a countdown: This modifier allowed me to introduce a fully custom VoiceOver interface, which was far more useful than iterating through each element of the cell in sequence. It also prevented the screenreader reading `676,252` as six hundred and seventy-six thousand, two hundred and fifty-two. Just the numbers individually makes more sense for a 2FA code. Learn how to speed run accessibility in your SwiftUI app in the full (free) article: blog.jacobstechtavern.com/p/…
3
1
7
1,144
Git, Vercel, and Cloudflare: The 3 amigos. Frankly, if you held a gun to my head, and told me to explain what Vercel does or you’ll blow my brains out, my last words would be a curse upon Vercel. I kinda reckon it hosts web servers, but don’t take my word for it. With some slightly annoying admin, you can make it so every git push automatically goes through to Vercel and gets deployed. So… I guess it’s a CI service? ļæ¼ Cloudflare is a DDoS protection service, which for some reason also does domain registration. I set up a CNAME for the subdomain games.jacobstechtavern.com that keeps my wildest ideas airgapped from my colleagues at Granola. This basically means for my jacobstechtavern.com domain, I can easily create subdomains, and Vercel routes the deployed web pages there. Vercel and Cloudflare work together in ways I simply cannot comprehend, but they steam a good ham. The point is that, now, the cost of deploying is zero. The cost of implementing is also zero. The cost of putting my thoughts into a computer is reduced to a moment of mumbling. Thus blurs the line between ideas in your head and working software in your hand. Read ā€œWho needs AI psychosis?! I gave myself VR psychosisā€ here 🤠 blog.jacobstechtavern.com/p/…
1
4
732
If you want people to block your email address, this is the best way by far
2
4
769
I think it’s time I brought back this particular Swift rant: Swift was introduced with `class` functions (that behave statically but they’re overrideable by subclasses). But Swift also ideologically opposes the ubiquitous `abstract` classes because ā€œmuh protocolsā€. The language dropped its ā€œone way to do thingsā€ mantra YEARS ago. We have 219 keywords but not this one actually useful one. The language is adding borrowing semantics to satisfy several dozen systems programers, while ignoring abstract classes which might be useful for millions of developers that practicing OOP.
11
3
60
6,451
iOS can speak to hardware peripherals with a wide range of modern wireless communication tech: - NFC (near-field communication) - BLE (bluetooth low energy) - UWB (ultra-wideband) Some of these are protocols, some are specific radio standards, some require custom physical hardware work. But each 3-letter initialism is essentially a marketing term for an umbrella of wireless technologies that work at different layers. Ok. So. Today… - I’ll cover these technologies so you get the gist of how they work. - With some nifty hardware I procured, I’ll give you a live demo to show what these technologies are useful for. - I’ll show real, working code that allows you to interact with these technologies in your apps today. Learn how iOS talks to your hardware in ā€œNFC, BLE, UWB, WTF?!ā€ 🧐 blog.jacobstechtavern.com/p/…
1
1
11
983
CAEmitterLayer is ā€œforbiddenā€ because you need to fight like hell to sneak it past your product team. But god, it’s worth the scrap. 
CAEmitterLayer is a Core Animation thingy that makes cool particle effects. You can make confetti, snow, sparks, fireworks, smoke (I've done this before for a social media streak counter šŸ”„), or glitter. It lets you make fun. Give it a try: 
 CAEmitterLayer is famously finicky. 
Clipping and layout timing can be easy to muck up, it will disagree with you on your definition of ā€œnowā€, tweaking the million knobs takes forever, and you’ll almost certainly need to whip out that trusty UIKit classic DispatchQueue.main.asyncAfter(deadline: .now())to get anything to bother showing up.
But when it works, boy does it work. If you want a tip for getting it past your designers, offer to do… just a subtle little bit of confetti, perhaps clipped to the inside of a banner image… for now. Read ā€œForbidden iOSā€ here šŸ‘¹ blog.jacobstechtavern.com/p/…
1
2
37
2,758
How does AlarmKit work under the hood? If you’ve ever suffered from sleep paralysis, AlarmKit is largely powered by the same thing under the hood: Daemons. Daemons are system programs that run in the background. On iOS, they power core services such as the home screen (SpringBoard), touch & motion events from hardware (backboardd), audio & video playback (mediaserverd), and location services (locationd). A daemon also powers iOS alarms, in both AlarmKit and the system’s Clock app. The basic underlying functionality in the Clock app is provided by MobileTimer.framework. This framework contains the full functionality powering timers and alarms. When using classdump-dyld or RuntimeBrowser, the full header interface for the alarms can be generated into a header file, Alarm.h. The interface is very similar to the AlarmKit scheduling parameters we’re going to look at soon, so it’s a fairly clear indication that Clock and AlarmKit are both built on top of this framework. So I think we are okay to use them interchangeably going forward. The Clock app has a private entitlement, com.apple.private.mobiletimerd, which grants it permissions to communicate with the mobile timer daemon (which itself links and imports MobileTimer.framework). This privileged program runs constantly in the background, monitoring alarms set up by the framework, scheduling them, and triggering them by calling _fireScheduledAlarm(). When fired, the alarm tells SpringBoard to interrupt whatever’s happening, and present the alarm UI. Alarms have special system privileges: - They override silent mode. - They use a private AVKit audio session category to ignore muting. - They are allowlisted by focus modes like Do Not Disturb or Sleep Focus. Read my full AlarmKit deep-dive here ā° blog.jacobstechtavern.com/p/…
12
1,062
Jolly good UX here
1
6
955
I have not yet summoned the courage to write about Swift concurrency internals
2
24
1,430
Advanced Swift Concurrency: Launch Speed Optimisation You can parallelise app launch work in didFinishLaunchingWithOptions using a GCD DispatchGroup to wait and Swift's taskGroup to parallelise steps
9
10
172
12,426
This avoids performing all critical work serially on the main thread - but use this with caution - any main-thread isolated work can deadlock if you don't understand what you're doing. I run you through a practical session on app performance in my blog: buff.ly/41eqvb9
1
1,054