Everything native needs this first, and it is worth having whatever happens to the rest: a CarPlay browse tree is built from exactly these calls. API covers what a client reads and writes -- feeds, entries with the server's own paging, filtering, sorting and search, read and pinned, position, read-all, download, delete, fetch -- and APIModels is what ipx sends, with the server's field names kept so a type here can be checked against web.rs without translating first. It sends nothing of its own to authenticate: ipx decides who is asking by cookie and CookieBridge keeps HTTPCookieStorage in step, which is the same property that lets a plain <audio src> work. Events reads /api/events with URLSession's byte stream, so there is no new dependency. An event this build has never heard of decodes as .other rather than throwing, because a new one on the server must not stop the stream. It reconnects with a backoff: a deploy closes the stream cleanly and should be picked straight back up, a daemon that is down should not be hammered. Library is gone, folded into API. Two clients writing positions was one too many, and the page's forwarded beacon no longer carries a path: it asks the host to save, and the host uses its own clock, which is the only one not stale when the web view has been frozen in the background. The tests run against the browser suite's fixture daemon rather than canned JSON, because the shapes being decoded are the server's and a fixture would only prove it matches itself. Thirteen pass, including a live scan reported over SSE and the playback round trip, which still works after the move. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
108 lines
3.7 KiB
YAML
108 lines
3.7 KiB
YAML
# XcodeGen makes the .xcodeproj from this, so the project is something you can read in a diff
|
|
# rather than a pbxproj nobody can review.
|
|
#
|
|
# brew install xcodegen && xcodegen generate && open iPodderX.xcodeproj
|
|
name: iPodderX
|
|
options:
|
|
bundleIdPrefix: net.sdf1
|
|
deploymentTarget:
|
|
iOS: "16.0"
|
|
macOS: "13.0"
|
|
createIntermediateGroups: true
|
|
settings:
|
|
base:
|
|
SWIFT_VERSION: "5.9"
|
|
TARGETED_DEVICE_FAMILY: "1,2"
|
|
CODE_SIGN_STYLE: Automatic
|
|
targets:
|
|
iPodderX:
|
|
type: application
|
|
platform: iOS
|
|
# The same UIKit app on the Mac. Everything that matters -- Playback, Library, Bridge,
|
|
# CookieBridge -- never touches a view, so only the shell is platform-specific.
|
|
supportsMacCatalyst: true
|
|
sources:
|
|
- Sources
|
|
- Resources/Assets.xcassets
|
|
info:
|
|
path: Resources/Info.plist
|
|
properties:
|
|
CFBundleDisplayName: iPodderX
|
|
CFBundleShortVersionString: "0.1.0"
|
|
CFBundleVersion: "1"
|
|
# Playing on with the screen locked. This is a plist key, not a signed entitlement, so it
|
|
# works on a free personal team -- unlike CarPlay, which Apple has to grant.
|
|
UIBackgroundModes: [audio]
|
|
UILaunchScreen: {}
|
|
# Mac idiom rather than a scaled iPad. Without this a Catalyst app draws its iPad
|
|
# interface at 77%, which is why everything read small on the Mac.
|
|
UIDesignRequiresCompatibility: false
|
|
# iOS 27 will not run an app that has not adopted the scene lifecycle. A CarPlay app is a
|
|
# second scene beside this one, so its role goes here too when the entitlement exists.
|
|
UIApplicationSceneManifest:
|
|
UIApplicationSupportsMultipleScenes: false
|
|
UISceneConfigurations:
|
|
UIWindowSceneSessionRoleApplication:
|
|
- UISceneConfigurationName: Default Configuration
|
|
UISceneDelegateClassName: $(PRODUCT_MODULE_NAME).SceneDelegate
|
|
UIRequiresFullScreen: false
|
|
UISupportedInterfaceOrientations:
|
|
- UIInterfaceOrientationPortrait
|
|
- UIInterfaceOrientationLandscapeLeft
|
|
- UIInterfaceOrientationLandscapeRight
|
|
UIStatusBarStyle: UIStatusBarStyleLightContent
|
|
# Only needed to point the app at the LAN address, which is plain HTTP. The tunnel is
|
|
# HTTPS and needs none of this.
|
|
NSAppTransportSecurity:
|
|
NSAllowsLocalNetworking: true
|
|
settings:
|
|
base:
|
|
PRODUCT_BUNDLE_IDENTIFIER: net.sdf1.ipodderx
|
|
MARKETING_VERSION: "0.1.0"
|
|
# Set as build settings rather than xcodegen's supportsMacCatalyst:, which this version
|
|
# accepts and then writes nothing for -- the generated project had no SUPPORTS_MACCATALYST
|
|
# at all and the Mac destination simply did not exist.
|
|
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
|
|
SUPPORTS_MACCATALYST: "YES"
|
|
# Apple Silicon only. The default builds a universal binary, and the x86_64 half is
|
|
# weight nothing here will ever run.
|
|
ARCHS[sdk=macosx*]: arm64
|
|
DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER: "NO"
|
|
|
|
iPodderXTests:
|
|
type: bundle.unit-test
|
|
platform: iOS
|
|
sources:
|
|
- Tests
|
|
dependencies:
|
|
- target: iPodderX
|
|
settings:
|
|
base:
|
|
PRODUCT_BUNDLE_IDENTIFIER: net.sdf1.ipodderx.tests
|
|
|
|
iPodderXUITests:
|
|
type: bundle.ui-testing
|
|
platform: iOS
|
|
sources:
|
|
- UITests
|
|
dependencies:
|
|
- target: iPodderX
|
|
settings:
|
|
base:
|
|
PRODUCT_BUNDLE_IDENTIFIER: net.sdf1.ipodderx.uitests
|
|
|
|
schemes:
|
|
iPodderX:
|
|
build:
|
|
targets:
|
|
iPodderX: all
|
|
iPodderXTests: [test]
|
|
iPodderXUITests: [test]
|
|
run:
|
|
config: Debug
|
|
test:
|
|
config: Debug
|
|
targets:
|
|
- iPodderXTests
|
|
- iPodderXUITests
|