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
- In the Widget view you need to create a
Link
and set itsdestination
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")!)
- 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
Does Webkit in iOS 11 (Beta) Support Webrtc
Selecting a Word in a Uitextview
iOS 10, Nsuserdefaults Does Not Work
How to Get a Swift Type Name as a String with Its Namespace (Or Framework Name)
Swift 3 JSON Nsfastenumerationiterator Has No Subscript Members
Cashapelayer with Different Colors
How to Get Email Id of User Using Facebook Sdk 4.7 in iOS 9
How to Show the Loading Indicator in the Top Status Bar
How to Increase Line Spacing in Uilabel in Swift
Distanceinmeters Problems with Arrays and Sort
User Set Time for Notification in Swift
Inappropriate Expected Declaration Error
What Do the "M" and "A" Icons in the Project Navigator of Xcode 4 Mean When I Create a New Project
React Native Xcode Project Product Archive Fails with Duplicate Symbols for Architecture Arm64