Add iOS app with CarPlay and Siri

SwiftUI port of the Android app under ios/, with CarPlay browse and
Now Playing, Siri play-media intents, and a build-ipa.sh script that
makes an unsigned IPA for sideloading. README gets an iOS section and
the vibecoded disclaimer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cdnutterandClaude Opus 5.5 committed 2026-10-20 07:15:47 -07:00
1 parent 45bc0eefe0
commit fdf9e970b2
45 files changed
+9822

No files matched your search

+48
View File
@@ -0,0 +1,48 @@
import Foundation
import UIKit
/// Album art: a memory cache over a big disk URLCache, so downloaded albums keep their covers
/// offline. Stands in for Coil on Android.
final class ArtLoader: @unchecked Sendable {
static let shared = ArtLoader()
private let memory = NSCache<NSURL, UIImage>()
private let session: URLSession
private let lock = NSLock()
private var inflight: [URL: Task<UIImage?, Never>] = [:]
init() {
let dir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0].appendingPathComponent("art", isDirectory: true)
let cfg = URLSessionConfiguration.default
cfg.urlCache = URLCache(memoryCapacity: 16 << 20, diskCapacity: 512 << 20, directory: dir)
cfg.requestCachePolicy = .returnCacheDataElseLoad
cfg.timeoutIntervalForRequest = 20
session = URLSession(configuration: cfg)
memory.totalCostLimit = 64 << 20
}
func cached(_ url: URL) -> UIImage? { memory.object(forKey: url as NSURL) }
func image(_ url: URL) async -> UIImage? {
if let img = cached(url) { return img }
if let t = lock.withLock({ inflight[url] }) { return await t.value }
let t = Task<UIImage?, Never> { [session] in
guard let (data, resp) = try? await session.data(from: url),
(resp as? HTTPURLResponse).map({ (200..<300).contains($0.statusCode) }) ?? true,
let img = UIImage(data: data) else { return nil }
let decoded = await img.byPreparingForDisplay() ?? img
return decoded
}
lock.withLock { inflight[url] = t }
let img = await t.value
lock.withLock { inflight[url] = nil }
if let img {
memory.setObject(img, forKey: url as NSURL, cost: Int(img.size.width * img.size.height * img.scale * img.scale * 4))
}
return img
}
func prefetch(_ url: URL) {
Task.detached(priority: .utility) { _ = await self.image(url) }
}
}