I'm trying to build a WatchOS 9 corner complication with WidgetKit (using Xcode 14 beta 3).
I'd like my complication to look like the battery one in the bottom right corner. Here's my view:
struct HydrationProgressCorner: View {
var entry: Provider.Entry
var body: some View {
Text("\((100 * entry.progress / entry.target).rounded(.towardZero).formatted())%")
.widgetLabel {
ProgressView(value: entry.progress, total: entry.target)
.tint(.blue)
.widgetAccentable()
}
}
}
How can I get my text to follow the curvature of the watch face?
Watch Complications
RSS for tagDisplay timely and relevant information about your app on the Apple Watch face using Complications.
Posts under Watch Complications tag
46 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hi all,
I am working with an older watch app that still uses the older template that combines the WatchKit App and WatchKit App Extension targets.
While trying to migrate the ClockKit complication to the new WidgetKit complication, I cannot get the widget complications to load anything from user defaults. It seems like the widget complications are not using the same user defaults as the watch app though being in the same App Group.
I experimented with a new watch app using the new SwiftUI template and the WidgetKit complication was able to load data from user default successfully.
Did anyone got WidgetKit complication to load data successfully from user default with the older watch app template?
Thanks in advance!
I recently raised this post explaining how I couldn't seem to get watchOS 9 complications to work, and I've figured out a partial fix.
The original post details the issues with complications - and some are still valid - but this fix applies to both my complications and Home Screen / Lock Screen widgets.
I was following the various WWDC 2020/2022 videos and the Emoji Rangers sample code, adding bits here and there, and assuming they were completely valid. Sadly, this bit of code in the widget's dynamic intents IntentTimelineProvider getTimeline really just banjaxed everything:
// Create entries for one day, 15 minutes apart
let currentDate = Date()
for minuteOffset in stride(from: 0, to: 60 * 60 * 24, by: 15) {
let entryDate = Calendar.current.date(byAdding: .minute, value: minuteOffset, to: currentDate)!
entries.append(EventEntry(date: entryDate, event: event))
}
If I remove that, and generate a different timeline with specific dates and times (for example: now, in 10 mins, in 2 hours, in a day, etc.) the complications appear correctly, as do Home Screen and Lock Screen widgets.
The outstanding issues with complications are:
The previews all use the same data, but getSnapshot() is supposed to return the data specific to that event from the configuration, i.e. if let theId = configuration.event?.identifier. "Christmas" is correct, but "Gallery Opening" is using Christmas's data.
Once I've selected the event I want to use in a complication the edit screen shows it as totally blank, not even a placeholder:
I hope this little fix works for you guys. And, if you know how to fix the above issues, let me know.
(iOS 16.1 beta 1, Xcode 14.1 beta 1)
I'm trying to migrate from Complication with CLKComplication to WidgetKit.
I have implemented the required methods in https://developer.apple.com/documentation/widgetkit/converting-a-clockkit-app, but the migration is not working. There is no evidence that the method for migration is also called.
It was the same with Xcode 14.0.1 and Xcode 14.1RC.
class ComplicationController: NSObject, CLKComplicationDataSource, CLKComplicationWidgetMigrator {
...
@available(watchOS 9.0, *)
var widgetMigrator: CLKComplicationWidgetMigrator {
return self
}
@available(watchOS 9.0, *)
func widgetConfiguration(from complicationDescriptor: CLKComplicationDescriptor) async -> CLKComplicationWidgetMigrationConfiguration? {
return CLKComplicationStaticWidgetMigrationConfiguration(kind: "MyWidget", extensionBundleIdentifier: "com.example.myapp.mywatchkitapp.mywidget")
}
}
What's wrong? Has anyone been able to migrate?
I’m trying to create complications für the Watch using the new WidgetKit. While the complications do show up fine in the preview in Xcode and also in the Simulator, on a real device the complications do never shown anything. The complications are listed and can be selected in the watch face settings, but the preview is always black, and when added to a watch face, it shows nothing. I can tap on this „nothing“ and the App launches, so the complication is actually there. But it doesn’t show anything.
This is also true for the Watch App on the iPhone, which lets me add the complications to a watch face, but it also shows nothing.
But in the Simulator everything works as expected, the complications show their content just fine. So it looks like the code is fine.
The iPhone runs the iOS 16.1, and the watch runs watchOS 9.1 (right now the latest public releases)
Does anyone having the same issue and maybe a solution or a hint, what exactly I should check?
Hello,
I try to add a couple of Complications to my App.
Most of the Complications needs to be Configurable, so I use an IntentConfiguration for them.
I use this recommendations function to create my complications.
func recommendations() -> [IntentRecommendation<ConfigurationIntent>] {
var recommendations = [IntentRecommendation<ConfigurationIntent>]()
for vehicle in vehicleStatusList {
let intent = ConfigurationIntent()
intent.vehicleItem = VehicleItem(identifier: vehicle.id, display: vehicle.name)
recommendations.append(IntentRecommendation(intent: intent, description: vehicle.name))
}
return recommendations
}
But with this I get the result from the screenshot... How can I change this to get a better User Experience?
When I click one item in the List, I get a correct Complication.
But it would be nice to have a change to name them correctly...
The first in the List is a StaticConfiguration...
I’ve created a single-target watchOS app in Xcode 14, but I can’t seem to get ClockKit complications working.
I’ve added a CLKComplicationDataSource class to my watch target, and in the Info pane for my target I have set the CLKComplicationPrincipalClass key to MODULE-NAME.ComplicationController
I haven’t yet added Complication placeholder images to my Assets.xcassets, but as far as I am aware, that shouldn’t be a problem while I am still testing.
However, when I run it on a watchOS simulator, the complications never show up on the watch complications list when adding a complication.
All of the tutorials I can find for ClockKit complications reference older two-target WatchKit apps. Do the newer single target apps no longer support ClockKit? If so, how can I make a two-target WatchKit app with Xcode 14?
Unfortunately I cannot use WidgetKit for my complications because I need to support watchOS 7 at least, and WidgetKit only supports watchOS 9+
Thanks for your help
I have a watch complication that used to work just fine and now all of a sudden in my latest build it's showing only dashes. The watch app is there, if I tap on the complication then the watch app opens correctly, if I edit the watch face and select the complication I can see my placeholder (and it's fine) but when I select that complication, the sample watch face shows dashes.
The only major change I've made is to make the watch app a single-target without an extension delegate as per Apple's recommended settings. I don't think this is the problem as the simulator shows the complication. It's only on the Watch Series 4 that I'm testing on that I can't see my complication.
It was working only last week and I have not touched the complication code at all, so I'm stumped as to what's wrong. I can't find any help on line, apparently no one has ever seen the complication just have dashes.
When I debug the watch app and ask for how many complications there are, it comes back with zero even though I've put them on the watch face. It seems like watchOS9 is just not respecting the complication any longer but I can't figure out why.
Please note that I'm not using WidgetKit complications as the corner complication doesn't flow the text in an arc like ClockKit does, so I'm still using the old complications. But none of the code is getting called as none of my breakpoints are firing.
I've rebooted the watch about 10 times and the problem persists.
Any ideas why all of a sudden this has fallen over and gone boom?
Anyone who has successfully migrated to using WidgetKit for their Apple Watch complications, your help would be greatly appreciated!
I've migrated to WidgetKit for my Watch app. On the Apple Watch Face Editor, the new Widget names and options appear correctly.
However, on the iPhone Apple Watch app, it offers both WidgetKit and old ClockKit complications... this should not be.
I also have found rendering issues where Watch Faces on the main My Watch tab have blank gaps for WidgetKit complications, when selecting the Watch Face they then render (see photos)
I've put in a feedback FB12460375
Have tried a full clear and reinstall of my app on both devices, no change. This issue occurs on WatchOS 9.5.2 / iOS 16.5.1 and WatchOS 10 Beta 2 / iOS 17 Beta 2
Any other ideas?
In my watch app the AppIntentRecommendation that is returned by func recommendations() needs to be updated after the app is launched.
Problem:
The information that I need to return a list of AppIntentRecommendation is not available when the app is first installed, so I return a "dummy" AppIntentRecommendation initially.
After the app is launched for the first time and user has signed in, I can update the AppIntentRecommendation in func recommendations() but watchOS does not update the list of widgets presented as complications.
func recommendations() -> [AppIntentRecommendation<ConfigurationAppIntent>] {
// Create an array with all the preconfigured widgets to show.
let defaults = UserDefaults.init(suiteName: "group.myApp")!
guard let names = defaults.dictionary(forKey: "names" as? [String:String] else {
return [AppIntentRecommendation(intent: .Demo, description: "Demo")]
}
var recs = [AppIntentRecommendation<ConfigurationAppIntent>]()
for (idx, name) in names() {
let rec = ConfigurationAppIntent()
rec.order = idx
rec.carName = name
rec.action = nil
recs.append(AppIntentRecommendation(intent: rec, description: "name") )
}
return recs
}
}
We are migrating ClockKit complications to WidgetKit in our watch app (watchOS 9+). The migration went smoothly, UI part works just fine. However, we've hit the wall with widgets not updating when requested by the watch app. I believe we are missing something very simple and fundamental, but couldn't find what exactly so far. Advice and tips would be very welcome! 🙇♂️
Our implementation details:
Whenever data is changed in the main app, the updated data is submitted to the watch app via WatchConnectivity framework using WCSession.default.transferCurrentComplicationUserInfo(_:). According to documentation, this method should be used to transfer complication-related data, since it will wake the watch app even if it is in the background or not opened at all.
Watch app receives updated data and stores it in UserDefaults shared with Watch Widget Extension hosting WidgetKit complications via App Group.
Watch app then request widget timeline reload via WidgetCenter.shared.reloadAllTimelines(). According to documentation, it reloads the timelines for all configured widgets belonging to the containing app, so it seems the appropriate way to reload WidgetKit complications.
Widget Timeline Provider class in Watch Widget Extension reads updated data from shared UserDefaults and uses it to provide the updated snapshot for widget views to render.
We believe our implementation logic is correct, but it doesn't work, for some reason. Widgets sometimes update when the watch app is opened, but not always. The most definitive way to force widgets to update is to switch to a different watch face, which confirms that the Widget Timeline Provider has access to properly updated data.
P.S. We are aware of the daily reload budget imposed on widgets, so we use widgets reload trigger sparingly. Anyway, according to documentation, reload budget is not effective when in DEBUG mode, but widgets won't reload even in DEBUG mode.
Thank you!
I've migrated my ClockKit complication to WidgetKit in my app.
Everything is working fine when I run the app on watch OS 9.4 simulator with Xcode 14.3.1
But when I compile withe Xcode 15 Beta 8 ( or beta 7, 6..), the invalidateConfigurationRecommendations() does not work anymore.
the Recommendations() func from the IntentTimelineProvider is not called and thus the complication/widget list is not updated for the user
The rest is working fine, all widgets are well displayed, and I can still add / change any widget from the original list.
Running the app on a watchOS 10 simulator using Xcode 14.3.1, I've succeeded to see an error message :
[widget] invalidateConfigurationRecommendations() - error reloading supported intents: Process not authorized to make this timeline request
But I could not find any information about this error
I've also tried by migrating the SiriIntent to AppIntent on Xcode 15 Beta 8, but the issue persists, Recommendations() is still not called upon invalidateConfigurationRecommendations()
The application project is composed of :
iOS App
watchKit App
WatchKit Extension
Widget Extension (for IOS App)
Widget Intent (for iOS App)
Complication Extension (for watchOS widgets)
Looking desperately how to fix this...
Thanks for any help
Hi guys,
We have received several reports from our users: after deleting the watch app, the complications still remain.
It seems system cache the config and forget to remove after deleting the app.
(Our app is useing WidgetKit as complications)
I'm in the process of migrating one of my app's Apple Watch complications from ClockKit to WidgetKit.
In my ClockKit complications, there are some cases where a show the relative time since the last event in the app (for example, "25MIN").
With ClockKit, this was quite straightforward using a CLKTextProvider designed for this exact purpose:
CLKRelativeDateTextProvider(date: date, style: .offsetShort, units: [.hour, .minute])
This has always worked great, since it lets me specify that hours and minutes should be used, but not seconds, since they're not relevant here.
How can I accomplish the same thing with WidgetKit and SwiftUI?
The closest thing I've found is this:
Text(date, style: .relative)
It shows, for example: 14MIN 8SEC
This is not what I want, since it shows seconds. It makes the complication look messy with all the extra information, and is distracting because it updates every second until an hour has passed.
I suppose I could write my own logic to create a new timeline entry every minute to show the number of hours and minutes since the last update, but since CLKTextProvider works so nicely I wanted to check whether there is some way to accomplish the same thing without creating a new entry for every minute.
There seem to be some more customizable options with date formatters, but I haven't found a way to combine this with the SwiftUI version that updates automatically on the Apple Watch face:
https://developer.apple.com/documentation/foundation/date/relativeformatstyle/3766585-presentation
In my Watch app on watchOS 9 I was using .foregroundColor(myColour) to colour the text in a widgetLabel on a corner complication like this:
let myColour: Color = functionThatReturnsAColorObjectConstructedLike Color.init(...) // green
.widgetLabel {
Text(myText)
.foregroundColor(myColour)
}
It worked fine; the widget label was green.
Now, in watchOS 10, I see that foregroundColor() is being deprecated in favour of foregroundStyle(), and I can use .foregroundStyle(.green), and - importantly - foregroundStyle() is only available on watchOS 10 and newer.
myColour is calculated depending on some other info, so I can't just write .green, and when I use .foregroundStyle(myColour) the widget label comes out as white every time, even if I set myColour = .green.
I think I have to use some sort of extension to pick the right combination, something like:
extension View {
func foregroundType(colour: Colour, style: any ShapeStyle) -> some THING? {
if #available(watchOS 10.0, *) {
return foregroundStyle(style)
} else {
return foregroundColor(colour)
}
}
}
// Usage
let myStyle: any ShapeStyle = SOMETHING?
...
.widgetLabel {
Text(myText)
.foregroundType(colour: myColour, style: myStyle)
It doesn't work. I just can't figure out what should be returned, nor how to return it. Any ideas?
I just got my new Apple Watch series 9 - woo!
But I loaded a couple of my test apps onto it - and the complications are not rendering.
They work fine on my "old" series 6 (both are on WatchOS 10).
I tried resizing them. They run and render fine in the simulator.
But this is how it looks on my actual watch (the upper 2 complications are mine).
The modular ultra watchface ignores the font type I set in my complication.
Happens in simulator and on device (see screenshots).
I set the font via:
.font(.system(size: CGFloat(self.fontSize), weight: self.fontWeight, design: .monospaced))
Can you please help? Is this as designed or a bug?
I really a monospaced font in that place.
Thanks!
Correct:
Wrong:
I have a watch extension app that is used to show the complications using ClockKit. Then when we upgraded to watch OS 9, I added the WidgetKit Extension and all migration code for clockKit as well. Everything was working fine in Watch OS 9
But now when we upgraded to watch OS 10. The widgetKit based complication not at all running, I can't even see any of our complication in watch face
On top of that the WidgetKit extension is not showing any debug information as well, none of the break points are executed
Please let me know if anyone knows the issue
Current project structure:
Main iOS app (targets iOS 17).
Widget Extension (targets iOS 17).
Watch app (targets watchOS 10).
Complications Extension (a Widget Extension, targets watchOS 10).
I did have the complications embedded within the Watch app, but you cannot have two @mains in the same target so the complications are in their own WidgetKit extension.
Sadly, when you add a WidgetKit extension to your project, Xcode ALWAYS sets it as an iOS widget extension; it doesn't give you the choice to make it a watchOS extension so you have to figure it out yourself.
Now, I'm hitting an issue where the main iOS app works fine, the iOS widgets work fine, the Watch app runs fine, but the complications dot show up anywhere. I can't preview them in Xcode because it says various things like:
This app was not built to support this device family; app is compatible with (
1,2
) but this device supports (
4
}
I can't find any information on what goes where in the build settings.
Can someone tell me how each bit of the project should look in the build settings, please?
(There's no information anywhere in Apple's developer documentation - which is a bit weird. They've released Xcode and they have no information on the various bits. And why doesn't Xcode set up a WidgetKit extension properly when you add it? Must we all do this manually, Apple?)
Prior to watchOS10, I marked a view that I want to hide sensitive information when not being worn by using .privacySensitive() This had been working just fine.
Comes watchOS10, this doesn't work anymore. Even with this simple code
Text("TEST Privacy")
.containerBackground(for: .widget) {
Color.black
}
.privacySensitive()
The text view is displayed even when not being worn.
Has anyone experienced this? It seems like a bug to me. Any workaround? Thank you.