Search code examples
iosswiftwidgetkitios18

Open specific screen in App Delegate after iOS 18 Control Center widget is tapped (failed to open URL)


I have created an iOS 18 Control Center Widget that launches my app. However I am not able to detect it in AppDelegate.swift. I have created custom scheme funRun://beginRun in Target => Info => URL Types URL Types setup in Xcode

This method in AppDelegate.swift does not fire at all and I get this error in console:

Failed to open URL runFun://beginRun: Error Domain=NSOSStatusErrorDomain Code=-10814 "(null)" UserInfo={_LSLine=279, _LSFunction=-[_LSDOpenClient openURL:fileHandle:options:completionHandler:]}
 func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        print("Scheme \(url.scheme)")
        print("Host \(url.host)")
        return true
    }

I tried this answer to no avail: iOS 18 Control Widget that opens a URL

Even adding EnvironmentValues().openURL(url) as suggested here did not help.

 @MainActor
    func perform() async throws -> some IntentResult & OpensIntent {
        let url = URL(string: "funRun://beginRun")!
        EnvironmentValues().openURL(url)
        return .result(opensIntent: OpenURLIntent(url))
    }

Here is my extension code: My goal is to detect the url string from the request, so I can decide which screen to launch from AppDelegate's open url method. When I test this with iOS 18 RC it does not work either in simulator or on real device

import AppIntents
import SwiftUI
import WidgetKit

@available(iOS 18.0, watchOS 11.0, macOS 15.0, visionOS 2.0, *)
struct StartRunControl: ControlWidget {
    var body: some ControlWidgetConfiguration { 
                                               
        StaticControlConfiguration(
            kind: "name.funRun.StartRun",
            provider: Provider()
        ) { value in
                ControlWidgetButton("Hello",
                                    action: MyIntent()) { hi in
                    Label("Start", systemImage: "sun.min.fill")
                }
        }
        .displayName("Start run")
        .description("Opens a run screen.")
    }
}
 
@available(iOS 18.0, watchOS 11.0, macOS 15.0, visionOS 2.0, *)
extension StartRunControl {
    struct Provider: ControlValueProvider {
        var previewValue: Bool {
            false
        }

        func currentValue() async throws -> Bool {
            let isRunning = true // Check if the timer is running
            return isRunning
        }
    }
}

@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 {
        let url = URL(string: "funRun://beginRun")!
        EnvironmentValues().openURL(url)
        return .result(opensIntent: OpenURLIntent(url))
    }
}

I even checked info.plist and it seems okay.

    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeRole</key>
            <string>Editor</string>
            <key>CFBundleURLName</key>
            <string>beginRun</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>funRun://beginRun</string>
            </array>
        </dict>
    </array>

Does anyone know where the problem might be? Thanks!


Solution

  • Refer to the following link to set the Target Membership of the file.

    https://forums.developer.apple.com/forums/thread/758637