Post

Replies

Boosts

Views

Activity

Swift Charts: Jumpy, unpredictable chart when using .chartXSelection(value:)
I'm seeing an issue related to Swift Charts when I use a @Binding to dynamically change the selection of the chart. Below is a quick example View that demonstrates the issue. This example is a chart that shows a count of events that happened in a day, and the user can touch a bar to reveal the count as an annotation above the bar. The expected behavior is that as the user can touch a bar and see the annotation appear above the bar. In some cases, the chart scale may need to change to allow space for the annotation, which is expected. However, unexpectedly, the whole chart's width changes unexpectedly by a few points, sometimes, when touching a bar. It would be fine if this was consistent with times when the updated scale needs to include an additional digit, however even when that's not the case the whole chart's width seems to change by a few points, and then inconsistently sometimes change back (or not) with subsequent touches. This behavior is not present in all cases, so you may need to run the preview a few times to get data where it's reproducible. Is there something I can do here to fix the issue? Or is it just a bug in Swift Charts? import SwiftUI import Charts struct ContentView: View { var body: some View { VStack { WeekHistogram() .frame(maxHeight: 180) } .padding() } } #Preview { ContentView() } // A simple struct to contain the data for the chart. public struct EventCount: Identifiable { /// Start date of a day let date: Date /// Counts of events on that day public let count: Int /// The ID: date stored as a string public var id: String { date.ISO8601Format() } // Storing a Date as the ID changes how Swift Charts lays them out along the axis to an undesired way. init(day: Date, count: Int) { self.date = day self.count = count } } struct WeekHistogram: View { // Dummy data that gets refreshed every time you run the preview private let countByDay: [EventCount] = EventCount.dummyData // Used to extract the date back from the EventCount.id private let formatter = ISO8601DateFormatter() // The currently selected bar (while user is touching the bar) @State private var selection: String? = nil var body: some View { Chart(countByDay) { element in // X-axis: Date of the event count // Y-axis: Count of events on that date BarMark( x: .value("Day", element.id), y: .value("Count", element.count) ) .annotation(position: .top) { // Only show the annotation when this bar is being touched if element.id == selection { Text("\(element.count)") .font(.headline) } } } .chartXSelection(value: $selection) .chartXAxis { // Custom view to show the weekday and day of the month // Removing this custom .chartXAxis does not fix the issue AxisMarks { value in // Convert the text of the date back to an actual date let date = formatter.date(from: value.as(String.self)!)! AxisValueLabel { VStack { Text(date.formatted(.dateTime.weekday())) Text(date.formatted(.dateTime.day())) .font(.headline) } } } } } } // Generate dummy data extension EventCount { static let dummyData: [EventCount] = { let startOfToday = Calendar.current.startOfDay(for: .now) let secondsPerDay = 24 * 60 * 60 // Generate [EventCount] with a random count from 3–8 for toady and each of the past 7 days. var counts = [EventCount]() for i in 0...7 { let day = startOfToday .addingTimeInterval(TimeInterval(-i * secondsPerDay)) let count = Int.random(in: 3...8) let eventCount = EventCount(day: day, count: count) counts.append(eventCount) } // Reverse the direction to order it for the chart counts.reverse() return counts }() }
0
0
729
Nov ’23
Action Button on Apple Watch Ultra 2 calls shortcut twice
Has anyone else seen this issue? When the Action Button on an Apple Watch Ultra 2 is connected to a Shortcut, it seems to run the shortcut twice. I'm on watchOS 10.0.2. A user of an app I built reported the issue, which is how I knew about it in the first place. I'm wondering if it's an issue on my watch specifically, or if many other people are seeing the same thing. I replicated the issue using a fresh project, and it only seems to happen when the shortcut responds with dialog. Does anyone know why this is happening and how to fix it? The shortcut with a dialog response works fine everywhere else, and only exhibits this behavior when run with the Action Button. Here is the full code with instructions to replicate the issue, so I'm curious if other people see the same thing: // When running a shortcut that returns dialog // with the Apple Watch Ultra Action Button, // the shortcut runs twice. // Create a new iOS project and add this: import AppIntents // AppIntent struct LogEventNow: AppIntent { static var title: LocalizedStringResource = "Log an Event" @MainActor func perform() async throws // -> some IntentResult // When just returning a successful result with .result(), // the shortcut only runs once as expected // Add ProvidesDialog to be able to return .result(dialog:) -> some IntentResult & ProvidesDialog { let loggedDate = Date() let formattedLoggedDate = loggedDate.formatted(date: .omitted, time: .complete) // Print the time of the event that was logged print(formattedLoggedDate) // This gives the expected result // Shortcut runs once, and prints one time to the console // return .result() // This gives unexpected result // Shortcut seems to run twice // and prints two date a 1–2 seconds apart return .result(dialog: "Successfully logged.") } } // AppShortcut // This makes it show up in the Shortcuts app struct EventShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: LogEventNow(), phrases: ["Log in \(.applicationName)"], shortTitle: "Log Event Now", systemImageName: "calendar" ) } } // Steps to reproduce the issue: // - Long press the AppShortcut in Shortcuts app // - Tap "Add to Shortcut" // - Tap the "i" at the bottom // - Turn on "Show on Apple Watch" toggle // - Open the Watch app on iPhone // - Tap "Action Button" // - Under "Action" choose "Shortcut" // - Select the Shortcut that was created in the Shortcuts app // - Push the Action Button and observe two dates printed to the console // Sometimes I'm also seeing "Remote execution timed out" on Apple Watch a minute or so after the shortcut has run, but only when leaving the Shortcuts app open after it runs from the Action button.
1
0
943
Oct ’23
How can I re-create a CLKRelativeDateTextProvider in a WidgetKit complication?
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
4
0
938
Sep ’23
Title and sashColor not showing up on customized long-look notification with WKUserNotificationHostingController
I am building custom long-look notifications in an Apple Watch app, but for some reason the title defined in the notification's UNMutableNotificationContent is not showing on the long-look notification, and the custom sashColor I’m defining is not used. In the “Transition to the Long-Look Interface” section of Presenting Notifications on Apple Watch, there is a screenshot that shows what I would expect to see: a title for the notification in the sash, and a custom sash color. I built an example app (code below) to isolate the issue. Here is a screenshot of the notification in my app: I expect to see the title (“Take Action!”) where the line is, and the sashColor as the background color for the circled region, based on my code. The short-look of the notification does show the title briefly before it transitions to the long-look (it was hard to get a good screenshot, but here is one as it was animating into the long-look): Showing or hiding the notification title is not mentioned anywhere that I can find in the documentation, so I expect that to show up automatically since it’s part of the notification. For the sashColor override, I referred to Customizing Your Long-Look Interface. Is there something else specific I need to do to show the title on my customized long-look notification, and get sashColor to work? Example App To recreate the issue, create a watchOS app with companion iOS app in Xcode. I called it CustomWatchNotifications. I updated the main iOS app file to this, with a simple class to request notification permission and send a test notification, which gets passed into the view: import SwiftUI import UserNotifications @main struct CustomWatchNotificationsApp: App { let notifications = NotificationController() var body: some Scene { WindowGroup { ContentView(notifications: notifications) } } } class NotificationController { func requestPermissions() { Task { try await UNUserNotificationCenter.current() .requestAuthorization( options: [.alert, .sound]) } } func scheduleNotification() { let content = UNMutableNotificationContent() content.title = "Take Action!" content.categoryIdentifier = "takeActionCategory" content.sound = .default // Schedule a new notification 5 seconds from now, // so there is enough time to lock the phone screen // to deliver notification to Apple Watch. let trigger = UNTimeIntervalNotificationTrigger( timeInterval: 5, repeats: false) let request = UNNotificationRequest( identifier: "takeAction", content: content, trigger: trigger) UNUserNotificationCenter.current() .add(request) } } This is the ContentView for the iOS app, which just includes the two buttons: import SwiftUI struct ContentView: View { let notifications: NotificationController var body: some View { NavigationView { Form { // Request notification permissions Button { notifications.requestPermissions() } label: { Text("Request Notification Permissions") } // Schedule notification Button { notifications.scheduleNotification() } label: { Text("Schedule Notification") } } } } } On the watchOS side, I updated the main app file to include a custom View for the notification, inside a WKUserNotificationHostingController for this specific notification category: import SwiftUI import UserNotifications @main struct CustomWatchNotifications_Watch_AppApp: App { var body: some Scene { WindowGroup { ContentView() } WKNotificationScene( controller: TakeActionNotificationController.self, category: "takeActionCategory" ) } } struct TakeActionNotificationView: View { var body: some View { Text("This is a test.") } } class TakeActionNotificationController: WKUserNotificationHostingController<TakeActionNotificationView> { // This does not seem to have an effect on sashColor. override class var sashColor: Color? { return .red } override var body: TakeActionNotificationView { return TakeActionNotificationView() } // This has to be here for custom notification to show up. override func didReceive(_ notification: UNNotification) {} } When you build and run on real devices, make sure the watchOS app is installed before schedule the test notification. Once you schedule the test notification, lock the iPhone screen immediately so the notification gets delivered to Apple Watch.
0
0
883
Nov ’22
How do I read Apple Watch battery level in iPhone app without a companion Apple Watch app?
I’m trying to read the battery level of a connected Apple Watch on an iPhone app. The built-in iOS Batteries widget shows the current Apple Watch battery level, so I know the data is being sent from Apple Watch to iPhone frequently, however I could not find a public API to read the level in my iOS app. My best solution so far was to build an Apple Watch app that periodically sends the battery level of the Apple Watch to the iPhone using WatchConnectivity. Then, I found this app. It can show the battery level of my paired Apple Watch without ever installing an app on the Apple Watch. To try to accomplish this same thing, I have tried a few different approaches. First, I tried Core Bluetooth, but after a lot of exploring, troubleshooting, and asking at a Core Bluetooth lab, I’ve ruled that out since Apple Watch does not expose its battery information that way. I experimented with the External Accessory framework, but that doesn’t seem like the right approach either, since it's for MFi accessories and I couldn't find a way to get Apple Watch info with it. How is it possible to read the Apple Watch battery level in an iPhone app without sending it from a companion Apple Watch app? Or is the app I linked to probably using private APIs?
3
0
2.3k
Jun ’21
Firebase on Xcode 12 beta 3 not supported? Won't build
I have multiple projects where I use the Firebase/Analytics pod installed with CocoaPods. Everything builds fine on Xcode 12 beta 2, but when I try to build for a real device or simulator (instead of Any iOS Device) on Xcode beta 3, it fails. Is this a known issue that should be fixed in a future version of Xcode? Building on beta 3 takes a lot longer, and finally fails with an error that says library not found for -lFirebaseCore. I've tried updating, removing, and re-installing Firebase with CocoaPods, along with cleaning the build folder and deleting DerivedData, restarting the computer, and restarting Xcode, but nothing has helped.
4
0
4.3k
Jul ’20
Is there an equivalent to readableContentGuide in SwiftUI?
I'm migrating some views from UIKit to SwiftUI. In my UIKit interfaces, I frequently use the readableContentGuide property with Auto Layout to handle horizontal spacing across different screen sizes - https://developer.apple.com/documentation/uikit/uiview/1622644-readablecontentguide. Is there an equivalent in SwiftUI? I've been using .padding, but short of providing conditional numerical values for different devices, I haven't found a way to automatically make horizontal spacing adapt to different screen sizes to create naturally-readable spacing. Is this possible?
0
0
895
Jul ’20
Are SwiftUI complications only supported in some complication families?
My apps support almost all complication families. I'm looking for ways to improve all my existing complications and design new ones considering the new features in watchOS 7, especially with custom designs using SwiftUI. However, I'm confused about SwiftUI support across different complication families. Is it possible to use SwiftUI to build a custom complication that takes up the entire space allocated to the complication family for all complication families? Based on this session - https://developer.apple.com/videos/play/wwdc2020/10048/, I see that I can use CLKComplicationTemplateGraphicRectangularFullView, for example, along with edgesIgnoringSafeArea, to define a fully custom view that takes up the entire area allocated to a Graphic Rectangular family complication. However, I am not seeing analogous classes available for other families. For example, most users of my app use the Graphic Corner complication family. There are three new classes I see for that family: CLKComplicationTemplateGraphicCornerGaugeView CLKComplicationTemplateGraphicCornerCircularView CLKComplicationTemplateGraphicCornerTextView None of these new classes seems to allow replacing the entire area allocated to a Graphic Corner template to a SwiftUI View like CLKComplicationTemplateGraphicRectangularFullView does. Does that mean it's not possible to use SwiftUI to build custom complications for many families? CLKComplicationTemplateGraphicCornerCircularView seems the closest, but only takes up a small portion of the Graphic Corner space, so it's not a good fit for my apps, which use gauges for this family. CLKComplicationTemplateGraphicCornerGaugeView has nowhere to provide a View, but does accept a Label, however this is very limited and based on my testing is extremely buggy and doesn't actually work. It doesn't seem to allow for any more customization than CLKComplicationTemplateGraphicCornerGaugeText did, except maybe to add one SF Symbol, if it were working correctly. Are SwiftUI complications limited to only a subset of complication families? And if so, is there any documentation that shows which ones are supported? Also, since I said CLKComplicationTemplateGraphicCornerGaugeView is not working, here's my example code with an attempt to use it with a Label: struct Complication_Previews: PreviewProvider {     static let gaugeProvider = CLKSimpleGaugeProvider(         style: .fill,         gaugeColor: .white,         fillFraction: 0.69)     static var previews: some View {         CLKComplicationTemplateGraphicCornerGaugeView(             gaugeProvider: gaugeProvider,             label: Label("Test",                          systemImage: "iphone"))             .previewContext()     } } I can't seem to attach an image here, but if you run that code the preview will show the iPhone SF Symbol then "..." because the Test text gets truncated.
1
0
1.2k
Jul ’20