1

I want to implement a functionality where when a user clicks a URL to reset their password, it directs them to the "PasswordReset" screen in my app.

Currently, when I click the URL (ex: https://app.myApp.com/auth/reset?token=1234), it opens up to the "FirstView" screen instead of the "PasswordReset" screen.

I noticed when I print the URL in the "myApp struct", it prints the correct URL above, but if I print the URL in the "RootView struct", it shows up as nil. What steps can I take to ensure that the URL loads to the "PasswordReset" screen?

@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
    }
}
MGY
  • 7,245
  • 5
  • 41
  • 74

1 Answers1

1

Here's my solution to this issue:

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()
            }
        }
    }
}
  • fullScreenCovering is not always the best solution. Sometimes, your app comes from the background, and you need to close connections HTTPS, and WSS, manage clean other things and then close presented and pushed views, and then you can present something. Otherwise, that will end badly for so many apps. Maybe this is solving your issue for today. There are other answers on SO regarding handling URLs via Universal Link. Thank you for sharing your solution anyway @Nadia. Best – MGY Oct 28 '22 at 07:54