Phase 2 startet. Server-AASA + Entitlements waren schon live, jetzt das Native-Side-Routing. Tap auf pageta.com/article/X in Safari/Mail öffnet die App und präsentiert den Reader als Sheet (kontextfrei — bleibt egal in welchem Tab der User war). - Sources/Core/URLRouter.swift: `PagetaRoute.parse(_:)` als pure function. Akzeptiert https://pageta.com/article/{id} (AASA-matched) und pageta://article/{id} (eigenes Scheme, für ShareExt-π-6 und Widget-π-7 vorbereitet). Bewusst ohne pageta.mana.how — Lead-Domain ist pageta.com seit 2026-05-18. - PagetaNativeApp: - `.onContinueUserActivity(NSUserActivityTypeBrowsingWeb)` für Universal Links - `.onOpenURL` für pageta:// scheme - `.sheet(item: $presentedRoute)` → NavigationStack { ArticleDetailView } mit „Fertig"-Cancel-Button. Sheet-State über separate PresentedRoute- Enum (Identifiable-Wrapper um PagetaRoute, damit der Pure-Type SwiftUI-frei bleibt). - OSLog-Trace mit URL-Quelle (universal-link | scheme) + matched ID. 7 neue URL-Tests (20/20 total grün): Pageta-com-Match, Scheme-Match, Case-Insensitive, Reject pageta.mana.how (Drift-Guard), Reject foreign host, Reject empty id, Reject unknown path. Real-Device-Test braucht Apple-Dev-Portal-Capability "Associated Domains" + Re-Provisioning — Simulator macht Universal Links nur eingeschränkt mit. pageta:// scheme funktioniert auch im Simulator direkt (xcrun simctl openurl). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
41 lines
1.4 KiB
Swift
41 lines
1.4 KiB
Swift
import Foundation
|
|
|
|
/// Pure URL-Parser für die App-Routing-Entscheidungen.
|
|
///
|
|
/// Akzeptierte URL-Formen:
|
|
/// - `https://pageta.com/article/{id}` (Universal Link, AASA-matched)
|
|
/// - `pageta://article/{id}` (eigenes URL-Scheme, ShareExt + Widget-Deep-Links)
|
|
///
|
|
/// Andere Hosts (z.B. pageta.mana.how) werden bewusst NICHT geroutet —
|
|
/// `pageta.com` ist seit 2026-05-18 die Lead-Domain. Wenn die alte
|
|
/// mana.how-Domain mal in Universal-Links auftaucht, hier ergänzen.
|
|
enum PagetaRoute: Equatable, Sendable {
|
|
case article(id: String)
|
|
|
|
static func parse(_ url: URL) -> PagetaRoute? {
|
|
guard let scheme = url.scheme?.lowercased() else { return nil }
|
|
|
|
switch scheme {
|
|
case "https":
|
|
guard url.host?.lowercased() == "pageta.com" else { return nil }
|
|
return parsePath(url.pathComponents.dropFirst())
|
|
case "pageta":
|
|
// Bei custom-scheme ist `host` der erste Pfad-Bestandteil:
|
|
// `pageta://article/abc` → host="article", path="/abc"
|
|
var parts: [String] = []
|
|
if let host = url.host { parts.append(host) }
|
|
parts.append(contentsOf: url.pathComponents.filter { $0 != "/" })
|
|
return parsePath(parts[...])
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
private static func parsePath(_ parts: ArraySlice<String>) -> PagetaRoute? {
|
|
let segments = Array(parts)
|
|
guard segments.count >= 2, segments[0] == "article" else { return nil }
|
|
let id = segments[1]
|
|
guard !id.isEmpty else { return nil }
|
|
return .article(id: id)
|
|
}
|
|
}
|