Local Music - 離線的 iPhone 音樂播放器
era: agent
created_at: 2026/10/12 03:00:00

一個完全離線的 iPhone 私人音樂播放器,不需要帳號、沒有雲端、沒有追蹤、沒有任何第三方 SDK。
為什麼會有這個 App
我的手機是 iPhone 8 Plus,而現在的 iTunes 已經不支援它了,所以沒辦法再把新的音樂同步進去。
其實從以前就覺得用 iTunes 同步這件事很麻煩,只是一直將就著用,這次算是壓倒駱駝的最後一根稻草。
把自己的音樂放進自己的手機然後單純地播放它,這件事困難得沒有道理:不是要註冊帳號、訂閱服務,就是要裝一套桌面同步程式,或是把檔案上傳到別人的伺服器。
剛好 AI 變得這麼厲害,就做了一個都不需要的。
刻意的 Vibe Coding
這個專案和其他的不太一樣,Swift 和 iOS 都不是我熟悉的領域,所以這是一份純 Vibe Coding 的專案:幾乎每一行都是 AI Agent 依照分階段的 prompt 寫出來的,我負責指揮,以及拿著實機測試。
而且我是在 Windows 上開發的:Xcode 專案檔由 XcodeGen 產生,編譯和測試都丟給 CI 上的 macOS runner,產出未簽署的 IPA 再用 AltStore 側載到手機。
在這個專案裡,我把關的方式不是一行一行讀 code,而是測試:每個階段先定好要驗證什麼,做完之後裝到手機上實際操作過才算數。
只靠 CI 不夠
CI 能確保的事情是:程式編譯得過、單元測試驗證的邏輯是對的。
但 CI 的 runner 上只有 iOS 18 以上的模擬器,而我的手機是 iOS 16,版本根本對不上,所以 CI 綠燈沒辦法告訴我「在我的手機上是好的」。
版面就是實際踩到的例子:同一份 build,CI 是綠的,裝到手機上卻有好幾個頁面的清單底部沒有留出空間給迷你播放器,因為 SwiftUI 的某個排版行為在 iOS 16 和新版上並不一樣。
至於背景播放、鎖定畫面控制、Wi-Fi 傳輸這些,模擬器本來就測不出來,一樣只能拿手機試。
所以最後的做法是:
- 每一項裝置上的行為都在實體
iPhone 8 Plus上手動驗證 - 沒有真的測過的項目就標記為
NOT RUN,不會因為CI綠了就當成「可以動」 prompt、決策紀錄、測試報告全部都放在repo裡
功能
- 從「檔案」、
AirDrop匯入,或是透過Wi-Fi傳輸:App會在區域網路上開一個上傳頁面,從電腦把檔案拖進去就好 - 背景播放、鎖定畫面控制、睡眠計時器
- 播放速度與音高獨立調整、
10段等化器 - 播放清單、本機歌詞(
.lrc) - 單一檔案的備份與還原,還原前會先預覽
只使用 Apple 官方的框架,沒有任何第三方相依套件。
左邊是播放畫面,右邊是 Wi-Fi 傳輸:App 自己當伺服器,網址帶一組一次性的 token,只在這個畫面停留時才收檔案,閒置幾分鐘就自動關掉。
速度和音高是兩個獨立的滑桿:一般播放器把速度調快,聲音的音調也會跟著變高;這裡調速度不會動到音調,想變調再另外調音高。等化器有十組預設集,也可以自己存一組。