使用 URLSchemeRouter
,你可以为你的应用程序的 URL Scheme 创建一个路由器并添加路由,类似于 JavaScript 中的 Express 或 Hono 路由器。路由输入从 URL 查询项中解码。
首先,创建一个 URLSchemeRouter
,传入你在应用程序的 Info.plist
中声明的 Scheme。
let router = URLSchemeRouter(scheme: "notesapp")
接下来,向你的路由器添加一些路由。为你想要在应用程序中支持的每个动作添加一个路由。这是一个处理以下 URL 的示例:notesapp:///search
注意
URL Scheme 的 host 组件会被忽略。这意味着 notesapp:///search
等价于 notesapp://x-callback-url/search
router.route("/search") {
print("Navigate to search view")
}
最后,当你的应用程序收到 URL 时调用 router.handle(_:)
来触发匹配的路由。
router.handle(url)
要从 URL 解析查询项,创建一个与你想要解析的项匹配的 Decodable
类型。然后,在路由处理程序中指定你想要解码的类型。这是一个处理 notesapp:///create?title=My%20note
的示例
struct Note: Decodable {
let title: String
let body: String?
}
router.route("/create") { (note: Note) in
print("Create note with title: \(note.title), body: \(note.body ?? "empty")")
}
URLSchemeRouter
开箱即用地支持 x-callback-url,因此如果在路由处理程序中抛出错误或输入解码失败,则会调用 x-error
,并在成功时调用 x-success
。你也可以在路由处理程序中返回一个 Encodable
类型,以将输出传递给 x-success
。
struct Note: Encodable {
let title: String
let body: String?
}
router.route("/fetchNote") {
let note: Note = try database.fetchNote()
return note
}
当打开 notesapp:///fetchnotes?x-success=otherapp://
时,路由器会自动打开 otherapp:///?title=...&body=...
,并从你的 Encodable
填充成功参数。
从你的路由处理程序抛出的错误会自动作为参数传递给指定的 x-error
回调。如果未指定 x-error
,默认情况下,URLSchemeRouter
在抛出错误时将不执行任何操作。要处理错误,你可以在创建路由器时指定一个可选的 onError
闭包。在此示例中,当 URLSchemeRouter
遇到解码问题或错误时,会显示一个警报
let router = URLSchemeRouter(scheme: "notesapp") { [weak self] error in
guard let self else { return }
let alertController = UIAlertController(
title: error.localizedDescription,
message: nil,
preferredStyle: .alert
)
alertController.addAction(.init(title: "OK", style: .default))
window?.rootViewController?.present(
alertController,
animated: true
)
}
在实践中,你的 URLSchemeRouter
可能会位于 scene(_:openURLContexts:)
或 onOpenURL
(SwiftUI) 中。 这是一个如何在 iOS 应用程序中处理 URL 的完整示例
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo _: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
// App setup code...
for urlContext in connectionOptions.urlContexts {
handleURL(urlContext.url)
}
}
func scene(_: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
for context in URLContexts {
handleURL(context.url)
}
}
func handleURL(_ url: URL) {
let router = URLSchemeRouter(scheme: "readlaterapp") { [weak self] error in
guard let self else { return }
let alertController = UIAlertController(
title: error.localizedDescription,
message: nil,
preferredStyle: .alert
)
alertController.addAction(.init(title: "OK", style: .default))
window?.rootViewController?.present(
alertController,
animated: true
)
}
struct SaveParameters: Decodable {
let url: String
}
router.route("/save") { (parameters: SaveParameters) in
guard let url = URL(string: parameters.url) else {
struct InvalidURLError: Error, LocalizedError {
var errorDescription: String? { "Invalid URL" }
}
throw InvalidURLError()
}
try database.save(url)
}
struct FetchParameters: Encodable {
let urls: [String]
}
router.route("/fetch") {
return FetchParameters(
urls: try database.fetchURLs().map(\.absoluteString)
)
}
router.handle(url)
}
}