import SwiftUI import AppIntents import Bedrock @main struct BusinessCardClipApp: App { @State private var recordName: String? @State private var launchErrorMessage: String? #if DEBUG @State private var debugState: ClipDebugState? #endif var body: some Scene { WindowGroup { Group { #if DEBUG if let debugState { ClipDebugHarnessView(initialState: debugState) } else if let recordName { ClipRootView(recordName: recordName) } else if let launchErrorMessage { ClipErrorView(message: launchErrorMessage) { self.launchErrorMessage = nil } } else { ClipLoadingView() } #else if let recordName { ClipRootView(recordName: recordName) } else if let launchErrorMessage { ClipErrorView(message: launchErrorMessage) { self.launchErrorMessage = nil } } else { ClipLoadingView() } #endif } .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in Design.debugLog("Clip: onContinueUserActivity fired, webpageURL=\(activity.webpageURL?.absoluteString ?? "nil")") handleUserActivity(activity) } .onOpenURL { url in Design.debugLog("Clip: onOpenURL fired, url=\(url.absoluteString)") handleURL(url) } .task { Design.debugLog("Clip: .task running - checking launch sources") #if DEBUG if let dbg = parseDebugStateArgument() { Design.debugLog("Clip: debug state from args: \(dbg.rawValue)") debugState = dbg return } #endif if let envURL = ProcessInfo.processInfo.environment["_XCAppClipURL"], let url = URL(string: envURL) { Design.debugLog("Clip: _XCAppClipURL from env: \(envURL)") handleURL(url) } else { Design.debugLog("Clip: no _XCAppClipURL in env, no debug args. recordName=\(recordName ?? "nil")") } } } } private func handleUserActivity(_ activity: NSUserActivity) { Design.debugLog("Clip: handleUserActivity, webpageURL=\(activity.webpageURL?.absoluteString ?? "nil")") guard let url = activity.webpageURL else { Design.debugLog("Clip: handleUserActivity - no webpageURL, skipping") return } handleURL(url) } private func handleURL(_ url: URL) { Design.debugLog("Clip: handleURL \(url.absoluteString)") guard let id = extractRecordName(from: url) else { Design.debugLog("Clip: extractRecordName failed for \(url.absoluteString)") launchErrorMessage = ClipError.invalidRecord.localizedDescription return } Design.debugLog("Clip: extracted recordName=\(id), setting recordName") launchErrorMessage = nil recordName = id } private func extractRecordName(from url: URL) -> String? { if let components = URLComponents(url: url, resolvingAgainstBaseURL: true), let id = components.queryItems? .first(where: { $0.name == ClipDesign.URL.recordQueryName })? .value, !id.isEmpty { return id } // Fallback for path-based links (e.g. /clip/{recordName}). let candidate = url.lastPathComponent.trimmingCharacters(in: .whitespacesAndNewlines) guard !candidate.isEmpty, candidate != "/" else { return nil } return candidate } #if DEBUG private func parseDebugStateArgument() -> ClipDebugState? { let prefix = "--clip-debug=" guard let argument = ProcessInfo.processInfo.arguments.first(where: { $0.hasPrefix(prefix) }) else { return nil } let value = String(argument.dropFirst(prefix.count)) return ClipDebugState(rawValue: value) } #endif }