3

How to Create Configurable Widgets With Dynamic Options?

 1 year ago
source link: https://swiftsenpai.com/development/configurable-widgets-dynamic-options/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

How to Create Configurable Widgets With Dynamic Options?

In my last article, I have shown you how to create a widget with static options. This might be good for some cases, but in most cases, we might not be able to determine the widget’s options during development time. Under these circumstances, we will need a configurable widget with dynamic options.

In general, here are the situations where dynamic options are needed:

  1. The options are provided by a remote server (API).
  2. The options are from the widget’s host app.
  3. The options are generated at runtime.

In this article, I will walk you through the process of displaying dynamic options that are fetched from a remote server. Although the guide focuses on this scenario, the principles and techniques discussed can also be applied to the other 2 situations as well.

Note:

If you’re unfamiliar with the basic of creating a configurable widget , I highly encourage you to first read my blog post called “How to Create Configurable Widgets With Static Options?“.


The Crypto Price Widget

The sample widget we going to create is a customizable crypto price widget. It allows users to track their preferred cryptocurrencies.

The widget will fetch the market’s current top ten cryptocurrencies from the CoinCap API and show them as user selections. Once the user selected an asset, the widget will fetch the asset’s latest price from this API and show it on the widget.

As you can see, the selections (customization) given by this widget are from an API, which makes the widget’s options dynamic. Hence, creating this widget will involve setting up a custom type and adding an intents extension.

Let’s take a look at them in details one by one.


Adding Custom Type to Intent Definition

Assuming you already created the widget extension named “CryptoPriceWidget”. Open the intent definition file and add a new type. We will name our custom type Crypto and give it 2 properties: name and symbol, both of them will be of type String.

Add new type to intent definition in Xcode
Add new type to intent definition

Notice that identifier and displayString are mandatory for a custom type in the intent definition, so we will just leave it there.

Next, select the custom intent and add a new parameter. Set the parameter type to Crypto and also make sure to check the “Options are provided dynamically” checkbox.

Add parameter of custom type in Xcode

Add parameter of custom type

With that, we have added a custom type to the intent definition.


Adding the Intents Extension

Setting up the Intents Extension

In order to show dynamic options, we must let WidgetKit know where to get data for the dynamic options. This is where intents extension comes into play. Let’s go ahead and add one to the targets:

Add intents extension as new target in Xcode
Add intents extension as new target

After that, select the intents extension, go to the “General” tab, and then add the custom intent class name to the “Supported Intents” section. You can refer to the image below for more information:

How to add supported intent to intents extension in Xcode
Add supported intent

Next up, select the “CryptoPriceWidget” intent definition and make it a target member of the intents extension.

Make intent definition member of the intents extension target in Xcode
Make intent definition member of the intents extension target

This step is important as we will have to conform the IntentHandler to the intent handling protocol generated by the intent definition.

Speaking of the intent handling protocol, here’s how to see its definition:

Locate the intent handling protocol definition in Xcode

Locate the intent handling protocol definition

What we really interested is the protocol name. Go ahead and copy the protocol name CryptoPriceConfigurationIntentHandling (the name might vary depending on how you name the custom intent).

Now, open the IntentHandler.swift file located in the intent extension folder and add the conformance to the IntentHandler class.

Conform to intent handling protocol using Swift in Xcode

Conform to intent handling protocol

Implementing the Intent Handler

At this stage, you should see the compiler complaining about “Type ‘IntentHandler’ does not conform to protocol ‘CryptoPriceConfigurationintentHandling“. Go ahead and can ask Xcode to fix the issue for us.

Fix the intent handling protocol conformance error in Xcode

Fix the intent handling protocol conformance error

As a result, you should see 2 protocol stubs being added:

func provideSelectedCryptoOptionsCollection(
    for intent: CryptoPriceConfigurationIntent,
    with completion: @escaping (INObjectCollection<Crypto>?, Error?) -> Void
) {
    // Implementation here...
}

func provideSelectedCryptoOptionsCollection(
    for intent: CryptoPriceConfigurationIntent
) async throws -> INObjectCollection<Crypto> {
    // Implementation here...    
}

Both of these methods serve the same purpose, but one uses closure-based syntax while the other utilizes the Swift Concurrency syntax. We only need to implement one of these, so go ahead and delete the closure-based one.

As the name implies, this method will provide a collection of cryptocurrency options for users to choose from. In other words, code that fetches the top ten cryptocurrencies from the remote server will go into this method.

Also, notice the return type of the method — INObjectCollection<Crypto>, the Crypto type being used here is the custom type we added at the beginning of this article.

OK, enough with the explanation. Let’s take a look at its implementation and break it down after:

func provideSelectedCryptoOptionsCollection(
    for intent: CryptoPriceConfigurationIntent
) async throws -> INObjectCollection<Crypto> {
    
    // 1
    // Fetch list of top ten crypto from API
    let assets = try await AssetFetcher.fetchTopTenAssets()
    
    // 2
    // Transform `[Asset]` to `[Crypto]`
    let cryptos = assets.map { asset in
        
        let crypto = Crypto(
            identifier: asset.id,
            display: "\(asset.name) (\(asset.symbol))"
        )
        crypto.symbol = asset.symbol
        crypto.name = asset.name
        
        return crypto
    }
    
    // 3
    // Create a collection with the array of cryptos.
    let collection = INObjectCollection(items: cryptos)
    
    // Return the collections
    return collection
}
  1. Fetch the top ten cryptocurrencies from API using the AssetFetcher helper class.
  2. Transform the results we get from the API call ([Asset]) into our desired type ([Crypto]). Do take note that the displayString we provide when initializing Crypto will be shown on the widget’s configuration UI.
  3. Create and return a collection of Crypto so that WidgetKit can populate the dynamic options accordingly.

And with that, the setup process for the intents extension is complete.


Implementing the Widget

In terms of implementing the widget, there is nothing new to discuss, all the required principles and techniques have been discussed in the following articles:

Therefore, I’ll leave that as an exercise for you! In case you are stuck, you can find the full sample code here (files are located in the CryptoPriceWidget and CryptoPriceIntentExtension folder).


Wrapping Up

Now that you’ve learned how everything operates, showing dynamic options, whether they’re provided by the host app or generated at runtime, should be straightforward. All you need to do is to update the IntentHandler implementation accordingly.

Just in case you are not sure how to share data between the host app and the widget extension, this article should get you started.


If you enjoy reading this article, feel free to check out my other articles related to WidgetKit. You can also follow me on Twitter, and subscribe to my newsletter so that you won’t miss out on any of my upcoming iOS development-related articles.

Thanks for reading. 👨🏻‍💻


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK