Files
ipx/ios/Sources/Server.swift
rays a581e3fc88 An iOS app: the page in a web view, with the phone's own player (#418)
web/src/native.ts (#45) has handed playback to a host since September, and
there was no host in this repo: the earlier app, rays/ipodderx-app, grew native
lists beside the page and is left behind. This one is the page and a player:
a WKWebView of ipx-web, AVPlayer behind the bridge with the audio session to
play on locked, now playing and the remote commands (15 back, 30 forward,
scrubbing), and positions and read written from the player's own clock, with
DeadAir's copy named so the server reads the position on its clock.

Built with XcodeGen on a Mac; nothing here compiles the Swift. Its icon is
web/logo.svg at 1024.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RqkrVDqdiQEMWAkNGQ2tYA
2026-11-07 21:50:43 +00:00

44 lines
1.9 KiB
Swift

import Foundation
/// Which ipx-web the app shows. Asked for on first launch; shaking the phone asks again.
/// `-ipx.server <url>` on the launch arguments overrides it for that run (UserDefaults' argument
/// domain), which is how a test points the app somewhere else.
enum Server {
private static let key = "ipx.server"
static let suggested = "https://ipodderx.sdf1.net"
static var url: URL? {
get { UserDefaults.standard.string(forKey: key).flatMap(normalise) }
set { UserDefaults.standard.set(newValue?.absoluteString, forKey: key) }
}
/// What someone typed, as an address: a bare host gets https. Plain http is allowed only on
/// your own network, which is all NSAllowsLocalNetworking lets through, so it is refused here
/// rather than failing later and looking like the server is down.
static func normalise(_ text: String) -> URL? {
var s = text.trimmingCharacters(in: .whitespacesAndNewlines)
if s.isEmpty { return nil }
if !s.contains("://") { s = "https://" + s }
guard let u = URL(string: s), let host = u.host, !host.isEmpty else { return nil }
switch u.scheme {
case "https": return u
case "http": return isLocal(host) ? u : nil
default: return nil
}
}
static func isLocal(_ host: String) -> Bool {
if host == "localhost" || host.hasSuffix(".local") { return true }
let p = host.split(separator: ".").compactMap { Int($0) }
guard p.count == 4 else { return false }
return p[0] == 10 || p[0] == 127 || (p[0] == 192 && p[1] == 168)
|| (p[0] == 172 && (16...31).contains(p[1]))
}
/// A path the page sent (`/media/42`), against the server.
static func resolve(_ path: String) -> URL? {
guard let base = url else { return nil }
return URL(string: path, relativeTo: base)?.absoluteURL
}
}