Post

Replies

Boosts

Views

Created

Can't pin down how to show complications properly at different stages
I understand that complications in WidgetKit and watchOS 9 are different to the old ClockKit way, and I'm trying to populate the getSnapshot, getPlaceholder and getTimeline functions appropriately for the new method. I'm using dynamic intents, and I've got that working and providing a set of items I want the user to choose from, but at the various stages of selecting and adding a complication to the watch face, nothing I do seems to work properly on a device, and sometimes in the Simulator. On the Simulator: 1: 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. Are you only supposed to show ONE particular bit of data, like some dummy preview data or something? I can do that, but the complication picker then just looks really boring, with just the text below being different. I note that Apple's weather complications show different data. How did they do that? 2: You can see that once I pick an event it's displayed correctly in the Watch face edit screen: The "72d" circular complication is the "New York" event, and the rectangular one is correctly using the data for the "London Party!". 3: Once I've selected the events I want to use in those complications the edit screen shows them as totally blank, not even a placeholder. Which method gives us that preview? 4: Once I confirm the edits and return to the Watch face, the complications appear correctly: Note: This is all from the Simulator (apart from the image of Apple's Weather complications). This stuff barely works on a device. Half the time the complications are all placeholders, and half the time they're using the wrong data! If it works in the Simulator, it should work on the phone, or there is no point in giving us the Simulator if the results are different. So basically: Q1. How do I use different data in the complications picker? Q2. How do you get the previews to show when you're in edit mode? Q3. Does anyone know how to get print() statements to work when I'm running the Complications scheme? I could answer all these questions myself if I could output some debug info to the console, but all I see is the output from the Watch App target...
11
2
3.9k
Aug ’22
Text.init(date, style: .timer) is 1 second out when it counts back up
Let's say you have a Text.init(Date().advanced(by: 5), style: .timer). It's set to countdown to a date 5 seconds from now (just for simplicity's sake). It goes like this: Countdown starts, and shows 0:05. After 1 second it shows 0:04. After 2 seconds it shows 0:03. After 3 seconds it shows 0:02. After 4 seconds it shows 0:01. After 5 seconds it shows 0:00. Countdown done. After 6 seconds it should show 0:01, right? It doesn't; it shows 0:00. After 7 seconds it shows 0:01, even though only 6 seconds have passed. It's almost as though the timer is being restarted once it hits zero, but that isn't right. There aren't "two seconds at 0:00" (that's not how time works...). Anyone seeing this, or is just me?
0
0
736
Aug ’22
[WC] WCSession counterpart app not installed BUT IT IS!
Right, this is getting on my nerves now. iOS app installed on iPhone via Xcode. Watch app installed on Watch via Xcode. Both apps are running and are in the foreground. iOS app launches on iPhone and reports: WCSession.isSupported = YES theDelegate.session.isPaired = YES theDelegate.session.watchAppInstalled = NO theDelegate.session.activationState = Activated I press a button in the Watch app. It reports: session == activated and reachable iOS app delegate receives a message from the Watch app: didReceiveMessage (from Watch): message = {     action = giveMeUpdatedItems; } The apps must be installed on the devices in order for the Watch app to have used sendMessage (which is only available if the session is reachable, which it is). iOS app delegate passes that through as a notification to another bit of code that collates the info and sends it back to the Watch app. watchNotificationUpdateData; userInfo = {     action = giveMeUpdatedItems; } That bit of code in the iOS app checks whether we can send data to the Watch app, and doesn't send the data because: WCSession.isSupported = YES theDelegate.session.isPaired = YES theDelegate.session.watchAppInstalled = NO theDelegate.session.activationState = Activated If I remove the check for watchAppInstalled, I get this: Error sending Watch application context: Watch app is not installed. {     NSLocalizedDescription = "Watch app is not installed.";     NSLocalizedRecoverySuggestion = "Install the Watch app."; } I've deleted and reinstalled the app on both devices countless times. I've rebooted the devices, plus the Mac. I've reinstalled Xcode. I've cleaned builds. I've deleted DerivedData. And still it says the companion app isn't installed.
10
3
4.9k
Aug ’22
SF Symbol as Image in .accessoryInline Lock Screen widget not rendered
In the first image you can see my app at the top of the list, and Apple's Weather app widget. Both are showing the SF Symbol rendered correctly. In the second image the inline widget chosen is Apple's Weather app. The SF Symbol is correctly shown. In the third image my app's inline widget has been chosen, but the image is not rendered correctly; it's just a block. Is there something I have to do to my image to have it rendered correctly?
1
0
963
Aug ’22
Can you use different descriptive text for adding Home Screen & Lock Screen widgets?
My app lets you create a list of items and pick one as the main item. For Home Screen widgets there are two bits of text you can use in the panel that appears when you want to add the widget: public var body: some WidgetConfiguration { IntentConfiguration(kind: "myWidgetKind", intent: DynamicSelectionIntent.self, provider: Provider()) { entry in MyWidgetEntryView(entry: entry) } .configurationDisplayName("Title") .description("Description") So, for a Home Screen widget the panel displays the title and description, and the preview shows the main item. Once you've added the widget you can edit it and pick a different item, so the title is "Display an Item" and the description is quite general, telling you what the widget can display. The title and description are also displayed in the panel when you want to add a Lock Screen widget: For the inline Lock Screen widget, you see "Title". For rectangular and circular widgets below the clock, you see the usual Home Screen panel, so both "Title" and "Description". There's no way of editing the item that sits behind the Lock Screen widget once you've added it, so the general text needs to be more specific and refer to the main item. How do you give different title and description text when you're adding a Lock Screen widget?
1
0
1.1k
Aug ’22
Can't get timers to work properly in widgets
Hi all. I have a timer working in a view on the Watch app, but I just can't get them working in widgets. Can you use timers in Home Screen & Lock Screen widgets? I can't find anything that says you can't... Take this code: struct ScratchPadView: View { @State var backgroundGradient: LinearGradient = gradientOne let gradientTimer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() var body: some View { let date: Date = getDateFromString("20220828 10:00") ZStack { backgroundGradient .onReceive(gradientTimer) { _ in self.backgroundGradient = (date >= Date()) ? gradientOne : gradientTwo } } } } All this is supposed to do is change the gradient in the background from gradientOne to gradientTwo when the current date is after the date supplied. Doesn't work. The date can come and go and the gradient never changes from what it was initially set up to use: gradientOne. Like I say, this works fine in a Watch app View, but just doesn't work in a widget. Is the only alternative to provide a timeline entry for that date so the widget refreshes and notices that the date has now passed?
0
0
817
Aug ’22
How to refresh a View?
In my Watch app, the WatchDelegate class is the WCSession delegate and handles transferring data with the iOS app. When the delegate receives some data it stores it in the UserDefaults. When the Watch app is launched it reads the existing data stored in the defaults and creates the view. I have a WatchApp file which contains this: @main struct WatchApp: App { var body: some Scene { WindowGroup { if(getItems().count == 0) { NoEventsView() } else { ItemsListView(available: getItems()) } } } } As you can see, if there are no events in the defaults it shows the NoEventsView; and if there are some it shows the EventsListView. When the Watch delegate receives a change in the events, I need to refresh this view. The delegate can receive zero or more events. How on Earth do I do that? In iOS I could call a method to reload a table of data, or post a notification to another view controller to do that, but in the Watch and with SwiftUI there doesn't seem to be any obvious way of refreshing a view. Is there any way of telling the App struct to refresh, or a particular view? For example, if I extracted the if statement into its own "struct WhichView: View", could I tell that to refresh? I've read a LOT on the net these past few days on @State vars, @ObservedObject, @Published etc, but nothing I've seen works, or is far too weird and complex for my situation. I literally just want WatchApp or WhichView to redraw when I tell it to. How do I do that? Thanks.
4
1
2.4k
Aug ’22
I'm confused about WatchKit and WidgetKit and Watch Apps... Help!
Sorry for the length of this post, and all the questions. I have an iOS app written in Objective-C (too big to convert to Swift right now), and I successfully added a WatchKit app and WatchKit Extension some years ago. I also added Home Screen widgets when iOS 14 was released. With the iOS 16 betas I'd like to support Lock Screen widgets, and have also decided to move the WatchKit app/extension to SwiftUI. User journey: MyApp is launched and an item with an image is created. This item is stored in Core Data and its image is stored in a directory in the app's documents directory. A version of it is stored in NSUserDefaults in a shared app group. The user adds a Home Screen widget to show that item. Its data is pulled from the defaults, and the image is loaded from the document's directory. The user installs the Watch app and launches it. The Watch app looks inside the user defaults to retrieve the item. The iOS app sends the image to the Watch and the Watch app stores it locally (it's a small image, taking up barely a few Kb). This all works fine right now. Currently I have these targets: MyApp = main iOS app. MyApp WatchKit = Watch app storyboards and asset catalogs. MyApp WatchKit Extension = code to update the Watch interface. MyApp Widget = Home Screen widgets. MyApp IntentHandler = dynamic intents handler for the Home Screen widgets. Q1. Where do I put the code for the Lock Screen widgets? I figure these go into the My App Widget target because they're widgets and appear on the iPhone? In this video (https://developer.apple.com/videos/play/wwdc2022/10050) at 07:00 it tells you to duplicate the existing Widget target, change the bundle, change it to run on watchOS and embed it in your existing Watch App. As my original Watch App is written in Objective-C (MyApp WatchKit Extension, above) I can't/shouldn't do that, so... Q2. I think I have to create a new MyApp Watch App target, and perform the video steps on that target? I can create the views for that app, no problem. Most people update to the latest watchOS, and it's only now that watchOS 9 won't support Watch Series 3. Q3. Do I need to keep MyApp WatchKit and MyApp WatchKit Extension around? I can support older versions of watchOS if it helps my users, but they'll probably want to use the new version of watchOS, right? Can you install both versions of the app on your Watch (with watchOS 9), or does the new Swift app override the old WatchKit extension? Q4. Once I've designed the new Watch App's views in SwiftUI to replace the old WatchKit extension, where do I put the code for the complications that are being replaced? The WWDC 2022 videos (above, and a couple linked to on that page) have confused me a bit. Do I put complications views in the new MyApp Watch App target along with the other views that replace the old Watch app, or in MyApp Widget? Q5. The MyApp Widget target contains a bunch of code (WidgetUtils.swift) that populates the Home Screen widgets (and the new Lock Screen widgets), and it would fit right into the new MyApp Watch App target. Can I share that code between the two app targets just by adding WidgetUtils.swift to both target's membership? MyApp sends small images to the current MyApp WatchKit Extension. There's no code in the existing WidgetUtils.swift to handle file transfers because the images for the Home Screen widgets are pulled from the iOS app, so I need to write that in Swift for the new MyApp Watch App. The logic is already there in the old target, but I can't see any sort of equivalent to the WatchKit extension delegate where I currently handle the file transfers. Q6. Where does that go in MyApp Watch App? The existing MyApp WatchKit Extension occasionally asks MyApp for some new data. This is all done in the extension delegate which wakes up the iOS app and updates the NSUserDefaults which the extension then reads from. Q7. How do you do that in MyApp Watch App? Don't be afraid to be verbose in your responses. The more detail the better! Thank you in advance.
4
0
3.8k
Aug ’22