onmyway133 / blog

🍁 What you don't know is what you haven't learned
https://onmyway133.com/
MIT License
679 stars 33 forks source link

How to open app with Control Widget on iOS 18 #983

Open onmyway133 opened 1 month ago

onmyway133 commented 1 month ago

In iOS 18, we can make Control Widget in Widget extension

import WidgetKit
import SwiftUI

@available(iOS 18.0, *)
struct BookControlWidget: ControlWidget {
    var body: some ControlWidgetConfiguration {
        StaticControlConfiguration(kind: "Book") {
            ControlWidgetButton(action: BookIntent()) {
                Label("Book", systemImage: "airplane")
            }
        }
        .displayName("Book")
    }
}
import AppIntents

@available(iOS 18.0, *)
struct BookIntent: AppIntent {
    static let title: LocalizedStringResource = "Book"

    static var openAppWhenRun = true
    static var isDiscoverable = true

    func perform() async throws -> some IntentResult & OpensIntent {
        let url = URL(string: "myapp://book")!
        return .result(opensIntent: OpenURLIntent(url))
    }
}

From Creating controls to perform actions across the system

The system requires the Target Membership of the app intent to be set to both the app and the widget extension to open the app

We can open our app with OpenIntent

Set your control’s action to an app intent that conforms to OpenIntent to open your app when someone uses a control. Using OpenIntent allows you to take someone to a specific area of your app when a control performs its action.

image

The system requires the Target Membership of the app intent to be set to both the app and the widget extension to open the app.

import AppIntents

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

Another way is to use OpenURLIntent from iOS 18 to open universal link

Return an OpenURLIntent as the IntentResult of another app intent’s [perform()](https://developer.apple.com/documentation/appintents/appintent/perform()-2vmgc) method or use place the intent on a button that appears on an interactive widget or Live Activity.

image

Note that you need to use a universal link for your URL representation, you can’t use a custom URL scheme.

import AppIntents

@available(iOS 18.0, *)
struct Bookntent: AppIntent {
    static let title: LocalizedStringResource = "Book"

    static var openAppWhenRun = true
    static var isDiscoverable = true

    func perform() async throws -> some IntentResult & OpensIntent {
        let url = URL(string: "https://myapp.com/book")!
        return .result(opensIntent: OpenURLIntent(url))
    }
}

There's an issue in iOS 18 where universal link triggered from Control Widget does not open the app correctly. iOS 18.1 fixes this problem. Read iOS 18.1 release notes

Fixed: Apps that support universal links might not open normally and instead open the browser when the user navigates to a URL provided by the OpenURLIntent or URLRepresentableIntent APIs. (133764689) (FB14784347)

Read more

dota2AndTI commented 1 month ago

func perform() async throws -> some IntentResult & OpensIntent { let url = URL(string: "myapp://book")! return .result(opensIntent: OpenURLIntent(url)) } 这种方式iOS18不能跳转APP

BillyMiracle commented 4 weeks ago

func perform() async throws -> some IntentResult & OpensIntent { let url = URL(string: "myapp://book")! return .result(opensIntent: OpenURLIntent(url)) } 这种方式iOS18不能跳转APP

把target membership的主工程和extension都加上应该就可以了“The system requires the Target Membership of the app intent to be set to both the app and the widget extension to open the app.”