At the end of the last part I was aware that although the widget worked in all the contexts I care about, it looked a little weird in the .accessoryCircular family, which appears in certain watch complications and iPhone Lock Screen widgets.

In this part I'll add a second type of widget and talk a little about different widget families and contexts. As usual my focus here is on trying to share as much code as I can and have a single project that supports all the different options in obvious ways.

This section is a lot of moving code around, really, but again it sets us up going forward for more flexible widgets.


Divide and Conquer

First I'm going to pull apart the JustOneThingWidget file. I've made a new ThingView.swift file and pulled the ThingView and the ThingEntryView out to it, to leave just the Widget-specific stuff in JustOneThingWidget.swift.

Lets also make a WidgetBundle.swift file and pull the WidgetBundle out to it.

That just leaves our JustOneThingWidget struct.

Next we're going to rename the file and the struct to SystemThingWidget (and you'll see I added some more descriptive text to the configuration display name and description. Also, crucially, I've added a .supportedFamilies modifier to specify only the .systemXXX families.

There's also a .systemExtraLarge family but it's only on iPad, which I don't much care about. Having it there would mean more conditional checks so I just ignored it. Those three are available on iOS and macOS so all good.

The last thing you can see above is that I ended up just deleting ThingEntryView and going straight to ThingView. The EntryView wasn't really doing anything useful and I didn't need anything from the Entry but the Thing.

Next we're going to add a new file and call it AccessoryWidget and copy and paste the whole SystemWidget into it.

I've renamed the struct and changed the configurationDisplayName and description just so we can tell it apart from the SystemWidget. I've also set the supportedFamilies to the .accessoryXXX options.

Now we have two widget types we need to make sure they are both in the WidgetBundle. This is where things get interesting.


Computer Says No

It turns out that (at time of writing) the system families are not supported on the Watch and the accessory families are not supported on the Mac, so we'll need to help Xcode build and package the right things for the right platforms.

First we'll see target memberships for the SystemWidget and AccessoryWidget files to only build them for the right platforms. SystemWidget is NOT for the watch and AccessoryWidget is NOT for the Mac.

Next, since the two widgets may or may not be available, we'll need to help WidgetBundle out.

Once again, SystemWidget is NOT for the watch, AccessoryWidget is NOT for the Mac.

At this point everything. should build again and we have two distinct widgets for two different situations, though they both just show our ThingView text.

Lets try them out in the various simulators.

What do we have?

On the iPhone, we can see the Home Screen is loading the SystemWidget (the displayName is Just One Thing). If we add a widget to the Lock Screen we can see its using the AccessoryWidget (displayName is One Tiny Thing).

The watch is interesting too. It's not immediately obvious we're getting the AccessoryWidget because the watch doesn't show the displayName or the description. Instead its showing the text from the recommendations method on the Provider ("My Intent Widget").

Here we can see the accessoryRectangular, accessoryInline and accessoryCircular. Rectangular looks just like the SystemWidget, and we could have tried to make the SystemWidget support that family, but we'd have still had to deal with a more complicated situation around the availability of families on platforms. This way, there are two distinct widgets for different situations, but we know they are just simple wrappers round the ThingView, so even though we don't share the Widget struct itself, there's a single place we can make changes and have them be reflected in both platforms.

I notice that they accessoryInline view isn't using my fonts or colours, just simple text. Not a problem for now, but something to be aware of if we change ThingView to be more complicated.

Lastly the accessoryCircular is still just a tiny version of the ThingView and we want to change that.

I'd like to quickly explain the recommendations situation first though.

If we got back to the Provider code and add second recommendation...

Then go back to the watch simulator to add a widget...

You can see how the configurations work. We have one single AccessoryWidget that (in this case) supports the accessoryInline family, but we offer the user two different configurations.

If you remember back a few posts ago, I explained that the IntentConfiguration stuff is to provide a way for a widget to be configured on the iPhone or the Mac. Perhaps we offer a way to change the number of Things, or the colour.

That complex UI is not possible on the watch and so we offer some suggested configurations like "One Thing, Random Colour", "Two Things, Accent Colour" and the watch user can select which prebuilt configuration they want to add.

We'll ignore this for now and leave the existing single recommendation as we have no configuration system yet. Let's get back to making a slightly nicer circular complication.

Where the SystemWidget just uses a ThingView, the AccessoryWidget now has its own wrapper view, which checks the environment for the widgetFamily and returns something small and simple in the circular case.

When I hacked this together the first time I made the circular widget show a total number of Things, but since I haven't built a real DataStore for this yet, I just went with the SFSymbol I've been using as an icon. Its not winning any awards, and its just a basic launcher rather than glanceable information, but it looks slightly better than the tiny text.

That's a wrap for part 6. The commit is "Part 6: Separate Widgets"