Conversation
iOS 18 floor, universal iPhone/iPad, SwiftUI. project.yml is the source of truth; the generated xcodeproj is ignored. CliampCore owns shared product identity so the app target proves package wiring.
macos-26 with Xcode 26.6 and XcodeGen 2.45.4, unsigned simulator build and tests. README, evidence record, and AGENTS routing updated for the second client.
Enforce warnings-as-errors in CliampCore via the manifest, correct the Clang key to GCC_TREAT_WARNINGS_AS_ERRORS, move the device matrix into the README, and refresh the stale tracker prose.
The design system first: all 32 roles plus dark, five hand-built palettes with the Android values, the Poppins/JetBrains Mono scale, the SVG path parser the icon set shares with Android, and the shared chrome and controls.
Station and its source contract, the twelve-channel seed, m3u parsing with a deterministic fallback id, and the shared clock/compact formatters.
Android already ships Poppins but the notices only named JetBrains Mono; the iOS bundle ships both, so the OFL text and the attribution travel with them.
The permanent chrome (mini player, bottom tabs, landscape rail), the Stations tab on the live cliamp playlist with the twelve-channel fallback, Now Playing over a real AVPlayer radio transport, and the Settings page with the six built-in themes switching live. Fonts are bundled and registered; persistence for these preferences lands with SET-01.
The signature meter: three layers anchored to the bottom, the shared attack/release and peak-fall math, the three geometry presets, and the log-spaced dB band folding.
A post-effects MTAudioProcessingTap hands vDSP the PCM AVPlayer is actually playing; a display-link model pushes it through MeterCore at frame rate. Player and mini player both wear the meter, the player is reordered to the Android layout (status above meta), and the preview hooks gain a play-without-opening variant.
Mirror discovery and failover with a pinned mirror, 60-item pages with the source HLS filter and url dedupe, stats/tags/countries, play reporting, and the custom-station validation and JSON store.
Top/trending/tag/country filters, paged loading that keeps rows and scroll while a query loads, votes, retry and exhausted states, the custom add form and remove menu, and persisted grid/list choices.
Directory stations still stream over plain HTTP, and the media-only ATS exception is not honoured for AVPlayerItem on iOS 26, so DEC-07 takes the blanket exception and QA-05 owns the review justification. The player now logs the underlying failure code.
Retain the processor for the tap's lifetime and release it in finalize (P1 dangling pointer); publish bands with a non-blocking preallocated handoff instead of a blocking locked copy; average both channels in the interleaved layout; reject non-float32 PCM; correct the vDSP real-FFT scale to 2/N; stop the display links when spectrum is off.
Share one discovery sweep so reentrant callers cannot clobber mirror state; retry fetch and decode together; count server records rather than displayed rows for pagination offsets; start the first directory page before the other startup fetches so a user filter cannot be overwritten; paginate in grid mode too.
og:image / apple-touch-icon / favicon discovery with a capped scrape, scaled ImageIO decodes, a week-long disk cache and a 60-second miss backoff. Rows, tiles, the mini player and the player plate use it; cliamp channels keep the generated plate, and the player follows the source's fill-versus-letterbox rule.
A second Icy-MetaData connection feeds a tested block parser, so the now-playing line shows the real artist/title like Android; the meta block is left-aligned and the transport reports buffered seconds. The queue count stays 0 until the queue lands.
Bounded delegate reads stop at the scrape/image caps instead of buffering first; cancellation no longer publishes stale art or starts the miss backoff; disk writes are atomic; grid tiles load full-size art; and known cover URLs are honoured. RAD-09 is marked W with the local-embedded gap owned by LIB-01.
The hook lets a capture or UI test play an arbitrary stream URL, and the evidence record for the first shell/screens slice was never committed.
The generated Omarchy theme map joins the resolver, Settings lists all 28 rows, and AppState writes every preference to defaults so a relaunch keeps the theme, grid choices and toggles. A preview hook selects a palette for captures.
The icon is generated from the same geometry as the Android adaptive icon: oxide bevel ground with the six-bar mark in cream, via a checked in generator. The launch screen uses the flat oxide ground colour, so the first frame is the app's own background.
ptgamr
marked this pull request as draft
September 17, 2026 07:48
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Native iOS client for cliamp
A Swift 6 + SwiftUI port of the Android client: iOS 18 minimum, universal iPhone/iPad, portrait and landscape. The project is one XcodeGen manifest plus two local Swift packages —
CliampCore(product logic, IO, parsers, 173 tests) andCliampDesign(tokens and components) — with an unsigned simulator CI job.Behaviour is ported against the Android source as the reference (never edited from an iOS task) and
docs/design.mdas the design source of truth.docs/ios-parity.mdowns scope, task IDs and evidence; this description is a summary of that tracker.How to build and test
CI runs the same unsigned build/test on
macos-26with the pinned toolchain (.github/workflows/ios.yml).Live SFTP tests are opt-in against a real server (
CLIAMP_SFTP_LIVE=1 swift test --package-path CliampCore --filter SftpLiveTests); the assertions adapt to any server viaCLIAMP_SFTP_*env vars, andCLIAMP_ART_FILE=/path/track.mp3exercises embedded-artwork extraction against one real file. Details inios/README.md.Achieved
Foundation and CI
FND-01— XcodeGen project, iOS 18 floor, universal iPhone/iPad, Swift 6, local packages, unsigned CI job, documented commands.DEC-07— HTTP transport decided and recorded (blanket ATS exception; the media-only key does not coverAVPlayerItemon iOS 26).Radio — phase 2, 12/12 implemented
RAD-01cliamp channels from the playlist endpoint with fallback.RAD-02Radio Browser directory: 60-item pages, top-voted/trending, tag/country filters, counts, retry, exhausted state, mirror failover, dedupe, click reporting, HLS filter.RAD-03independent list/grid choices per section, persisted.RAD-04add/remove custom stations with the same URL/name validation as Android.RAD-05M3U/PLS resolution with bounded redirect handling and fatal malformed media.RAD-06full player surfaces (source line, artwork, ICY titles, format details, speed).RAD-07one playback state behind mini player and full player; transport, history and fallback ring.RAD-08streaming rule, seek/elapsed/remaining.RAD-09artwork pipeline (branding discovery, bounded reads, atomic disk cache, embedded tags, never blocking audio).RAD-10reconnect: 1/2/4/8/15/30 s backoff, 20 s stall watchdog, network-return resume.RAD-11background and locked-device playback, lock-screen metadata and transport.RAD-12last station, favourites and history; cold launch is silent by default.Podcasts — phase 4, 7/8 (built ahead on the radio player's source lists)
POD-01iTunes directory browse/search with country and category selection.POD-02subscribe/unsubscribe, cached feeds, show pages with description and episodes.POD-03episodes in displayed order; show switches replace the source, end-of-item advances.POD-04progress by canonical media URL at the 5 s cadence, resume on reopen.POD-05manual downloads with progress, cancellation, failure states, cellular gate.POD-06offline playback prefers the downloaded file without changing history.POD-07auto-download opt-in keeps the latest three full unplayed episodes with retention rules.POD-08downloads smart list and subscribed-episode search — lands withLIB-02/SRC-02.Library — phase 3,
LIB-01–LIB-04DEC-02local library decided: managed Documents folder with Files/Finder sharing; snapshot cache, metadata, folder covers, grouping, four sorts, identity/removal handling.LIB-01local library scanning (AVFoundation metadata, folder grouping,local:<path>identity, removed files drop out).LIB-02smart lists: recently played, downloads, favorites (with all/local/stations/podcasts scopes), local songs with folder filter and sort chips; counts, empty states, active indicators.LIB-03playlists: create/rename/pin/unpin/delete, mixed-source members with snapshots, add-songs picker (local/stations/podcasts).LIB-04playlist membership/sorts/pins persisted; per-list sort preferences.LIB-05song info, favourite action and play statistics — next.Providers — phase 5, first provider (SSH/SFTP) + shared account layer
PRV-01spec-driven wizard: conditional fields, pickers, test-then-save, edit/remove, multiple accounts.PRV-02Keychain secrets (split out of the JSON store, deleted with the account), opaquecliamp-sftp://playback references resolved at play time.PRV-03browse: albums → artist albums → album tracks, a-z/newest sort, filter, rescan, empty/error states, all-provider songs pane, album queue identity, embedded cover art per track.PRV-11SSH setup: password, pasted ed25519 key and passphrase, Tailscalenone; TOFU host-key pinning with a per-host fingerprint; clear errors for refused credentials, bad keys and unreachable hosts.PRV-12progressive filename-derived index with scan status and rescan, commit-on-success (failed/cancelled walks keep the old index), and streaming/seeking over SFTP through anAVAssetResourceLoaderwith range reads — playback and indexing share one connection without disturbing each other.PRV-04–PRV-10— Navidrome, Subsonic, Jellyfin, Emby, Plex, Audiobookshelf, Lyrion.Search, settings, audio, design system
SRC-01fuzzy search page with all six chips, source labels, highlighting, resident-first ranking and stale-query protection (local/tags/providers scopes await their stores).SRC-02shows, cached subscribed episodes, favourites and history search with per-list queue identity.SRC-03search keeps its query and scope, resets the pager stack on show open instead of overwriting browse state.VIS-01–VIS-07palette roles and 27 themes, bundled Poppins/JetBrains Mono, rows/hairlines/headers/chips/toggles/sliders, mechanical keys, the three-tab shell with swipe paging, player/settings overlays, immediate theme persistence.SET-01every setting exposed and persisted with immediate effect where wired (auto-resume, cellular, mono, buffer and resume-local behaviour land withAUD).AUD-01real playback-driven spectrum (post-effectsMTAudioProcessingTap+ vDSP FFT) in player, mini player and the meter.DAT-01relaunch restores favourites, history, custom stations, playlists, podcasts, the local-library snapshot and the SFTP index; no signed provider URLs are persisted.Evidence
CliampCoretests, 10 app tests, plus 10 opt-in live SFTP tests run against both an asyncssh fixture and a Docker OpenSSH server with a real 125-track library.What's left
Grouped by phase, mirroring
docs/ios-parity.md.Phase 3 — queue and leftover library/search
QUE-01–QUE-04— the ordered upcoming sequence with explicit source identity, transport consumption, drag reorder/swipe removal/Clear, shuffle with base-order restore, 130-track boundary behaviour.LIB-05— song info, favourite action, play statistics, resume-local-songs behaviour.POD-08— downloads smart list and subscribed-episode search reflecting changes immediately.SRC-01/SRC-02— fill thelocal,tagsandprovidersscopes and verify the blank-query/error/loading states.Phase 5 — remaining providers
PRV-04Navidrome ·PRV-05generic Subsonic ·PRV-06Jellyfin ·PRV-07Emby ·PRV-08Plex ·PRV-09Audiobookshelf ·PRV-10Lyrion — each with its auth branches, browse, playback headers, artwork and seek verified against a real server.PRV-03starred results and provider artwork for the API providers.Phase 6 — audio character, settings, system surfaces
AUD-02seven-band EQ with all 17 presets ·AUD-03live mono/speed and buffer verification ·SET-02counts, history purge, directory stats, about surface ·SCB-01ListenBrainz wizard ·SYS-01–SYS-03widgets, quick entry and share intake.Foundation gaps
FND-02Android capture set ·FND-03codec/container matrix and locked-device proof beyond the first ICY MP3 spike ·FND-04device proofs for Keychain, file access and LAN ·FND-05stable-ID fixtures including the 130-item queue and#80445566.Decisions to close
DEC-01audio architecture and format matrix ·DEC-03widget/control availability ·DEC-04cellular policy ·DEC-05LAN permission and suspended/terminated behaviour ·DEC-02follow-ups if synced libraries or external folders are wanted later ·DEC-06ongoing reference-drift checks.Verification and acceptance
QA-01–QA-05and theJ-01–J-14journey matrix on the declared devices;QA-05also owns the App Review note for the ATS exception.Known limitations (recorded, not blockers)
ssh-rsa, which modern servers reject.local,tagsandprovidersreturn nothing until their stores land (phases 3/5).Review notes
ios/CliampCore/Sources/CliampCore/(logic and parsers),ios/Cliamp/Sources/RadioPlayer.swift,ios/Cliamp/Sources/Screens/,ios/Cliamp/Sources/Providers/.docs/ios-parity.md) is the durable artifact: task rows, phase summary and the activity log with per-commit evidence.android/or cliamp (TUI) files are touched.ios/Cliamp.xcodeprojis generated and ignored; editios/project.yml.