Universal Links: gescannter Einzelstück-Link öffnet die App
Server liefert /.well-known/apple-app-site-association (nur /i/* beansprucht; /l/* bleibt im Browser). iOS: Associated-Domains-Entitlement, Universal Link (https://…/i/<UID>) wird abgefangen (onContinueUserActivity) und öffnet das Einzelstück direkt. Hinweis: Damit das Signieren durchläuft, muss die Capability "Associated Domains" einmalig im Apple-Developer-Portal für die App-ID aktiv sein. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,8 @@ struct RootView: View {
|
||||
@EnvironmentObject private var session: Session
|
||||
@EnvironmentObject private var router: Router
|
||||
@EnvironmentObject private var display: DisplaySettings
|
||||
/// Über einen gescannten Universal Link geöffnetes Einzelstück.
|
||||
@State private var linkedItem: Item?
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
@@ -41,6 +43,14 @@ struct RootView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.task(id: router.openItemUid) {
|
||||
guard let uid = router.openItemUid else { return }
|
||||
linkedItem = try? await APIClient.shared.itemByUid(uid: uid)
|
||||
router.openItemUid = nil
|
||||
}
|
||||
.sheet(item: $linkedItem) { it in
|
||||
NavigationStack { ItemEditView(item: it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
10
ios/Sources/Vorrania.entitlements
Normal file
10
ios/Sources/Vorrania.entitlements
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:vorrania-ch.scarriffle.com</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -15,6 +15,8 @@ enum Route: String {
|
||||
|
||||
final class Router: ObservableObject {
|
||||
@Published var route: Route?
|
||||
/// Über einen Universal Link (…/i/<UID>) zu öffnendes Einzelstück.
|
||||
@Published var openItemUid: String?
|
||||
|
||||
func handle(shortcut type: String) {
|
||||
if type.hasSuffix("checkin") { route = .checkin }
|
||||
@@ -28,11 +30,18 @@ final class Router: ObservableObject {
|
||||
route = .expiring
|
||||
}
|
||||
|
||||
/// vorrania://checkin bzw. vorrania://checkout
|
||||
/// vorrania://checkin bzw. vorrania://checkout – oder ein Universal Link
|
||||
/// (https://…/i/<UID>), der das Einzelstück direkt öffnet.
|
||||
func handle(url: URL) {
|
||||
guard url.scheme == "vorrania" else { return }
|
||||
let target = (url.host ?? url.path.replacingOccurrences(of: "/", with: "")).lowercased()
|
||||
if let route = Route(rawValue: target) { self.route = route }
|
||||
if url.scheme == "vorrania" {
|
||||
let target = (url.host ?? url.path.replacingOccurrences(of: "/", with: "")).lowercased()
|
||||
if let route = Route(rawValue: target) { self.route = route }
|
||||
return
|
||||
}
|
||||
let parts = url.pathComponents // z.B. ["/", "i", "ABC123"]
|
||||
if let idx = parts.firstIndex(of: "i"), idx + 1 < parts.count {
|
||||
openItemUid = parts[idx + 1].uppercased()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +60,9 @@ struct VorraniaApp: App {
|
||||
.environmentObject(router)
|
||||
.environmentObject(display)
|
||||
.onOpenURL { router.handle(url: $0) }
|
||||
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in
|
||||
if let url = activity.webpageURL { router.handle(url: url) }
|
||||
}
|
||||
.onAppear {
|
||||
if let type = AppDelegate.pendingShortcut {
|
||||
router.handle(shortcut: type)
|
||||
|
||||
Reference in New Issue
Block a user