Expand my Community achievements bar.

Join us in celebrating the outstanding achievement of our AEP Community Member of the Year!

Apple’s WidgetKit and Adobe Experience Platform Mobile SDK Integration

Avatar

Employee

9/15/21

Authors: Christopher Hoffmann (@cdhoffmann) and Jiabin Geng (@shalehaha)banner.png

In this blog, we give you the step by step tutorial on how you can build a mobile widget with Apple’s WidgetKit and Adobe Experience Platform Mobile SDKs.

With the launch of iOS 14, Apple has introduced various new technologies. One such technology is WidgetKit, which gives developers the API to create their own home screen widgets on iOS. Widgets present relevant information at a glance and can be pinned in different sizes on any home screen page. Widgets are a great opportunity to provide another means of engagement for users. Users can interact with widgets by simply tapping them to be taken into the corresponding application via a deep link. They can also be made user-configurable to allow users to personalize them. With this, Widgets add a layer of customization that will allow users to spice up their home screens, while also providing users with relevant information and a quick access point into the corresponding application.

To implement a widget, you add a widget extension to your app, configure the widget with a timeline provider, and use SwiftUI views to display the widget’s content. The timeline provider tells WidgetKit when the widget’s content should be updated.

Figure 1: Apple WidgetKit processFigure 1: Apple WidgetKit process

 

Widgets can also be made user-configurable by adding a custom SiriKit intent definition to the widget extension. If the widget is user-configurable, WidgetKit automatically provides a customization interface to let users personalize their widgets according to the intent definition implementation.

Figure 2: Widget customizationFigure 2: Widget customization

 

Figure 3: Location for widgetFigure 3: Location for widget

Adobe Experience Platform SDK Integration

WidgetKit provides APIs for deep linking from the widget into the corresponding application. If the widget is user-configurable, it is also possible to deep-link into different parts of the application depending on the configuration. The integration with AEP Mobile SDK happens in the application rather than the widget extension, by performing the tracking while handling the deep link in the application.

Because widgets are SwiftUI views, the widgetUrl(_:)API can be used to deep-link into the widget’s corresponding application. When the widget is tapped, the usual deep link delegate methods are called, application(_:open:options:), scene(:openURLContexts), or onOpenUrl(perform:), depending on the configuration of the application’s lifecycle.

If custom intents are being used to support configurable widgets, then different URLs can be sent through the delegate methods when deep linking, depending on the configuration of the widget. Once the corresponding delegate methods are called, the URL can be used to distinguish how the user reached the application.

In addition to the deep linking APIs, the WidgetCenter singleton can be used to extract information about the widgets that have been installed on the home screen via the getCurrentConfigurations(_:) API. The following tutorial demonstrates how custom intents can be used in conjunction with deep-linking APIs to send relevant data to the SDK.

Tutorial

We will be building a configurable widget for AEP Swift SDK Sample application to demonstrate how to use the SDK with WidgetKit. The widget will be configured to allow switching between Core, Edge, and Assurance widget configurations. Allowing us to deep-link into the corresponding Core, Edge, and Assurance tabs in the sample app.

Step 1: Create a Widget Extension Target named AEPSampleWidget

From the menu, select file > New > Target > Widget Extension

Figure 4: Create a new widgetFigure 4: Create a new widget

Name the target: AEPWidgetExtension, and make sure “Include Configuration Intent” is checked, hit finish.

Figure 5: Naming a new widgetFigure 5: Naming a new widget

Step 2: Configure the intent definition file

Within “AEPWidgetExtension.intentDefinition” file, change the title to Extension Selection. Add an enum type by clicking the “+” at the bottom left of the custom intents file. Name the enum SampleExtension. Add the following cases: unknown, core, edge, assurance.

This enum is what will be used to switch between different widget configurations, and enable custom URL schemes for each configuration during deep linking.

Figure 6: Create the enumsFigure 6: Create the enums

Navigate back to the Configuration pane and add a parameter named sampleExtension. Change the type to SampleExtension (the enum type previously added). Adding this parameter adds the sampleExtension enum as a property to the configuration intent object which will be used in the widget implementation.

Figure 7: Add intent parametersFigure 7: Add intent parameters

Step 3: Configure AEPSampleWidget.swift

Remove the SimpleEntry configuration property and add a new extension name property. This will allow us to use the custom intent’s sampleExtension enum property to define a name for each entry.

struct SimpleEntry: TimelineEntry {
    let date: Date
    let extensionName: String

Add a helper method:

func extensionName(for configuration: ConfigurationIntent) -> String

This helper method will serve to extract a string version of the configuration intent’s sampleExtension type. This will be used when creating SimpleEntry instances as shown below.

  func extensionName(for configuration: ConfigurationIntent) -> String {
    switch configuration.sampleExtension {
    case .core:
      return "core"
    case .assurance:
      return "assurance"
    case .edge:
      return "edge"
    default:
      return "unknown"
    }
  }

Update your simple entry instances to support the new initializer in the following locations:

  1. Within the placeholder method, setting the extension name to core as the default placeholder configuration for the widget.
     func placeholder(in context: Context) -> SimpleEntry {
                SimpleEntry(date: Date(), extensionName: "core")
            }
            
  2. Within the getSnapshot method:
     func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(), extensionName: extensionName(for: configuration))
        completion(entry)
      }
  3. Within the getTimeline method:
    func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        var entries: [SimpleEntry] = []
        // Generate a timeline consisting of five entries an hour apart, starting from the current date.
        let currentDate = Date()
        for hourOffset in 0 ..< 5 {
          let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
          let entry = SimpleEntry(date: entryDate, extensionName: extensionName(for: configuration))
          entries.append(entry)
        }
        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
      }

Add the widgetUrl(_:) modifier to the AEPSampleWidgetEntryView's widget body. As a reminder, this is the API by which the deep link URL will be passed through to the delegates once the widget is tapped. The URL used here is the base URL the sample app is already using for deep linking with the entry’s extension name appended to the end. This is how different configurations will pass different URLs.

var body: some View {
    Text(entry.date, style: .time)
    Text(entry.extensionName)
      .widgetURL(URL(string: “sampleapp://” + entry.extensionName))
  }

Note: The AEPSampleApp target is already configured to support deep linking via the sameapp URL scheme.

Step 4: Configure the deep linking delegates

The AEPSampleApp project uses the SceneDelegate for the application lifecycle. In order to handle deep links when using a SceneDelegate, the scene(_scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) is the corresponding delegate method. This is where the deep linking into the application from the configurable widget will be handled. The host is extracted from the URL and then the host is used to check which corresponding view is the destination. Then AEP Mobile SDK MobileCore.track(_action:data:) API is called to perform the tracking.

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
  guard let context = URLContexts.first else { return }
  AEPAssurance.startSession(context.url)
  guard let tabbarController = window?.rootViewController as? UITabBarController else { return }
  guard let viewControllers = tabbarController.viewControllers else { return }
  guard let coreVCIndex = viewControllers.firstIndex(where: {$0 is CoreViewController}) else { return }
  guard let edgeVCIndex = viewControllers.firstIndex(where: {$0 is EdgeViewController}) else { return }
  guard let assuranceVCIndex = viewControllers.firstIndex(where: { $0 is AssuranceViewController}) else { return }
  guard let destinationViewName = context.url.host else { return }
  if destinationViewName == "core" {
    MobileCore.track(action: "Launching app to core from widget", data: nil)
    tabbarController.selectedIndex = coreVCIndex
  } else if destinationViewName == "edge" {
    MobileCore.track(action: "Launching app to edge from widget", data: nil)
    tabbarController.selectedIndex = edgeVCIndex
  } else if destinationViewName == "assurance" {
    MobileCore.track(action: "Launching app to assurance from widget", data: nil)
    tabbarController.selectedIndex = assuranceVCIndex
    }
  }

The sample app is now configured to work with home screen widgets and AEP Mobile SDK. Simply run the widget extension target, and long tap on the widget to edit the widget. Select any of the extensions from the menu and tap on it should deep-link to the corresponding view, and perform the track action with AEP Mobile SDK.

Conclusion

Adobe Experience Platform can be used in conjunction with WidgetKit to track when users tap on widgets to deep-link into specific parts of an application. If user configurable widgets are being used, then different deep links can be used to distinguish how the user reached the application. WidgetKit cannot be used to reliably track when users install or uninstall widgets, but using WidgetCenter, one could get information about the widgets currently installed on the screen. The tracking should be performed on the application side, using the corresponding delegate methods for deep linking, and not in the widget extension. Importing the SDK into the widget extension is not officially supported at this time. Feel free to ask questions or suggest what other new iOS features you would like to see integrated with AEP Mobile SDK in the comments below.

The possibilities are limitless and constrained only by our own imagination.

Follow the Adobe Experience Platform Community Blog for more developer stories and resources, and check out Adobe Developers on Twitter for the latest news and developer products. Sign up here for future Adobe Experience Platform Meetup.

References

  1. Adobe Experience Platform
  2. Documenation: AEP Mobile SDK
  3. WidgetKit
  4. Apple Documentation: Making a Configurable Widget
  5. WidgetCenter

Originally published: Apr 1, 2021