import SwiftUI import AppIntents @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 handleUserActivity(activity) } .onOpenURL { url in handleURL(url) } .task { #if DEBUG debugState = parseDebugStateArgument() #endif } } } private func handleUserActivity(_ activity: NSUserActivity) { guard let url = activity.webpageURL else { return } handleURL(url) } private func handleURL(_ url: URL) { guard let id = extractRecordName(from: url) else { launchErrorMessage = ClipError.invalidRecord.localizedDescription return } 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 }