learning_ai_common_plat/packages/swift-platform-sdk/Sources/BLDeepLinkRouter.swift

183 lines
5.1 KiB
Swift

import Foundation
import os
/**
* Deep Link Router Swift
* Handles routing from push notification deep links to app screens
*/
public struct BLDeepLinkRoute {
public let screen: String
public let params: [String: String]
public init(screen: String, params: [String: String] = [:]) {
self.screen = screen
self.params = params
}
}
public typealias BLDeepLinkHandler = (BLDeepLinkRoute) -> Void
/**
* Deep Link Router class
*/
@available(iOS 15.0, *)
public class BLDeepLinkRouter {
private var handlers: [String: BLDeepLinkHandler] = [:]
private var fallbackHandler: BLDeepLinkHandler?
public init() {}
/**
* Register a handler for a specific screen
*/
public func register(screen: String, handler: @escaping BLDeepLinkHandler) {
handlers[screen] = handler
}
/**
* Set a fallback handler for unregistered screens
*/
public func setFallback(handler: @escaping BLDeepLinkHandler) {
fallbackHandler = handler
}
/**
* Parse a deep link URL and extract route
*/
public func parseDeepLink(_ urlString: String) -> BLDeepLinkRoute? {
guard let url = URL(string: urlString) else {
return nil
}
// Handle app-specific URLs: myapp://screen/params
if url.scheme != "http" && url.scheme != "https" {
let pathComponents = url.pathComponents.filter { $0 != "/" && !$0.isEmpty }
let screen = pathComponents.first ?? "home"
var params: [String: String] = [:]
if let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems {
for item in queryItems {
if let value = item.value {
params[item.name] = value
}
}
}
return BLDeepLinkRoute(screen: screen, params: params)
}
// Handle web URLs with deep link params
if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
let dlParam = components.queryItems?.first(where: { $0.name == "dl" })?.value {
return parseDeepLink(dlParam)
}
// Handle path-based routing: /screen/params
let pathComponents = url.pathComponents.filter { $0 != "/" && !$0.isEmpty }
if !pathComponents.isEmpty {
let screen = pathComponents[0]
var params: [String: String] = [:]
if let queryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems {
for item in queryItems {
if let value = item.value {
params[item.name] = value
}
}
}
return BLDeepLinkRoute(screen: screen, params: params)
}
return nil
}
/**
* Handle a deep link route
*/
@discardableResult
public func handle(_ route: BLDeepLinkRoute) -> Bool {
if let handler = handlers[route.screen] {
handler(route)
return true
}
if let fallback = fallbackHandler {
fallback(route)
return true
}
Logger.deepLink.warning("No handler for screen: \(route.screen)")
return false
}
/**
* Process a deep link URL end-to-end
*/
@discardableResult
public func process(_ urlString: String) -> Bool {
guard let route = parseDeepLink(urlString) else {
Logger.deepLink.warning("Failed to parse deep link: \(urlString)")
return false
}
return handle(route)
}
}
/**
* Create a broadcast deep link URL
*/
public func createBroadcastDeepLink(
baseURL: String,
screen: String,
params: [String: String] = [:],
broadcastId: String? = nil
) -> String? {
guard var components = URLComponents(string: baseURL) else {
return nil
}
components.path = "/\(screen)"
var queryItems: [URLQueryItem] = params.map { URLQueryItem(name: $0.key, value: $0.value) }
if let broadcastId = broadcastId {
queryItems.append(URLQueryItem(name: "broadcastId", value: broadcastId))
}
if !queryItems.isEmpty {
components.queryItems = queryItems
}
return components.string
}
/**
* Common deep link screens
*/
public struct BLDeepLinkScreens {
// Broadcasts
public static let broadcast = "broadcast"
public static let announcements = "announcements"
// Surveys
public static let survey = "survey"
public static let surveyList = "surveys"
// Product-specific
public static let settings = "settings"
public static let profile = "profile"
public static let upgrade = "upgrade"
public static let support = "support"
// Fallback
public static let home = "home"
}
// Logger extension
@available(iOS 15.0, *)
extension Logger {
static let deepLink = Logger(subsystem: "com.bytelyst.platform", category: "DeepLink")
}