如何处理打开 URL 以加载特定应用程序屏幕?
How to handle opening a URL to load a specific app screen?
我想实现一个功能,当用户点击 URL 重置他们的密码时,它会将他们定向到我的应用程序中的“PasswordReset”屏幕。
目前,当我点击 URL(例如:https://app.myApp.com/auth/reset?token=1234)时,它会打开“FirstView”屏幕而不是“PasswordReset”屏幕。
我注意到当我在“myApp 结构”中打印 URL 时,它会打印上面正确的 URL,但是如果我在“RootView 结构”中打印 URL ,它显示为零。我可以采取哪些步骤来确保 URL 加载到“密码重置”屏幕?
@main
struct myApp: App {
@State var route: URL?
var body: some Scene {
WindowGroup {
RootView(url: route)
.onOpenURL { url in
let _ = print(url) // prints: https://app.myApp.com/auth/reset?token=1234
self.route = url
}
}
}
}
struct RootView: View {
@ObservedObject var authenticator = Authenticator.shared
@State var url: URL?
var body: some View {
let _ = print(url) // prints: nil
// if user clicks on url to reset password, PasswordResetView should load
if url?.pathComponents.contains("reset") ?? false,
let resetToken = url?.queryDictionary?["token"] {
NavigationView {
PasswordResetView(viewModel: .init(onFinish: { url = nil }))
.onAppear {
self.authenticator.accessToken = resetToken
}
}
// if user had accessToken (can log in), the MainTabView should load
} else if authenticator.accessToken != nil {
MainTabView()
// if user has no accessToken, FirstView should load
} else {
NavigationView {
FirstView()
}
}
}
}
extension URL {
var queryDictionary: [String: String]? {
guard let query = self.query else { return nil }
var queryStrings = [String: String]()
for pair in query.components(separatedBy: "&") {
let key = pair.components(separatedBy: "=")[0]
let value = pair
.components(separatedBy: "=")[1]
.replacingOccurrences(of: "+", with: " ")
.removingPercentEncoding ?? ""
queryStrings[key] = value
}
return queryStrings
}
}
这是我对这个问题的解决方案:
struct myApp: App {
@ObservedObject var authenticator = Authenticator.shared
@State var route: URL?
@State var urlToken: String?
var body: some Scene {
WindowGroup {
RootView(url: route)
.onOpenURL { url in
self.route = url
if url.pathComponents.contains("reset"), let token = url.queryDictionary?["token"] {
self.urlToken = token
}
}
.fullScreenCover(item: $urlToken) { _ in
NavigationView {
PasswordResetView(viewModel: .init(onFinish: {
route = nil
urlToken = nil
}))
.onAppear {
self.authenticator.accessToken = urlToken
}
}
}
}
}
}
struct RootView: View {
@ObservedObject var authenticator = Authenticator.shared
var body: some View {
if authenticator.accessToken != nil {
MainTabView()
} else {
NavigationView {
FirstView()
}
}
}
}
我想实现一个功能,当用户点击 URL 重置他们的密码时,它会将他们定向到我的应用程序中的“PasswordReset”屏幕。
目前,当我点击 URL(例如:https://app.myApp.com/auth/reset?token=1234)时,它会打开“FirstView”屏幕而不是“PasswordReset”屏幕。
我注意到当我在“myApp 结构”中打印 URL 时,它会打印上面正确的 URL,但是如果我在“RootView 结构”中打印 URL ,它显示为零。我可以采取哪些步骤来确保 URL 加载到“密码重置”屏幕?
@main
struct myApp: App {
@State var route: URL?
var body: some Scene {
WindowGroup {
RootView(url: route)
.onOpenURL { url in
let _ = print(url) // prints: https://app.myApp.com/auth/reset?token=1234
self.route = url
}
}
}
}
struct RootView: View {
@ObservedObject var authenticator = Authenticator.shared
@State var url: URL?
var body: some View {
let _ = print(url) // prints: nil
// if user clicks on url to reset password, PasswordResetView should load
if url?.pathComponents.contains("reset") ?? false,
let resetToken = url?.queryDictionary?["token"] {
NavigationView {
PasswordResetView(viewModel: .init(onFinish: { url = nil }))
.onAppear {
self.authenticator.accessToken = resetToken
}
}
// if user had accessToken (can log in), the MainTabView should load
} else if authenticator.accessToken != nil {
MainTabView()
// if user has no accessToken, FirstView should load
} else {
NavigationView {
FirstView()
}
}
}
}
extension URL {
var queryDictionary: [String: String]? {
guard let query = self.query else { return nil }
var queryStrings = [String: String]()
for pair in query.components(separatedBy: "&") {
let key = pair.components(separatedBy: "=")[0]
let value = pair
.components(separatedBy: "=")[1]
.replacingOccurrences(of: "+", with: " ")
.removingPercentEncoding ?? ""
queryStrings[key] = value
}
return queryStrings
}
}
这是我对这个问题的解决方案:
struct myApp: App {
@ObservedObject var authenticator = Authenticator.shared
@State var route: URL?
@State var urlToken: String?
var body: some Scene {
WindowGroup {
RootView(url: route)
.onOpenURL { url in
self.route = url
if url.pathComponents.contains("reset"), let token = url.queryDictionary?["token"] {
self.urlToken = token
}
}
.fullScreenCover(item: $urlToken) { _ in
NavigationView {
PasswordResetView(viewModel: .init(onFinish: {
route = nil
urlToken = nil
}))
.onAppear {
self.authenticator.accessToken = urlToken
}
}
}
}
}
}
struct RootView: View {
@ObservedObject var authenticator = Authenticator.shared
var body: some View {
if authenticator.accessToken != nil {
MainTabView()
} else {
NavigationView {
FirstView()
}
}
}
}