WidgetKit

RSS for tag

Show relevant, glanceable content from your app on iOS and iPadOS Home Screen and Lock Screen, macOS Desktop, Apple Watch Smart Stack and Complications, and in StandBy mode on iPhone.

Posts under WidgetKit tag

200 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

WidgetKit Not running in iOS 14/15 (FBSOpenApplicationServiceErrorDomain)
I was adding WidgetExtension target for my old project. The widget target is running fine in iOS 17, In my case, widget need to support iOS 14 and above, so I updated my widget code to old style, To removing WidgetConfigurationIntent and AppIntentTimelineProvider. import WidgetKit import SwiftUI struct Provider: TimelineProvider { func placeholder(in context: Context) -> SimpleEntry { SimpleEntry() } func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) { completion(SimpleEntry()) } func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) { completion(Timeline(entries: [SimpleEntry()], policy: .never)) } typealias Entry = SimpleEntry } struct SimpleEntry: TimelineEntry { let date: Date = Date() } struct NCWidgetEntryView : View { var entry: Provider.Entry var body: some View { VStack { Text("Time:") Text(entry.date, style: .time) } } } struct NCWidget: Widget { let kind: String = "NCWidget" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: Provider()) { entry in NCWidgetEntryView(entry: entry) } .configurationDisplayName("DisplayName") .description("description") } } In my case, the code was working fine in iOS 17 simulator, But if I try to run in iOS 15, it returns the below error code-SendProcessControlEvent:toPid: encountered an error: Error Domain=com.apple.dt.deviceprocesscontrolservice Code=8 "Failed to show Widget 'com.name-pprd.NCWidgetExtension' error: Error Domain=FBSOpenApplicationServiceErrorDomain Code=5 "The request to open "com.apple.springboard" failed." UserInfo={NSLocalizedDescription=The request to open "com.apple.springboard" failed., NSLocalizedFailureReason=Unexpected error type., NSUnderlyingError=0x600003570b40 {Error Domain=BSServiceConnectionErrorDomain Code=3 "XPC error received on message reply handler" UserInfo={BSErrorCodeDescription=OperationFailed, NSLocalizedFailureReason=XPC error received on message reply handler}}, BSErrorCodeDescription=InvalidResponse}." UserInfo={NSLocalizedDescription=Failed to show Widget 'com.name-pprd.NCWidgetExtension' error: Error Domain=FBSOpenApplicationServiceErrorDomain Code=5 "The request to open "com.apple.springboard" failed." UserInfo={NSLocalizedDescription=The request to open "com.apple.springboard" failed., NSLocalizedFailureReason=Unexpected error type., NSUnderlyingError=0x600003570b40 {Error Domain=BSServiceConnectionErrorDomain Code=3 "XPC error received on message reply handler" UserInfo={BSErrorCodeDescription=OperationFailed, NSLocalizedFailureReason=XPC error received on message reply handler}}, BSErrorCodeDescription=InvalidResponse}., NSUnderlyingError=0x600003570bd0 {Error Domain=FBSOpenApplicationServiceErrorDomain Code=5 "The request to open "com.apple.springboard" failed." UserInfo={NSLocalizedDescription=The request to open "com.apple.springboard" failed., NSLocalizedFailureReason=Unexpected error type., NSUnderlyingError=0x600003570b40 {Error Domain=BSServiceConnectionErrorDomain Code=3 "XPC error received on message reply handler" UserInfo={BSErrorCodeDescription=OperationFailed, NSLocalizedFailureReason=XPC error received on message reply handler}}, BSErrorCodeDescription=InvalidResponse}}} Domain: DTXMessage Code: 1 User Info: { DVTErrorCreationDateKey = "2024-07-20 17:30:58 +0000"; } -- System Information macOS Version 14.5 (Build 23F79) Xcode 15.0.1 (22266) (Build 15A507) Timestamp: 2024-07-20T23:00:58+05:30 In widget target Minimum target is 14.0 In App target Minimum target is 13.0
1
0
109
1d
onContinueUserActivity not working on first app launch
HI, I'm trying to deeplink from my widget to a view in my app. I'm using the "SwiftUI app lifecycle". This the code I'm currently using: var body: some Scene { WindowGroup { RootView() .onContinueUserActivity("NextDeparturesWidgetConfigurationIntent") { userActivity in guard let configuration: NextDeparturesWidgetConfigurationIntent = userActivity .widgetConfigurationIntent(), let stationEntity = configuration.stationEntity else { return } NotificationCenter.default.post(name: .onOpenStation, object: stationEntity.id) } } } It is working fine when the app is already running (in the background). However when the app is "cold starting" (ie. not in memory) onContinueUserActivity is never called. I tried adding a UIApplicationDelegateAdaptor: func application( _ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions ) -> UISceneConfiguration { if let userActivity = options.userActivities.first { let configuration: NextDeparturesWidgetConfigurationIntent? = userActivity.widgetConfigurationIntent() // casting fails } return UISceneConfiguration( name: nil, sessionRole: connectingSceneSession.role ) } I can see that the activity is received but it is never casted to NextDeparturesWidgetConfigurationIntent. What am I missing ?
0
0
58
1d
ControlConfigurationIntent won't open the app despite setting openAppWhenRun = true
I am working on building Control widgets for our app and have noticed that openAppWhenRun doesn't seem to work for any ControlConfigurationIntent. When attaching the debugger to the widget extension in a sample project, I see the following error: Unknown NSError The operation couldn’t be completed. (LNActionExecutorErrorDomain error 2018.) This is reproducible as of Xcode 16.0 Beta 2 and Beta 3. I have noted that using an OpenIntent, with a parameter called target that conforms to AppEnum seems to open the app properly, but if I use that workaround, adding any additional parameters to the OpenIntent seems to break things again. Are others seeing this issue? I have feedback FB14357691. Here's some sample code below to reproduce: var body: some ControlWidgetConfiguration { AppIntentControlConfiguration(kind: "Open Any Screen", intent: OpenAppScreenIntent.self) { template in ControlWidgetButton(action: template) { Label { Text("Open App") } icon: { Image(systemName: "calendar") } }.tint(.red) } } } enum AppScreen: CaseIterable { case calendar case campus case search var title: String { switch self { case .calendar: "Calendar" case .campus: "Campus" case .search: "Search" } } } struct OpenAppScreenIntent: ControlConfigurationIntent { static var title: LocalizedStringResource = "Open app to a screen" static var description = IntentDescription("Opens the app.") /// The app should open regardless of what happens here static let openAppWhenRun: Bool = true @Parameter(title: "Screen", optionsProvider: OsuScreenOptionsProvider()) var screen: String? struct OsuScreenOptionsProvider: DynamicOptionsProvider { func results() async throws -> ItemCollection<String> { var screenTitles: [String] = [] for screen in AppScreen.allCases { async let title = screen.title await screenTitles.append(title) } return ItemCollection { ItemSection(items: screenTitles.map { IntentItem($0)}) } } func defaultResult() async -> String? { return "Campus" } } @MainActor func perform() async throws -> some IntentResult { #warning("The app should open regardless of what happens in this method, but it doesn't") return .result() } }
5
0
124
3d
"Unknown extension process" is displayed after app updating
Hello. In iOS 17, after updating the app, when trying to "Edit Widget" from a long press on the widget, "Unknown extension process" is displayed and the widget cannot be edited. At this time, the widget becomes completely white (or completely black), and it cannot be fixed without restarting the iPhone. This issue occurs sporadically on some devices. The implementation uses AppIntentTimelineProvider. Does anyone know a solution, workaround, or the cause of this problem? Thank you.
0
0
101
5d
ControlWidget in iOS 18 Beta not showing and the widget created prior to iOS18 Beta not showing too.
I tried creating a ControlWidget following this Apple document - https://developer.apple.com/documentation/widgetkit/creating-controls-to-perform-actions-across-the-system, but for some reason the Control is not showing while I am trying to add and on top of that the widget which we created prior to iOS18 is also not showing, while trying to add. Here is the gist of code : struct WidgetLauncher{ static func main() { if #available(iOSApplicationExtension 18.0, *) { appWidgetsFor18.main() } else { appWidgets.main() } struct apptWorkWidgets: WidgetBundle { var body: some Widget { WidgetPriorToiOS18() } } @available(iOSApplicationExtension 18.0, *) struct appWidgetsFor18: WidgetBundle { var body: some Widget { WidgetPriorToiOS18() PerformActionButton() //This from the apple's document. } } @available(iOSApplicationExtension 18.0, *) struct PerformActionButton: ControlWidget { var body: some ControlWidgetConfiguration { StaticControlConfiguration( kind: "com.example.myApp.performActionButton" ) { ControlWidgetButton(action: PerformAction()) { Label("Perform Action", systemImage: "checkmark.circle") } } .displayName("Perform Action") .description("An example control that performs an action.") } } struct PerformAction: AppIntent { static let title: LocalizedStringResource = "Perform action" func perform() async throws -&gt; some IntentResult { // Code that performs the action... return .result() } }
0
0
102
4d
WidgetBundle with ControlWidget does not work on iOS 17
I'm trying to add a ControlWidget to my WidgetBundle like this: struct MyWidgets: WidgetBundle { var body: some Widget { if #available(iOSApplicationExtension 18.0, *) { LauncherControl() } MyLiveActivityWidget() HomeScreenWidget() LockScreenWidget() } This works exactly as expected on iOS 18. However on iOS 17 my app seems to have no widgets at all. The workaround described here (https://www.avanderlee.com/swiftui/variable-widgetbundle-configuration/) does not work either since WidgetBundleBuilder.buildBlock does not accept ControlWidget as an argument. What is the correct way to include a Control widget conditionally on iOS 18?
1
0
273
5d
How should a Widget communicate with the main app's Audio objects
If I have an interactive widget, with button that triggers an AudioPlaybackIntent, how should the intent communicate with the main app's audio player to pause/play? I was thinking about instantiating the player in the Widget explicitly in its lifecycle. But, if the audio player gets instantiated separately in the app and in the widget, wouldn't I have two players running? Or am I misunderstanding the setup?
0
0
129
1w
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
171
1w
Updating watchOS complication when data in iOS app changes.
Hello, I'm facing problems when attempting to update my watchOS complication when relevant data on the iPhone app changes. From what I gather reading the documentation I have to use the Watch Connectivity Framework to send said new data from the phone to the watch: use transferCurrentComplicationUserInfo() to send a dictionary of data from the phone to the watch implement the didReceiveUserInfo delegate method to handle incoming data on the watch in said handler, save the incoming data to UserDefaults using an App Group so the widget-extension can read that data after saving the data to UserDefaults, call WidgetCenter.shared.reloadAllTimelines() so watchOS can request fresh timelines for my complications change the getTimeline() method of my TimelineProvider so it uses the received data from UserDefaults OR async fetch fresh data if received data from phone is too old If I understand correctly, transferCurrentComplicationUserInfo() is limited to be used a maximum of 50 times a day. I'm running the apps in debug mode, so this should be no problem. Here is my current implementation: 1 : Setup of my WC class: final class Connectivity: NSObject { // singleton approach static let shared = Connectivity() // used to rate limit transmissions from phone → watch private var lastSentBalanceContext: Date? = nil private override init() { super.init() // no need to check availability on watchOS #if !os(watchOS) guard WCSession.isSupported() else { return } #endif WCSession.default.delegate = self WCSession.default.activate() } } 2 : The method enabling transmission from phone to watch: #if os(iOS) extension Connectivity: WCSessionDelegate { func sendBalanceContext(sample: HealthData) { guard WCSession.default.activationState == .activated else { return } guard WCSession.default.isWatchAppInstalled else { return } // rate limitat transmissions guard self.lastSentBalanceContext == nil || abs(Date.now.timeIntervalSince(self.lastSentBalanceContext!)) > 10 else { return } if WCSession.default.remainingComplicationUserInfoTransfers > 0 { WCSession.default.transferCurrentComplicationUserInfo([ "context": "balance", "date": sample.date, "burnedActive": sample.burnedActive, // more data... ]) self.lastSentBalanceContext = .now } } // boilerplate handlers here } #endif 3 : Delegete method that handles incoming data on the watch: #if os(watchOS) extension Connectivity: WCSessionDelegate { func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) { guard let context = userInfo["context"] as? String, context == "balance" else { return } guard let date = userInfo["date"] as? Date, let burnedActive = userInfo["burnedActive"] as? Int /* more data... */ else { return } guard let SharedDefaults = UserDefaults(suiteName: "group.4DXABR577J.com.count.kcal.app") else { return } // TimelineProvider uses this to determine wether to use this data or fetch data on its own SharedDefaults.set(Date.now, forKey: "lastReceivedBalanceContext") SharedDefaults.set(date, forKey: "date") SharedDefaults.set(burnedActive, forKey: "burnedActive") // more data... WidgetCenter.shared.reloadAllTimelines() } // boilerplate handlers } #endif 4 : Finally, the TimelineProvider: struct HealthDataEntry: TimelineEntry { let date: Date let data: HealthData } struct HealthDataTimelineProvider: TimelineProvider { // other callbacks here func getTimeline(in context: Context, completion: @escaping (Timeline<HealthDataEntry>) -> Void) { let SharedDefaults: UserDefaults = UserDefaults(suiteName: "group.4DXABR577J.com.count.kcal.app")! let nextUpdateDate = Calendar.current.date(byAdding: .minute, value: 15, to: .now)! // use data from phone if it is less than 60 seconds old if let lastReceivedBalanceContext = SharedDefaults.object(forKey: "lastReceivedBalanceContext") as? Date { let interval = lastReceivedBalanceContext.timeIntervalSince(.now) if interval > -60 && interval <= 0 { let data = HealthData(date: SharedDefaults.object(forKey: "date") as? Date ?? Date(timeIntervalSinceReferenceDate: 0), burnedActive: SharedDefaults.integer(forKey: "burnedActive"), burnedActive7: SharedDefaults.integer(forKey: "burnedActive7") /* other data ... */) let timeline = Timeline( entries: [HealthDataEntry(date: .now, data: data)], policy: .after(nextUpdateDate) ) completion(timeline) return } } // default: fetch from HealthKit (if received data from phone is > 60s) Task { let timeline = Timeline( entries: [HealthDataEntry(date: .now, data: try! await asyncFetchData())], policy: .after(nextUpdateDate) ) completion(timeline) } } } The issue I am facing is that the watchOS complication only gets refreshed when I acitvely build and run the watchOS app in Xcode and then initiate a transmission of data to the watch. This works even if I do it back to back to back. As soon as I stop the watchOS app from within Xcode, my complications won't update anymore. I noticed this behavior when I used print() statements throughout my code to see whether it is beeing executed as expected. The iPhone sends data, the watch receives it but then the watch fails to update the complications ONLY when not running from Xcode. Can you spot any flaws in my implementation or in my understanding? Maybe transferCurrentComplicationUserInfo() just isn't as reliable as I think it should be? I interpreted it as being practically guaranteed to refresh the complications 50 times a day, pretty much instantly? Any help or guidance would be greatly appreciated!
1
0
208
1w
[Live Activity / Dynamic Island] Countdown Timer issues (ending activity, styling)
Hello! I'm building a Countdown Timer for the Dynamic Island using a Live Activity. I have two issues for which I can't find any solution: We want to display the time in the "X minutes" format, like in this example. I went through the forum but all the answers were wrong, because they were using a Text(:format) which never updates in the live activity, or a Text(:timerInterval) which we can't format. I want the Live Activity to end once the timer gets to zero. I found this staleDate parameter that I thought would helped, but is actually only adding loaders on my design once the date is reached. I tried to implement a solution like the answer of this post, but the if (context.isStale) {...} part is never being rendered. It also looks like the stale sate gets activated only when the app is focus again. I tried several fixes, went through a lot of forum and posts, but I can't find any solution. Thanks!
0
0
147
1w
Non-tinted image in complications using WidgetKit
I have a watchOS app where a user can select a picture. I’d like the picture to be displayed in a complication. I’m using WidgetKit, and I found out that for some watch faces (rendering mode = accented), the image gets tinted. Instead of the picture, the user sees only a colored box. It appears that using the old framework, ClockKit, it was possible to display an image that gets slightly colored with the tint color on tinted watch faces. I believe this is to ensure backward compatibility of old complications. My question is: can I do the same using WidgetKit? I tried using the widgetAccentable() modifier, but it doesn’t work. Here's an example of what I mean. In the middle complication, instead of the pink square, I'd like to display the user picture.
5
1
249
1w
iOS 18 Control Widget that opens a URL
I already have an iOS 17 App Intent that works with a URL: @available(iOS 16, *) struct MyAppIntent: AppIntent { static let title : LocalizedStringResource = "My App Inent" static let openAppWhenRun : Bool = true @MainActor func perform() async throws -> some IntentResult{ await UIApplication.shared.open(URL(string: "myapp://myappintent")!) return .result() } } Now, with iOS 18 and Control Widgets, I want to create a Control Widget button that smply opens the app with the same URL. However UIApplication code is not allowed within extensions. For this, Apple says to use OpenIntent which is shown here: Link Apple Sample Code from the link: import AppIntents struct LaunchAppIntent: OpenIntent { static var title: LocalizedStringResource = "Launch App" @Parameter(title: "Target") var target: LaunchAppEnum } enum LaunchAppEnum: String, AppEnum { case timer case history static var typeDisplayRepresentation = TypeDisplayRepresentation("Productivity Timer's app screens") static var caseDisplayRepresentations = [ LaunchAppEnum.timer : DisplayRepresentation("Timer"), LaunchAppEnum.history : DisplayRepresentation("History") ] } WWDC session video about this does not cover this particular method in detail and also this sample code is a bit confusing. So how can I alter this code to just open the app with a URL?
0
0
334
2w
iOS18, interactive widget not respond (AppIntent not working)
here is my case: i add the AppIntent to both your app and widget extension targets. the intent will run my app process when app is running. it works perfectly on iOS 17. but iOS 18, my app process never called. i download app's demo, https://developer.apple.com/documentation/widgetkit/emoji-rangers-supporting-live-activities-interactivity-and-animations it looks like the same issue. it runs well because even it runs in the widget extension target, it still can present expected UI. but in real case, we need to run the app process to do some work. by debugging, i found the app process never called(set breakpoint). i add openAppWhenRun, it works well on iOS 18. but it will open the app when the widget is running. it is not what i want.
1
1
256
1w
openAppWhenRun makes AppIntent crash when launched from Control Center.
Adding the openAppWhenRun property to an AppIntent for a ControlWidgetButton causes the following error when the control is tapped in Control Center: Unknown NSError The operation couldn’t be completed. (LNActionExecutorErrorDomain error 2018.) Here’s the full ControlWidget and AppIntent code that causes the errorerror: Should controls be able to open apps after the AppIntent runs, or is this a bug?
3
1
346
1w