Over the past hours, we've shipped three major phases of Kanora's distributed host/client library system. The full stack—discovery, API, and client integration—is now functional and ready for user testing.
What We Built
Phase 1: Discovery & Sidebar (iOS)
Features:
- Bonjour discovery of Kanora hosts on home network
- Discovered hosts stored in Core Data
- Library sidebar picker showing local + remote sources
- One-click switching between libraries
Result: iOS users see discovered Mac hosts and can select them as a library source.
Phase 2: Remote API (macOS)
Endpoints: 8 REST endpoints serving library data
GET /api/health- Connection checkGET /api/artists,/api/artists/{id}- Artist browsingGET /api/albums,/api/albums/{id}- Album browsing with tracksGET /api/playlists,/api/playlists/{id}- Playlist browsingGET /api/files/{trackId}/stream- Audio streaming with HTTP 206 range support
Implementation: SwiftNIO-powered HTTP server on port 8008, reusing the existing MediaHTTPServer infrastructure.
Result: macOS exposes its entire library via REST, ready for iOS clients to query.
Phase 3: Client Integration (iOS)
Components:
- RemoteLibraryService: HTTP client implementing LibraryServiceProvider protocol
- RemoteFileCache: Transparent caching of remote files before playback
- PlaybackService Integration: Automatic download-then-play workflow
- Library Context Switching: Seamless local ↔ remote switching
Result: iOS users can browse remote libraries with the exact same UI that works for local music, and audio streams automatically from cache.
End-to-End Workflow
Here's what's now possible:
User Action → iOS Component → Result
─────────────────────────────────────
1. Open Kanora on iPhone
→ Bonjour discovers Mac host
→ Host appears in sidebar
2. Tap "Ben's Mac" in sidebar
→ REST health check passes
→ Switch to RemoteLibraryService provider
3. Tap "Artists"
→ HTTP GET /api/artists
→ Local UI populates with remote data
→ No UI changes needed
4. Browse to album, tap "Play"
→ HTTP GET /api/files/{trackId}/stream
→ RemoteFileCache downloads to device cache
→ PlaybackService plays from cache
5. Playback seamless
→ User doesn't know it's remote
→ Cache persists during session
→ Next song pre-downloads in background
Architecture Decisions
Why This Approach?
Discovery:
- Bonjour: Zero-config, "just works" on home network, trusted boundary
- No authentication needed for MVP (home network assumption)
API:
- REST over HTTP: Simple, stateless, cacheable
- JSON serialization: Lightweight, easy to test
- Mirrored data model: Views don't know local vs remote
Streaming:
- Download-then-play: Better UX than buffering
- Cache on device: Foundation for offline playback (Phase 4)
- HTTP 206 range support: Enable adaptive bitrate future
Service Layer:
- LibraryServiceProvider protocol: Views transparent to data source
- Actor-based state: Safe concurrent access
- nonisolated methods: Enable View integration
What We Avoided
Complexity:
- No authentication (Phase 2: trust model sufficient)
- No remote playback control (Phase 4: future)
- No offline archival (Phase 4: future)
- No external network access (home-only for now)
Anti-patterns:
- No ViewModels for remote browsing (use existing views)
- No async/await blocking in UI (proper actor isolation)
- No duplicate data fetching (cache awareness built in)
Implementation Stats
Lines of Code (new):
- Phase 1: ~500 (discovery, UI, tests)
- Phase 2: ~900 (API, models, tests)
- Phase 3: ~600 (client, cache, integration)
- Total: ~2000 lines of focused, tested code
Commits:
- Phase 1: 5 commits
- Phase 2: 5 commits
- Phase 3: 4 commits
- Total: 14 focused commits with clear separation of concerns
Tests:
- Phase 1: 3 unit tests
- Phase 2: 3 integration tests
- Phase 3: 8 integration tests
- Total: 14 tests covering the full stack
Build Status:
- iOS: ✅ SUCCESS
- macOS: ✅ SUCCESS
Key Technical Highlights
1. Bonjour with Modern Swift Concurrency
Using Network.framework's NWBrowser with async/await patterns—no callbacks or completion handlers.
2. Service-Oriented Architecture
LibraryServiceProvider protocol makes the entire system testable and viewable-agnostic. Swap implementations without touching UI code.
3. Transparent Caching
RemoteFileCache handles downloads silently. PlaybackService just calls "play track"—it works whether local or remote.
4. Proper Actor Isolation
All services use actors for thread safety. Core Data operations properly isolated to MainActor. No data races or concurrency issues.
5. Reused Infrastructure
Built on top of existing:
- MediaHTTPServer (SwiftNIO)
- Core Data models
- Existing library UI components
- PlaybackService
What's Next
Phase 4: Offline Download Management (Future)
- Download queue UI
- Selective download (by artist, album, playlist)
- Storage management
- Background sync
Phase 5: Remote Playback Control (Future)
- Tell Mac what to play
- Control speakers from phone
- Queue management on remote device
Phase 6: Multi-Host Support (Future)
- Browse multiple Mac libraries
- Unified search across hosts
- Smart caching prioritization
For Contributors
If you want to extend this:
- Add a new API endpoint: Modify
RemoteLibraryAPIServiceinKanora macOS - Add a new query: Create new method in
RemoteLibraryServiceandLocalLibraryService - Test an endpoint: See
RemoteLibraryIntegrationTestsfor patterns - Debug discovery: Check
DiscoveryServicelogs for Bonjour events
All services follow the same patterns. The architecture is meant to be extended.
Testing This Yourself
- Run Kanora on Mac (any library will do)
- Run Kanora on iPhone/iPad on same WiFi
- Look for "Ben's Mac" (or your Mac name) in the library sidebar
- Tap it
- Browse—it should feel exactly like browsing local music
The Philosophy
This implementation embodies Kanora's core belief: you own your music, you control your music, no subscription required.
The host/client architecture means:
- Your Mac is your music server (you control the hardware)
- Your iPhone/iPad access it (you control the devices)
- No cloud sync, no DRM, no tracking
- Everything on your home network (privacy preserved)
It's what iTunes Home Sharing felt like, but designed for 2026 with modern Swift tooling.
Thank You
This work leveraged:
- Swift Concurrency (actors, async/await)
- SwiftUI for iOS UI
- SwiftNIO for HTTP
- Core Data for persistence
- Network.framework for Bonjour
- Claude Code for rapid iteration
Three phases in one development session. Ship fast, ship focused.
What's next? Let's build Phase 4.