Own product

Mira

How does a film remain the same when the network, address or quality changes?

Mira iOS home screen highlights one production above rows of library content.
iOS development state in a simulator. Film artwork belongs to the connected test library.

Mira is a native Jellyfin client for Apple and Android platforms. The main product work sits in playback, routing and system integration.

Field
Media playback for Jellyfin
Period
2026
Status
In development
Platforms
iPhone, iPad, Android, Android TV, Fire TV, Android Auto
Services
Native iOS app, Native Android app, Media playback, Device integration

Context

A personal media server may use a local address at home and a remote address on the road. Network transitions should not restart playback without need.

Responsibility

Product design, separate native clients, shared architecture contracts, network and playback coordination, TV and Auto surfaces, and device readiness.

Outcome

An Android client prepared for device testing and an ongoing iOS build with native playback, library, downloads, casting and a synchronised Jam queue.

Mira iOS detail page for Big Buck Bunny with playback action and metadata.
A second capture from the same iOS development state; it does not evidence the current Android playback path.

Two native apps are a deliberate cost decision

Mira does not use one cross-platform client. iPhone and iPad are built with Swift, SwiftUI and AVFoundation; Android uses Kotlin, Compose and Media3. The difficult parts already sit close to the operating system: decoder capability, picture in picture, AirPlay or Cast, background downloads, remote controls and network transitions.

The cost is real. Shared product rules must be implemented twice and checked for parity. Modules and type names therefore follow a common architecture contract even though code remains separate. Native is not a quality badge here; it is a decision to pay for two implementations because the product depends deeply on each platform.

A server session has a route and an epoch

The same Jellyfin library may be reachable through a local address at home and a remote address elsewhere. Mira keeps one endpoint resolver per server session. It publishes the active route, an epoch, probe status and diagnostics together as an atomic state. HTTP reads that state when sending; playback observes changes.

The epoch makes a network world explicit. A new route is more than a different string: it may affect requests, cached resources and media URLs. Keeping the decision central prevents UI, library and player code from developing slightly different fallback rules.

Change the path, not the film

Older project documentation still describes rebuilding the player after a route change. Current Android code goes further. A custom Media3 data source remembers the byte offset of the stream. When the reachable base address changes, it rebases the URL and reopens at the same position, preserving the media session where the server exposes equivalent bytes.

A route change is kept distinct from a quality change. The latter can require a new transcode or representation and a controlled player transition. The former is first a transport problem.

This has boundaries: range requests, authentication and media identity must remain valid, and transitions can still fail. The claim is a concrete continuation mechanism for a common case, not invisible switching under every network condition.

Shared listening begins with a shared clock

Jam distributes one queue across devices. A server timestamp is not enough because clocks differ and messages take time. The client estimates clock offset in an NTP-like exchange and schedules playback relative to that offset. Each device waits its own measured difference rather than assuming identical clocks.

WebSocket messages carry queue and state. Real multi-device tests across different networks are still required for a release claim. The architecture makes latency measurable; it does not pretend latency vanished.

What this demonstrates for client work

Mira shows native product development at a hard platform boundary. CIDIX can structure separate clients around one product model without forcing system integration into an undersized common layer.

For streaming, communications or sensor sessions, the transferable question is what must survive an infrastructure change. Mira makes route, epoch and media session explicit. A new engagement would define those invariants first, then choose native, cross-platform or a hybrid based on actual platform dependencies.

Technology and what it is for

Swift 6
iOS client
SwiftUI
Interface on Apple devices
AVFoundation
Playback on Apple devices
Kotlin 2.3
Android client
Jetpack Compose
Interface on Android, TV and Auto
Media3
Playback and data source on Android
WebSocket
Synchronised listening
Jellyfin API
Media server interface