Home > Mobile >  SwiftUI: .onOpenURL action closure is not updating @State property which is of type URL
SwiftUI: .onOpenURL action closure is not updating @State property which is of type URL

Time:11-22

I am implementing my first iOS Application with SwiftUI, in which I want users to be able of clicking on an invitation link for joining a topic group (DeepLinking). Like joining a WhatsApp-Group with a link.

Therefore I associated my Domain (lets say: https://invite.example.com/) with my Swift-Project. Whenever I click/open a URL (e.g. https://invite.example.com/313bceff-58e7-40ae-a1bd-b67be466ef72) my app opens and if the user is logged in an the .onOpenURL action method is triggered as expected. However, if I try to save the url in a @State URL property in the called closure, it gets not stored. The @State boolean property for showing the sheet is set to true though.

That is my code in the @main struct.


import SwiftUI

@main
struct MyApp: App {
    
    @StateObject private var appRouter: AppRouter = AppRouter()
    
    @State private var openAcceptInvitationSheet: Bool = false
    @State private var invitationURL: URL? = nil
    
    var body: some Scene {
        WindowGroup {
            switch appRouter.currentScreen {
            case .launch:
                EmptyView()
            case .login:
                LoginSignupNavigationView()
            case let .home(authenticatedUser):
                HomeTabView()
                    .environmentObject(authenticatedUser)
                    .onOpenURL { url in
                        invitationURL = url //Gets not set -> url is not nil here!
                        openAcceptInvitationSheet = true //Is working and sheet will be presented
                    }
                    .sheet(isPresented: $openAcceptInvitationSheet) {
                        //invitationURL is nil -> Why?
                        AcceptInvitationNavigationView(invitationURL: invitationURL!)
                    }
            }
        }
    }
    
}

Everything else is working here as expected. I guess I have a misconception of how the @State properties work. However in all my other views I managed assigning values to @State properties in closures which later can be used.

CodePudding user response:

Rather than using two variables for your sheet, use one – the optional URL.

.sheet(item: $invitationURL) { url in
  AcceptInvitationNavigationView(invitationURL: url)
}

The optionality of your URL? state variable takes the place of the boolean value in determining whether the sheet should display, and the sheet receives the unwrapped URL value.

I don't think that your URL is not being set – it's more a question of it's not set at the time the original sheet's closure is evaluated, which is a subtly different SwiftUI object life cycle thing! Sticking to a single object massively simplifies everything. You'll also be able to change your code in AcceptInvitationNavigationView to expect a URL rather than having to deal with being passed an optional URL.

As noted in comments, this only works if URL conforms to Identifiable, which it doesn't by default. But you can use a URL's hashValue to synthesize a unique identifier:

extension URL: Identifiable {
  var id: Int { hashValue }
}
  • Related