如何处理打开 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()
            }
        }
    }
}