Detect App Launch from Widgetkit Widget Extension

Detect app launch from WidgetKit widget extension

To detect an app launch from a WidgetKit widget extension where the parent application supports scenes you'll need to implement scene(_:openURLContexts:), for launching from a background state, and scene(_:willConnectTo:options:), for launching from a cold state, in your parent application's SceneDelegate. Also, add widgetURL(_:) to your widget's view.

Widget's View:

struct WidgetEntryView: View {

var entry: SimpleEntry

private static let deeplinkURL: URL = URL(string: "widget-deeplink://")!

var body: some View {
Text(entry.date, style: .time)
.widgetURL(WidgetEntryView.deeplinkURL)
}

}

Parent application's SceneDelegate:

// App launched
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _: UIWindowScene = scene as? UIWindowScene else { return }
maybeOpenedFromWidget(urlContexts: connectionOptions.urlContexts)
}

// App opened from background
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
maybeOpenedFromWidget(urlContexts: URLContexts)
}

private func maybeOpenedFromWidget(urlContexts: Set<UIOpenURLContext>) {
guard let _: UIOpenURLContext = urlContexts.first(where: { $0.url.scheme == "widget-deeplink" }) else { return }
print(" Launched from widget")
}

Perform a deeplink from SwiftUI widget on tap

  1. In the Widget view you need to create a Link and set its destination url:
struct SimpleWidgetEntryView: View {
var entry: SimpleProvider.Entry

var body: some View {
Link(destination: URL(string: "widget://link1")!) {
Text("Link 1")
}
}
}

Note that Link works in medium and large Widgets only. If you use a small Widget you need to use:

.widgetURL(URL(string: "widget://link0")!)

  1. In your App view receive the url using onOpenURL:
@main
struct WidgetTestApp: App {
var body: some Scene {
WindowGroup {
Text("Test")
.onOpenURL { url in
print("Received deep link: \(url)")
}
}
}
}

It is also possible to receive deep links in the SceneDelegate by overriding:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>)

You can find more explanation on how to use this function in this thread:

  • Detect app launch from WidgetKit widget extension

Here is a GitHub repository with different Widget examples including the DeepLink Widget.

How to read files created by the app by iOS WidgetKit?

In order to read files created by the iOS widgetKit, you need to create files in the shared container

let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "yourapp.contents")?.appendingPathComponent("hello")
let data = Data("test read".utf8)
try! data.write(to: url!)

And you can read the data in the Widget class

@main
struct StuffManagerWidget: Widget {
let kind: String = "TestWidget"

var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: TestIntent.self, provider: Provider()){ entry in
WidgetEntryView(entry: entry, string: string)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}

var string: String = {
let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "yourapp.contents")?.appendingPathComponent("hello")
let data = try! Data(contentsOf: url!)
let string = String(data: data, encoding: .utf8)!
return string
}()
}

iOS Widget Extension: Failed to show Widget on running with widget extension scheme

Well, I finally figured it out and solved this problem.

Letter case turns out to be the culprit.

One thing I haven't mentioned is that my project contains a folder reference name "Plugins" to be packed into the bundle. It contains several JavaScript files used by the app.

Screenshot: How the file tree looks like

Then I noticed that in the "Build Phases" settings, the embedded app extensions are specified "PlugIns" as the default destination, which shares the same name with the "Plugins" folder listed in the "Copy Bundle Resources" if the letter case is ignored.

Screenshot: "Build Phases" settings

After exploring the build folder, I found that the widget extension was placed under the "Plugins" folder instead of the "PlugIns" one. When the widget extension is being launched, the "PlugIns" folder in the bundle is being looked up to find the .appex extension. The "PlugIns" folder does not exist but the "Plugins" one does in this scenario. And this can also explain why the app works but the extensions do not.

Renaming the folder to be included in the bundle solved my problem.

Interact with item inside widgets iOS14

You can do this using Link in SwiftUI.

Link(destination: url, label: {
// add your UI components in this block on which you want to perform click action.
}

url: Provide a unique string to identify widget action in the main app.

Now In the main app, use below method in AppDelegate.swift file:

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
}

In this method, you can identify the URL that comes from the WidgetKit action.



Related Topics



Leave a reply



Submit