watchOS 9 complications - where do they go?

I have an app with Home Screen widgets, and new Lock Screen widgets, and I'm trying to get my complications to work on the Watch.

The widgets are running off dynamic intents, taking a list of items from user defaults and providing that list for the user to choose when they add a Home Screen or Lock Screen widget. This works fine.

In the "Complications and widgets: Reloaded" video from WWDC2022 at about 7:00 the guy tells you to duplicate the widget extension, rename it, and set it to run on watchOS. I ended up with these targets:

  • Main App (embeds Widget, Intents Handler and Watch App)
  • Widget
  • Intents Handler
  • Watch App (embeds Complications)
  • Complications (new, copy of Widget)

At about 8:30 he adds the supported families for the Watch to his EmojiRangersWidget WidgetConfiguration, so it looks like the code for the accessory widgets is used for both Lock Screen widgets and complications?

Should I be putting all my complications views in the Widget target, something like this?

@main
struct WidgetEntry: Widget
{
	public var body: some WidgetConfiguration {
		IntentConfiguration(kind: kWidgetKind,
							intent: DynamicItemSelectionIntent.self,
							provider: Provider()
		) { entry in
			WidgetEntryView(entry: entry)
		}
		.configurationDisplayName("abc")
		.description("def")
#if os(watchOS)
		.supportedFamilies([.accessoryCircular, .accessoryInline, .accessoryRectangular, .accessoryCorner])
#else
		.supportedFamilies([.accessoryCircular, .accessoryInline, .accessoryRectangular, .systemSmall, .systemMedium])
#endif
	}
}

struct WidgetEntryView: View
{
	var entry: Provider.Entry
	@Environment(\.widgetFamily) var family

	@ViewBuilder
	var body: some View {
#if os(watchOS)
		switch family {
			case .accessoryCircular, .accessoryCorner, .accessoryInline, .accessoryRectangular:
				ComplicationView(item: entry.item)
			@unknown default:
				UnknownComplicationView(item: entry.item)
		}
#else
		switch family {
			case .accessoryCircular, .accessoryInline, .accessoryRectangular:
				LockScreenWidgetView(item: entry.item)
			case .systemSmall:
				SmallWidgetView(item: entry.item)
			case .systemMedium, .systemLarge, .systemExtraLarge:
				MediumWidgetView(item: entry.item)
			@unknown default:
				UnknownWidgetView(item: entry.item)
		}
#endif
	}
}

I've previously been told by an Apple engineer on these forums: "The complication code should be in the watch app target. That's where watchOS 7 and 8 will look for complications, and where watchOS 9 will look for old ClockKit complications for migration to their WidgetKit counterparts." I'm no longer supporting watchOS 7/8, but does this mean that watchOS 7/8 will look for old complications in there to migrate to the new stuff into the Widget target? I asked a couple of follow-up questions but they never got answered ¯\_(ツ)_/¯

Apple make this stuff so unnecessarily complex, even though their videos make it look so easy. How many times have we all paused their videos to see exactly what code they're writing and where they're putting it? There's practically zero help out there - these forums are full of questions and few answers. Xcode should have much better documentation and help to guide you through this. It takes so long to get anything done because there just isn't the information we need.

Accepted Reply

Okay, that didn't work.

By trying to put all the complications code into the Widget target I got a billion errors... I went through them all and got down to a few that simply cannot be fixed. (Two @main for some reason even though the Watch App and Widget were not sharing code.)

So, this is what I have. Let me know if I'm doing something glaringly obviously wrong:

(There's a ComplicationsIntentHandler and a WidgetIntentHandler that do almost the same thing but need to be separate.)

Replies

Okay, that didn't work.

By trying to put all the complications code into the Widget target I got a billion errors... I went through them all and got down to a few that simply cannot be fixed. (Two @main for some reason even though the Watch App and Widget were not sharing code.)

So, this is what I have. Let me know if I'm doing something glaringly obviously wrong:

(There's a ComplicationsIntentHandler and a WidgetIntentHandler that do almost the same thing but need to be separate.)