ClockKit

RSS for tag

Display app-specific data in the complications on the clock face using ClockKit.

Posts under ClockKit tag

10 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

ClockKit to WidgetKit migration bug
Hi, I'm migrating ClockKit complications to WidgetKit. Everything works well except when I run the WidgetKit version, I see complications appear twice each in the complications list for my app when adding them. If I restart the app from Xcode, it won't happen again, only for the first time. But on the actual device, only Watch restart or app reinstall fixes it which is frustrating and would not be ideal for live users. I even tried Apple's example ClockKit project -> added complications to watch face -> added WidgetKit target, CLKComplicationWidgetMigrator and func widgetConfiguration(from complicationDescriptor: CLKComplicationDescriptor) code -> run the app -> new complications appear correctly replacing old ones -> when hold to add/change complications I see it doubled (screenshot attached) and it's even selected twice If I add more complications, they will appear two times as well except one selected in two places almost like it's two same lists created. class ComplicationController: NSObject, CLKComplicationDataSource, CLKComplicationWidgetMigrator { @available(watchOSApplicationExtension 9.0, *) var widgetMigrator: CLKComplicationWidgetMigrator { self } @available(watchOSApplicationExtension 9.0, *) func widgetConfiguration(from complicationDescriptor: CLKComplicationDescriptor) async -> CLKComplicationWidgetMigrationConfiguration? { switch complicationDescriptor.identifier { case "Coffee_Tracker_Caffeine_Dose": return CLKComplicationStaticWidgetMigrationConfiguration( kind: "WidgetKitComplications", extensionBundleIdentifier: "com.example.apple-samplecode.Coffee-Tracker.watchkitapp.watchkitextension.WidgetKitComplications") default: return nil } }
4
0
437
Sep ’24
Widgets Not Refreshing Timelines
I'm making an app where there are two widgets. Both widgets are supposed to get their timelines once per day, as all data for the day is known at midnight. I'm having an issue where when put on a development device, the widgets' timelines work correctly, but do not refresh the next day. I've tried both .atEnd and .after(Date) refresh policies and neither seems to work. Does anyone know why the widget isn't refreshing properly? I'm almost certain that I'm under the daily limit of refreshes (one timeline refresh and ~12 timeline entries per day). Thank you for any help! Dev device iPhone 15 Pro on 17.5.1 (21F90) with Xcode Version 15.4 (15F31d). Below is the timeline code for one of the widgets: struct EndTimeProvider: TimelineProvider { var currentHour: Int { Calendar.current.component(.hour, from: .now) } var currentMinute : Int { Calendar.current.component(.minute, from: .now) } var placeholderSixthPeriod: Period { var endMinute: Int = 0 var endHour: Int = 0 if currentMinute > 60-14 { endHour = currentHour + 1 endMinute = (currentMinute + 14) % 60 } else { endHour = currentHour endMinute = currentMinute + 14 } return Period(name: "Period 6", start: "00:00", end: "\(endHour):\(endMinute)") } func placeholder(in context: Context) -> EndTimeEntry { return EndTimeEntry(date: .now, displayPeriod: placeholderSixthPeriod, scheduleName: "Regular Day") } func getSnapshot(in context: Context, completion: @escaping (EndTimeEntry) -> ()) { if context.isPreview { completion(placeholder(in: context)) return } let entry = EndTimeEntry(date: .now, displayPeriod: placeholderSixthPeriod, scheduleName: "Regular Day") completion(entry) } func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { var entries: [EndTimeEntry] = [] let context = PersistenceController.shared.backgroundContext let scheduleFetch = StoredScheduleOnDate.fetchRequest() do { let storedSchedules = try context.fetch(scheduleFetch) let currentDate = Date() if let todaySchedule = storedSchedules.first(where: { Calendar.current.isDate($0.date!, equalTo: currentDate, toGranularity: .day) })?.schedule?.asDayType() { // Have an entry at midnight when schedules are needed let morningStart = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: .now)! let morningPeriod = Period(name: "Good morning", start: "00:00", end: todaySchedule.periods.first!.start) let morningEntry = EndTimeEntry(date: morningStart, displayPeriod: morningPeriod, scheduleName: todaySchedule.name) entries.append(morningEntry) // Passing periods should show the next full period's end time. // This means that an entry's date should be the past period's end, or the start in the first period's case. let firstPeriod = todaySchedule.periods.first! let firstPeriodEntry = EndTimeEntry(date: firstPeriod.getStartAsDate(), displayPeriod: firstPeriod, scheduleName: todaySchedule.name) entries.append(firstPeriodEntry) for index in 1..<todaySchedule.periods.count { let entry = EndTimeEntry(date: todaySchedule.periods[index-1].getEndAsDate(), displayPeriod: todaySchedule.periods[index], scheduleName: todaySchedule.name) entries.append(entry) } // Have an entry at the end of the day to have the start time of the next day shown if let tomorrowSchedule = storedSchedules.first(where: { Calendar.current.isDate($0.date!, equalTo: Calendar.current.date(byAdding: .day, value: 1, to: currentDate)!, toGranularity: .day) })?.schedule?.asDayType() { // At EOD, show tomorrow's start let endOfDay: Date = todaySchedule.periods.last!.getEndAsDate() let overnightPeriod: Period = Period(name: "Good night", start: todaySchedule.periods.last!.end, end: "00:00") let overnightEntry = EndTimeEntry(date: endOfDay, displayPeriod: overnightPeriod, scheduleName: tomorrowSchedule.name, overrideDisplayDate: tomorrowSchedule.periods.first!.getStartAsDate()) entries.append(overnightEntry) } } } catch { fatalError("Could not fetch from Core Data for widget timeline. \(error)") } let tomorrowMorning = Calendar.current.date(bySettingHour: 0, minute: 1, second: 0, of: Calendar.current.date(byAdding: .day, value: 1, to: .now)!)! let timeline = Timeline(entries: entries, policy: .after(tomorrowMorning)) completion(timeline) } } struct EndTimeEntry: TimelineEntry { let date: Date let displayPeriod: Period let scheduleName: String let overrideDisplayDate: Date? init(date: Date, displayPeriod: Period, scheduleName: String, overrideDisplayDate: Date) { self.date = date self.displayPeriod = displayPeriod self.scheduleName = scheduleName self.overrideDisplayDate = overrideDisplayDate } init(date: Date, displayPeriod: Period, scheduleName: String) { self.date = date self.displayPeriod = displayPeriod self.scheduleName = scheduleName self.overrideDisplayDate = nil } } ...
2
0
585
Jul ’24
Extract metadata from a .watchface
Hello, fellow developers! How would you approach .watchface file to extract it's metadata? I need to know in which version of the app the face was created, to prompt the user to update the app if necessary. Looking at binary I suppose that a .watchface file consists of a PNG previews and JSON metadata and it all packed in one file somehow. Probably archived with gzip.
0
0
387
Jul ’24
Major Clock app suggestion
When you use set an Alarm in clock app, you get to choose mutliple tones and even songs from Apple music. However, when you set a Sleep|Wake up alarm using a sleep schedule, you are prompted to choose only limited tones. This limits the Alarm tone options for sleep one. Hope, Apple lets users pick the full library of tones in next update.
1
0
408
Jun ’24
Complication works on watch, but not visible in watch app on phone
I have an iPhone app, and added watch support, including a simple launcher complication. I can add the complication using the edit UI on the watch, but the complication is not visible using the watch app on the iPhone. When I add my complication on the watch and then open the watch app on the iPhone, the complication slot shows as "Off". What could I be doing wrong?
2
0
549
Jun ’24
`WCSession.isComplicationEnabled` is `false` when complication is in Smart Stack
⌚️Hello, I've noticed in watchOS 10 that when a complication is in the Smart Stack, the value of WCSession.isComplicationEnabled is false. I'm not sure if this is intentional or a bug. It seems trivial at first glance, but it actually affects the communication mechanism mentioned in Implementing Two-Way Communication Using Watch Connectivity. In the following two scenarios, if the user has only added the app's complication to the Smart Stack, then the watch app will not be able to communicate properly with the iOS app. Scenario 1 - WCSession.transferCurrentComplicationUserInfo() // update complications from the iOS app if WCSession.default.isComplicationEnabled { let userInfoTransfer = WCSession.default.transferCurrentComplicationUserInfo(userInfo) // ... } As described in Implementing Two-Way Communication Using Watch Connectivity, when the iOS app proactively updates the data of the watch app, since WCSession.isComplicationEnabled is false, WCSession will refuse to transfer any data. This causes the standalone complication in Smart Stack to not be updated. Scenario 2 - WKApplicationRefreshBackgroundTask When the watch app uses WKApplication.scheduleBackgroundRefresh() to periodically update data, as long as the user has added the app's complication to the watch face, the corresponding WKApplicationRefreshBackgroundTask can be executed periodically in the background to fetch data. However, if the user has only added complication to the Smart Stack, then the watch app will be completely purged, and the background task will not be executed at all. Although WCSession.isComplicationEnabled is not directly used in this scenario, its behavior appears to be the same, that is, the complication in the Smart Stack is not considered a complication by the system. Should I submit a bug report?
2
0
988
Feb ’24
ClockKit Complications not working with Xcode 14 single-target watchOS app
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
3
1
1.9k
Aug ’24