Read App Intents Documentation

Nucleic-Session: 56C5F781-4444-466B-91CE-A7734A8E117F
Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
2026-07-06 14:41:56 -07:00
co-authored by Nucleic
parent ef8aa79df4
commit 4434bb09c1
@@ -1225,6 +1225,59 @@ final class RemoteStore: ObservableObject {
func setSessionAutoShip(_ id: SessionID, _ autoShip: Bool) { send(.setSessionAutoShip(id, autoShip)) } func setSessionAutoShip(_ id: SessionID, _ autoShip: Bool) { send(.setSessionAutoShip(id, autoShip)) }
func setSessionShipBranch(_ id: SessionID, _ branch: String?) { send(.setSessionShipBranch(id, branch)) } func setSessionShipBranch(_ id: SessionID, _ branch: String?) { send(.setSessionShipBranch(id, branch)) }
// MARK: - App Intents support
/// Bring networking online and dial the paired host(s) for an App Intent that runs in a *cold*
/// background process (Siri / Shortcuts / a widget or Live Activity button), where the SwiftUI
/// scene never mounts and `onAppear` never fires. Mirrors the push handler's silent-launch
/// bootstrap (`startNetworkingIfNeeded` + `reconnect`). Idempotent; a no-op in demo mode.
func bootstrapForIntent() {
guard !demoMode else { return }
startNetworkingIfNeeded()
if isPaired { reconnect() }
}
/// Whether any paired Mac currently has a live channel.
var hasLiveConnection: Bool { connections.values.contains { $0.connectivity.isLive } }
/// Await a live connection to any paired Mac, up to `timeout` seconds — an intent must act over a
/// *live* channel or fail clean (UX_IOS §6, §3.1). Brings networking up first if it's cold, then
/// polls until a link comes up or the deadline passes. Returns whether a link is live.
func awaitLiveConnection(timeout: TimeInterval = 6) async -> Bool {
if demoMode || hasLiveConnection { return true }
bootstrapForIntent()
let deadline = Date().addingTimeInterval(timeout)
while Date() < deadline {
try? await Task.sleep(for: .milliseconds(200))
if hasLiveConnection { return true }
}
return hasLiveConnection
}
/// Resolve an approval by id from an App Intent with a full `Decision`. Like
/// `respondFromNotification`, it prefers the owning Mac, else broadcasts to every live Mac (the
/// owner resolves; the rest see an unknown / `alreadyResolved` id and no-op), and queues briefly
/// on a dropped link so a decision made just as the socket blips still lands. `sessionID` (when
/// the surface knows it) routes directly. Returns whether it went out over a live channel now.
@discardableResult
func respondToApproval(id: ApprovalID, sessionID: SessionID?, decision: Decision) -> Bool {
if demoMode { demoHandle(.approvalRespond(id, decision)); return true }
if let sessionID, let conn = connection(owningSession: sessionID), conn.connectivity.isLive {
conn.send(.approvalRespond(id, decision))
openApprovals.removeAll { $0.id == id }
return true
}
let live = connections.values.filter { $0.connectivity.isLive }
if !live.isEmpty {
for conn in live { conn.send(.approvalRespond(id, decision)) }
openApprovals.removeAll { $0.id == id }
return true
}
pendingNotificationDecision = (id, decision, Date())
reconnect()
return false
}
// MARK: - Plumbing // MARK: - Plumbing
/// Route an intent to the Mac that owns its target (mesh P3). Sessions/projects/to-dos are shown /// Route an intent to the Mac that owns its target (mesh P3). Sessions/projects/to-dos are shown