Search code examples
swiftuiopenurl

Listen to link click in SwiftUI TextView


I have this code below

import SwiftUI

struct MyView: View {

    let text = "If you want to know more, ***[Click Here](http://example.com)*** to continue"
    @Environment(\.openURL) private var openURL
    
    var body: some View {
        Text(LocalizedString(text))
            .environment(\.openURL, OpenURLAction{ url in
                print(url)
                return .systemAction
            })
    }
    
}

I saw this in OpenURLAction Documentation and other SO questions like This answer from workingdog support Ukraine

But I always get a runtime error from Xcode saying:

Key path value type 'WritableKeyPath<EnvironmentValues, OpenURLAction>' cannot be converted to contextual type 'KeyPath<EnvironmentValues, OpenURLAction>'

Please can some one tell me what's going wrong? I'm using Xcode 14.0.1 if needed

Note: My reason for this is that the URLs associated with the text are not valid URLs, for example, I have a URL like this "[John Doe](friend-4-2)", so I want to open another ViewController in my app depending on the particular link the user clicked

Below is an image showing how my code looks like and the error I get enter image description here


Solution

  • This was an odd one...

    The error "Key path value type 'WritableKeyPath<EnvironmentValues, OpenURLAction>' cannot be converted to contextual type 'KeyPath<EnvironmentValues, OpenURLAction>'" only happens in when supporting anything less than iOS 15.

    That is because OpenURLAction(handler:) is only available starting iOS 15.

    https://developer.apple.com/documentation/swiftui/openurlaction/init(handler:)

    If you add @available(iOS 15, *) above the struct the error should go away.

    After that you should be able to intercept any URL

    import SwiftUI
    
    @available(iOS 15, *)
    struct LinkView: View {
        @State var clickedCount: Int = 0
    
        let text = "If you want to know more, ***[Click Here](yourApp://click-here)*** to continue"
        @Environment(\.openURL) private var openURL
        
        var body: some View {
            VStack{
                Text("\(clickedCount)")
                Text(LocalizedStringKey(text))
                    .environment(\.openURL, OpenURLAction { url in
                        clickedCount += 1
                        return .systemAction
                    })
            }
        }
    }
    @available(iOS 15, *)
    struct LinkView_Previews: PreviewProvider {
        static var previews: some View {
            LinkView()
        }
    }
    

    iOS 14 and below are not supported by this method.

    But if you want to support the lower versions you can use URL Scheme with onOpenURL(perform:)

    First add the Scheme "Target > Info > URL Types"

    enter image description here

    then use the code below.

    struct LinkView: View {
        @State var clickedCount: Int = 0
        
        let text = "If you want to know more, ***[Click Here](yourApp://clickHere)*** to continue"
        @Environment(\.openURL) private var openURL
        
        var body: some View {
            VStack{
                Text("\(clickedCount)")
                Link("Click-here", destination: URL(string: "yourApp://clickHere")!)
                Text(LocalizedStringKey(text))
                    .onOpenURL { url in
                        //Check the URL
                        if url.absoluteString.hasSuffix("clickHere") {
                            clickedCount += 1
                        }else{
                            print("Tried to open \n\t\(url.absoluteString)")
                        }
                        
                    }
            }
        }
    }