Search code examples
iosswiftwidgetwidgetkitios18

iOS 18 Control Widget that opens a URL


I already have an iOS 17 App Intent that works with a URL:

@available(iOS 16, *)
struct MyAppIntent: AppIntent {
    static let title : LocalizedStringResource = "My App Inent"
    static let openAppWhenRun   : Bool = true
    
    @MainActor
    func perform() async throws -> some IntentResult{
        await UIApplication.shared.open(URL(string: "myapp://myappintent")!)
        return .result()
    }
}

Now, with iOS 18 and Control Widgets, I want to create a Control Widget button that simply opens the app with the same URL. However, UIApplication code is not allowed within extensions. For this, Apple says to use OpenIntent which is shown here:

Documentation Link

Apple Sample Code from the link:

import AppIntents

struct LaunchAppIntent: OpenIntent {
    static var title: LocalizedStringResource = "Launch App"
    @Parameter(title: "Target")
    var target: LaunchAppEnum
}


enum LaunchAppEnum: String, AppEnum {
    case timer
    case history


    static var typeDisplayRepresentation = TypeDisplayRepresentation("Productivity Timer's app screens")
    static var caseDisplayRepresentations = [
        LaunchAppEnum.timer : DisplayRepresentation("Timer"),
        LaunchAppEnum.history : DisplayRepresentation("History")
    ]
}

WWDC session video about this does not cover this particular method in detail and also this sample code is a bit confusing.

So how can I alter this code to just open the app with a URL?


Solution

  • EDIT: It appears that custom URL schemes are not supported, even though it was working in earlier iOS 18 betas, as per this post

    You'll have to implement Universal Links as a workaround.


    ORIGINAL:

    I'm making use of the OpenURLIntent, and it's working perfectly.

    @available(iOS 18.0, watchOS 11.0, macOS 15.0, visionOS 2.0, *)
    struct MyIntent: AppIntent {
        static let title: LocalizedStringResource = "My Intent"
        static var openAppWhenRun: Bool = true
    
        init() {}
    
        @MainActor
        func perform() async throws -> some IntentResult & OpensIntent {
            guard let url = URL(string: "myapp://myappintent") else {
                // throw an error of your choice here
            }
    
            return .result(opensIntent: OpenURLIntent(deepLink))
        }
    }