BusinessCard/BusinessCardClip/BusinessCardClipApp.swift

95 lines
3.0 KiB
Swift

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
}