Departed Summer 2025
iOS Music App
the iPhone, offline · iOS app · Shipped
[image: iOS app Playlists grid in dark mode — Music Library of square album covers, Mac DeMarco, Debussy, and Alice DJ visible in the grid]
Departure
Davis Mountains, McDonald Observatory, no bars. Spotify opens to a sad face and asks if I'd like to log in. I have a free account, the MP3s are sitting in my Files app from years of scraping, and the iPhone has known how to decode audio since 2007 — somewhere in this stack the music should already be playing. So I opened Xcode in the camper and started a Swift app whose only job was to point AVAudioPlayer at a local file and press play.
Approach
- Swift
- AVFoundation
- MediaPlayer
- MPNowPlaying
No internet at the campsite. No Spotify Premium. Audio files already on the phone, nothing else.
Field log
Aug 2025 — Davis Mountains
Drove out to the McDonald Observatory dark-sky site. Zero LTE the whole valley. Starlink dish on the picnic table covered the laptop, but the phone in my pocket was on its own — and Spotify Free won't let you play your downloads without a check-in. The MP3s were sitting in the Files app, untouched.
Day 1 — paper
Sketched the whole app on one sheet of notebook paper. Five rectangular buttons, no icons, no styling. That was the spec.
[image: Hand-drawn paper wireframe of music app — five crude rectangular buttons labeled 'Play 1, Play 2, Play 3, Play 4, Auto Generated, Artists, ALL' under the title 'Music']
Day 1 spec. First playback
Pointed AVAudioPlayer at one bundled MP3 and called play(). Sound came out of the iPhone speaker with no router, no DNS, no account in the loop. The slide deck's 'problem' line was solved in about thirty lines of Swift.
Library
Replaced the paper buttons with a real Music Library — dark mode, square grid pulled from the file metadata. Mac DeMarco next to Debussy next to Alice DJ. Tap a cover and you drop straight into Now Playing with the right art and the right title.
Controls
Built the Now Playing view around 'Say My Name — Remix' as the test track. Wired the scrubber to AVAudioPlayer's currentTime so dragging the playhead actually moved the song instead of lying about it.
[image: iOS Now Playing screen for 'Say My Name - Remix' — large square album art at top, draggable scrubber with elapsed and remaining time, play / pause / skip controls underneath]
Queue
Up Next list backed by a plain array the player chews through in order. Tap a song from the library and it goes to the top; queued remixes line up underneath and roll forward when the current track ends.
[image: iOS app Queue screen — 'Up Next' header with the currently playing track highlighted at the top and a vertical list of upcoming remixes and tracks below]
Lock screen
Pushed the current track into MPNowPlayingInfoCenter and registered play / pause / next / previous on MPRemoteCommandCenter. Lock screen suddenly showed the cover and title, the AirPods squeeze worked, and CarPlay treated it like any other audio app.
[image: iPhone lock screen showing the music app's MPNowPlaying card — album art on the left, song title and artist on the right, scrubber and play / pause / skip controls below]
From the gallery
[image: Davis Mountains landscape from the campsite — rocky foreground with prickly pear cacti, rolling hills under a dramatic sky, late-afternoon sun cutting through scattered clouds]
[image: Flat rectangular Starlink user terminal sitting on the picnic table beside a laptop, cable running off frame toward the router]
What I came back with
Offline playback
Lesson from the terrain
When the platform's answer is 'subscribe or no music,' the iPhone underneath it is still a perfectly good audio computer — AVFoundation will play any file you point it at, and MPNowPlaying will hand that playback to the lock screen and the AirPods for free. Self-hosting your own software is mostly the willingness to write the thirty lines that the SaaS layer was charging rent for. The trip didn't need Spotify; it needed a binary I owned.
Cross-links
This fed into / from