← Blog

Building Slumba: SwiftUI on every Apple device

April 17, 2026 · Ian Goudie

Slumba is a sleep sounds app that ships natively on iPhone, iPad, Mac, Apple TV, and Apple Watch. It's built entirely in SwiftUI with zero third-party dependencies. One in-app purchase unlocks everything across all five platforms via StoreKit 2 and Family Sharing.

This post covers the technical decisions that shaped the app: how the audio engine works, why it sounds better than a naive loop, how one SwiftUI codebase adapts to five wildly different form factors, and where Apple's frameworks did the heavy lifting.

The problem with looping audio

Most sleep sound apps load an audio file and loop it. The problem is obvious after a few minutes: your brain catches the seam where the loop restarts. A rainstorm suddenly resets. Thunder claps repeat in the same sequence. The illusion breaks.

Slumba solves this with a dual-player architecture. Each sound layer runs two instances of AVAudioPlayer simultaneously, staggered in time. When one player approaches the end of its loop, the second player has already started its own pass at a randomized offset. Per-loop jitter varies the stagger timing so no two cycles align the same way.

The result: you can listen for hours and never catch the restart point. The audio feels continuous rather than cyclical.

Three layers per preset

Each of Slumba's five presets blends three independently-adjustable sound layers. Storm, for example, layers heavy rain, gale-force wind, and rolling thunder. Each layer has its own volume slider, and each runs its own dual-player pair.

That's six AVAudioPlayer instances per preset (three layers, two players each), all coordinated to start, stop, and fade in sync. The audio session is configured for background playback and mixes with other audio sources, so Slumba keeps playing when you lock your phone or switch apps.

SwiftUI across five platforms

Slumba targets iOS, iPadOS, macOS, tvOS, and watchOS from a single Xcode project. SwiftUI makes this possible, but "write once, run anywhere" is misleading. Each platform has different interaction patterns, screen sizes, and capabilities.

The core audio engine and preset data model are fully shared. The UI layer branches by platform where it needs to. A few examples:

Conditional compilation (#if os(tvOS), #if os(watchOS)) handles the divergence points. The goal was to avoid platform-specific targets entirely and keep one SwiftUI view hierarchy that adapts.

Deep Apple integration

One of Slumba's design principles is to integrate with as many Apple frameworks as possible rather than reinventing functionality inside the app. Here's what that looks like in practice:

Each of these integrations is a small amount of code, but together they make Slumba feel like it belongs on the platform rather than just running on it.

The star field

Slumba's ambient display shows a star field that's positioned using real astronomical coordinates. The app requests location access (entirely optional) and uses it to calculate which stars are visible from your position at the current time. The rendering is pure SwiftUI — no SceneKit, no Metal, no third-party astronomy libraries.

This was the most over-engineered feature in the app and also the one that makes people stop and look. Sometimes the right answer to "should I build this?" is "yes, because it's fun."

No third-party dependencies

Slumba has zero external packages. No analytics SDK, no crash reporter, no networking layer, no ad framework. The entire app is Apple frameworks and first-party code.

This isn't a philosophical stance against third-party code. It's a practical decision: every dependency is a maintenance burden, a potential privacy concern, and a binary size cost. For an app that does one thing well and runs on five platforms, the dependency surface area should be zero.

The tradeoff is real — no crash reporting means relying on App Store Connect's crash logs, which are delayed and incomplete. But for a solo developer shipping a focused utility app, the simplicity is worth it.

What I'd do differently

The audio engine would benefit from AVAudioEngine instead of AVAudioPlayer. AVAudioEngine gives finer control over mixing, crossfading, and real-time audio processing. I started with AVAudioPlayer because it was simpler to get working across all five platforms, and by the time I wanted more control, the dual-player stagger approach was already working well enough to ship.

The SwiftUI conditional compilation approach works but gets noisy as the platform count grows. A cleaner architecture would use protocol-based platform adapters that hide the #if os() blocks behind a single interface.

Try it

Slumba is free to download on the App Store. A one-time $2.99 in-app purchase unlocks all five presets on every Apple device. No subscription, no tracking, no ads.

If you have questions about the architecture or want to talk shop, reach out at ian.goudie@gmail.com.

Slumba app icon

Ian Goudie

Indie developer building Slumba, a native sleep sounds app for every Apple device. Built entirely in SwiftUI.