Files
byre/docs/iphone.md
T

4.4 KiB

byre for iPhone

The iPhone app is the same Byre target as the Mac app, built for iOS: platform differences are #if os(macOS) / #if os(iOS) blocks, with the small shared aliases (PlatformImage, PlatformColor, PlatformFont, PlatformView, touchOnly, deviceKind) in mac/Sources/Byre/Platform.swift. Only the layout is the phone's own (Phone.swift): a tab bar with Lead, Tasks and More. Tasks lists every project's active tasks with the Mac's own task rows; a task opens pushed, with the Mac's task bar and tab strip on top and the tab's view below in place of the Mac's attached sheet. Zoomed images and diagrams cover the screen. More holds Notes, Workflows, Status and Settings. Every action that focuses a task or project (a row, a link, a notification, the switcher) bumps Store.reveals, which brings the Tasks tab forward with the task open; the server's own focus changes don't.

The phone holds the client connection (Remote) like a Mac, so agents can show it pages and files and drive its Page view (show_page, show_file, drive_page); it answers every other client request with an error. It sends Byre-Device: iPhone (Macs send Mac), and the server signs commits and runs byre client-exec without --client only on Macs.

Left out on the phone: the Server tab and running byre locally, the shell, file, hands and signing client requests, the Sparkle updater, and keyboard shortcuts. Notifications come from the app's own event stream, so they only show while the app is open; push notifications would need APNs on the server.

Building

mac/phone.sh builds the app with xcodebuild (a Swift package can't be built for iOS by swift build) in the same shared directory and under the same lock as mac/bundle.sh, assembles Byre.app itself (icon through actool, fonts, Info.plist from mac/phone/Info.plist plus the version, commit and DT* keys App Store Connect checks), signs it ad hoc with mac/phone/byre.entitlements, and wraps it in mac/.build/phone/iphoneos/Byre.xcarchive. Release only, like the Mac app. xcodebuild -exportArchive -archivePath <archive> -exportOptionsPlist mac/phone/export.plist -exportPath <dir> -allowProvisioningUpdates re-signs it for the App Store and uploads it to TestFlight, using the Apple account Xcode is signed into: Xcode makes the distribution certificate and profile itself, so none are kept in the repo. Uploading needs the App ID run.fc.byre.ios (iCloud with the container iCloud.run.fc.byre.mac, and Push Notifications) and its app record in App Store Connect; neither can be made through Apple's API. The version is the last release tag's (App Store Connect takes only numbers) and the build number the UTC build time, YYYYMMDD.HHMMSS: App Store Connect refuses a build number that isn't higher than every earlier upload, and a commit count can go down when a branch with many commits uploads before its squash merge lands on main.

mac/phone.sh --simulator builds an ad-hoc-signed app for the iOS simulator instead, at mac/.build/phone/iphonesimulator/Byre.app; install it with xcrun simctl install <device> <app> and start it with xcrun simctl launch <device> run.fc.byre.ios. The simulator ignores entitlements given to codesign and refuses to launch an ad-hoc app signed with restricted ones, so the script links mac/phone/byre.entitlements into the binary's __TEXT,__entitlements section, as Xcode does; without the iCloud entitlement CloudKit raises at launch. Its node signs in to Tailscale like any other: xcrun simctl launch --console-pty prints libtailscale's sign-in URL, which changes on every launch.

The simulator's window (Xcode's Device Hub) lists the app's own accessibility elements, so hands' ax_tree and act press drive the app without a mouse. Hands' mouse steps (click, drag, scroll) refuse that window, because Device Hub reports no process id; a gesture such as dragging the keyboard away needs mouse events posted with CGEvent instead. xcrun simctl io booted screenshot --display LCD <file> captures the app at full resolution.

Platform facts

  • CloudKit on iOS requires the remote-notification background mode in Info.plist, even though the app sets up no push of its own.
  • ProcessInfo.hostName is the Mac's name in the simulator and meaningless on a phone, so the phone's tailnet node and Byre-Host are named iPhone.
  • SwiftTerm's iOS TerminalViewDelegate requires requestOpenLink, which its macOS one provides a default for.