I have programmed a ControlWidgetToggle which controls a recording state in our main app, that can be on (recording) or off (not recording). The recording can also be controlled from inside the main app, and there we call ControlCenter.shared.reloadControls(ofKind: "") to update the ControlWidgetToggle state when the recording state changes. Now this all works great, except for the situation in which the user terminates the app (for example using the app switcher screen). Then of course the recording will stop. I however have no idea how to update the state of the ControlWidgetToggle in this case, it will stay in the recording state since it does not know that the recording has been stopped.
The applicationWillTerminate delegate method of the AppDelegate will not be called in such a case. Does someone has an idea how to update the state of the ControlWidgetToggle once the parent app has been terminated?
WidgetKit
RSS for tagShow 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
Hey all,
iOS 18 - RC
I have an app that supports both Widgets and ControlWidget, which resides on the same AppIntent.
The following sync works fine when any action is taken on any of the three players -
App - Widget - both directions - works as expected
App - Control Widget - both directions - works as expected
However -
Widget - ControlWidget - the UI not always sync in real time (the values are ok)
So if for instance I increase a counter on the widget from 1 to 2, the comtrolwidget will still show 1, but if I tap it, it will increase to 3.
For any update/action taken on the AppInten, I call -
WidgetCenter.shared.reloadAllTimelines()
ControlCenter.shared.reloadAllControls()
Any idea how to ensure this sync?
Thanks a lot!
Dudi
I have created an iOS 18 Control Center Widget that launches my app. However I am not able to detect it in AppDelegate.swift. I have created custom scheme funRun://beginRun in Target => Info => URL Types URL Types setup in Xcode
This method in AppDelegate.swift does not fire at all and I get this error in console:
Failed to open URL runFun://beginRun: Error Domain=NSOSStatusErrorDomain Code=-10814 "(null)" UserInfo={_LSLine=279, _LSFunction=-[_LSDOpenClient openURL:fileHandle:options:completionHandler:]}``
`
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
// this does not fire
print("Scheme \(url.scheme)")
print("Host \(url.host)")
return true
}`
I tried this answer to no avail: iOS 18 Control Widget that opens a URL
Even adding EnvironmentValues().openURL(url) as suggested here did not help.
@MainActor
func perform() async throws -> some IntentResult & OpensIntent {
let url = URL(string: "funRun://beginRun")!
EnvironmentValues().openURL(url)
return .result(opensIntent: OpenURLIntent(url))
}
Here is my extension code: My goal is to detect the url string from the request, so I can decide which screen to launch from AppDelegate's open url method. When I test this with iOS 18 RC it does not work either in simulator or on real device
import AppIntents
import SwiftUI
import WidgetKit
@available(iOS 18.0, watchOS 11.0, macOS 15.0, visionOS 2.0, *)
struct StartRunControl: ControlWidget {
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(
kind: "name.funRun.StartRun",
provider: Provider()
) { value in
ControlWidgetButton("Hello",
action: MyIntent()) { hi in
Label("Start", systemImage: "sun.min.fill")
}
}
.displayName("Start run")
.description("Opens a run screen.")
}
}
@available(iOS 18.0, watchOS 11.0, macOS 15.0, visionOS 2.0, *)
extension StartRunControl {
struct Provider: ControlValueProvider {
var previewValue: Bool {
false
}
func currentValue() async throws -> Bool {
let isRunning = true // Check if the timer is running
return isRunning
}
}
}
@available(iOS 18.0, watchOS 11.0, macOS 15.0, visionOS 2.0, *)
struct MyIntent: AppIntent {
static let title: LocalizedStringResource = "My Intent"
static var openAppWhenRun: Bool = true
init() {}
@MainActor
func perform() async throws -> some IntentResult & OpensIntent {
let url = URL(string: "funRun://beginRun")!
EnvironmentValues().openURL(url)
return .result(opensIntent: OpenURLIntent(url))
}
}
I even checked info.plist and it seems okay.
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>beginRun</string>
<key>CFBundleURLSchemes</key>
<array>
<string>funRun://beginRun</string>
</array>
</dict>
</array>
Does anyone know where the problem might be? Thanks!
Environment: Xcode16 RC, iOS14/iOS15
Bug description: When creating a Widget Extension in Demo and running, it will crash with the following error:
But if running on iOS16 or above, it is normal
AppLaunchTimeoutError: Failed to launch app ”x.app” in reasonable time
The app ”x.app” did not launch on ”iPhone 15 Pro” in 15 seconds.
it always show that error, when it show i have to apply this command on terminal
xcrun simctl --set previews delete all
but after 30 seconds, i am facing with same issue.
Hello.
We have multiple WidgetKit extensions in our application. in IOS18 there is option to transform app icon into widget by long press on icon.
Is there any way to setup default WidgetKit extension that icon will be transformed to?
We have minimum deployments set to iOS 15 in one of the app for both application and widget target.
When install app in simulator or real device using Xcode 15.4, it works fine in iOS15, iOS16, iOS17 and iOS18 beta.
When install app in simulator or real device, using Xcode 16 beta 6, it works fine in iOS16, iOS17 and iOS18 beta but in iOS 15, our app doesn't show in widget gallery.
Hello,
I'm preparing my app Tameno for iOS 18, adding a couple of Control Center widgets.
One of them allows users to select their favorite interval. But in the context of my app, only seconds and minutes make sense as a unit value - I don't need milli-pseconds, nor hours.
Is there a way restrict these available options that come from a ControlConfigurationIntent?
@available(iOS 18.0, iOSApplicationExtension 18.0, *)
struct TamenoFavoriteIntervalControlWidgetSetupIntent : ControlConfigurationIntent {
static let title: LocalizedStringResource = "cw_FavoriteIntervalSetup"
static let isDiscoverable: Bool = false
@Parameter(title: "cw_IntervalParameter", defaultValue: 5.0, defaultUnit: .seconds, supportsNegativeNumbers: false) var interval: Measurement<UnitDuration>?
@MainActor
func perform() async throws -> some IntentResult {
.result()
}
}
I am able to restrict it just to seconds or minutes only (by adding unit: .seconds, or unit: .minutes to the @Parameter setup), but I'd really like to offer both.
Thank you,
Matthias
Hello. I am working with the iOS 18b8 and Xcode 16b6 betas, updating a Live Activity Widget to adopt the new FormatStyle variants of the SwiftUI Text views.
We used to use:
Text(workoutStartDate, style: .relative)
and it seems with iOS 18 we can replace it with:
Text(workoutStartDate, format: .relative(presentation: .numeric, unitsStyle: .wide))
The former code would auto-increment, allowing a user to look at their Live Activity and see the duration of their workout so far ticking by. The new code does provide a nice relative duration string, but it doesn't auto-increment in the Live Activity's View – I need that functionality.
I also updated other Texts in the Live Activity's View to adopt the .timer and .stopwatch FormatStyles. Those auto-increment and update no problem – once I realized I needed to provide a TimeDataSource<Date>.currentDate and not a simple Date.
But in this .relative case, there is no auto-incrementing, and I'm guessing it may be due to providing a Date (workoutStartDate) and not a TimeDataSource<Date> as TimeDataSource seems to be the magic to make the Text auto-increment (or it's possible this format simply doesn't support auto-increment? bummer if so since the prior way did).
How can I have a TimeDataSource<Date> that vends my arbitrary date (workoutStartDate)?
I dug around, didn't find anything, but I wouldn't be surprised if I was overlooking something.
PS: I have tried other approaches to solve this, such as simply using a .timer or .stopwatch format. That would change the under experience under iOS 18, and we think degrade it (the relative textual representation is nicer and provides distinction from the .timer also in the same View). We could keep the former approach, despite the minor layout issues under iOS 18. I have considered additional approaches as well – and am totally open to more! Here tho, I am truly curious how one might provide a custom TimeDataSource anchored at a date we provide (perhaps dynamically). Thank you.
I have been trying to add a control widget to my app. Now I want to show some info on control widget that is received from push notification data. In the apple documentation, they have added a push notification handler, but the pushTokensDidChange method only has the information of the controls whose push tokens are changed. There is no information on where can I access the push notification data and how to use the push notification data to update the info being shown in the control widget.
When a widget uses a custom color from Assets catalog. Those colors seem to be ignored and rendered as pure white when used in a widget and the Home Screen is set into the Tinted mode.
This can be re-produced both in a Simulator and on a device and on the latest 18.1 beta (as of writing this post).
Occasionally, I was able to catch the widget to be rendered correctly after it had been on the Home Screen for some time, but I could not identify what caused it, just re-rendering the widget by pushing a new entry into the timeline or changing the configuration, does not help the issue, colors are still rendered white.
I've attached the screenshots with my widget rendered in tree different modes.
Has anyone also encountered this? I'm not sure if I need to do something extra to properly support tinted mode.
I have a widget that displays data which is fetched from the server asynchronously.
The widget contains a refresh button.
The data is refreshed either when the app goes to the background of when the user taps the refresh button (timeline policy is .never)
Since the request may take more than 2 seconds, I would like to display a redact effect until the data is returned. Lets say the timeline entry contains isRedact property.
I would like to see the following happen:
1a. user taps refresh button
1b. widget is redrawn with redact state do to entry.isRedact=true
2a. data is fetched asynchronously
2b. on return widget is redrawn without redact state do to entry.isRedact=false
From what I understand, the widget is re-drawn only as a result of getTimeline entries, so I don't understand how to make 1b happen, since 1b is an entry that can be created immediately but 2b entry can only happen async
Of course there might be another way to redraw a widget that I don't know.
i used ios 18 new feature about custom control widget, but this looks like a bug, look at these pictures, control center list won't show these icons when i used my app for a while, and control center page can show these icons
If you set the display and brightness settings in iOS to be in dark mode, then the value returned in the colorScheme environment value:
https://developer.apple.com/documentation/swiftui/environmentvalues/colorscheme
@Environment(.colorScheme) var colorScheme
is always dark, even if you toggle between light or dark in the Home Screen customization option. Tinted is reported correctly.
Is there some way to get the Home Screen customization light/dark/tinted value correctly? Swapping the Home Screen customization value between light/dark does swap between the light and dark app icon so it seems like the widget color scheme should also swap in this case.
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
}
}
In iOS18 the user can change their Home Screen customization to choose either light, dark, or tinted. If they choose tinted the widgets are rendered using the "accented" rendering mode and without a background.
Is there some way to override so that the tinted mode is ignore completely and the widgets render as full color?
I know about WidgetAccentedRenderingMode (https://developer.apple.com/documentation/widgetkit/widgetaccentedrenderingmode) but that's only for images, not the whole control and doesn't help with the background also being removed in the tinted mode.
Live Activity works only when the device is unlocked and open the app is the app live activity can start from remote via push notification
What advantages do Control Widgets offer over Interactive Widgets, besides their ability to be added to Control Center, Lock Screen, and the Action Button? As far as I understand, both types of widgets provide toggle and button actions to deep link the widget to key functions within the main app.
Hi,
We’ve developed a workout app with a Live Activity feature to help users launch the mirroring view on iPhone, similar to the built-in workout app for biking activities.
While Live Activities are now available on watchOS 11, the integration feels a bit off for our Workout app. Is there a way to disable or exclude our Live Activity from appearing on watchOS?
Currently, when a user starts a workout, the Live Activity appears at the bottom of the screen, requiring users to tap the screen before they can use our app. The built-in Workout app doesn’t have this issue.
Additionally, our Live Activity appears in the Smart Stack, duplicating content with the built-in Workout Live Activity.
We’re unsure if we missed any keys or settings to exclude Live Activity from watchOS.
I'm trying to implement push notifications to update Control Center Widget as described here:
https://developer.apple.com/documentation/widgetkit/updating-controls-locally-and-remotely#Use-push-notifications-to-reload-controls
I've made my handler
import WidgetKit
import CommonTools
@available(iOSApplicationExtension 18.0, *)
struct ControlCenterPushHandler: ControlPushHandler {
func pushTokensDidChange(controls: [ControlInfo]) {
print("pushTokensDidChange called. \(controls)")
controls.forEach{
print("ControlInfo \($0)")
}
}
}
And registered it for my controls
var body: some ControlWidgetConfiguration {
AppIntentControlConfiguration(
kind: WidgetConfig.Constants.controlCenterLock,
provider: Provider()) { value in
ControlWidgetToggle(
"Lock/Unlock",
...
}
.pushHandler(ControlCenterPushHandler.self)
}
}
and then run my software on an iPhone 15 pro running iOS 18 developer beta 7.
pushTokensDidChange is not called as I add and remove controls from the control panel. Or ever for that matter.
If I inspect push tokens when my currentValue code runs
struct Provider: AppIntentControlValueProvider {
func currentValue(configuration: ControlCenterButtonConfiguration) async throws -> Value {
let controls = try await ControlCenter.shared.currentControls()
controls.forEach{
print("ControlInfo \($0)")
}
pushInfo is always nil
ControlInfo ControlInfo(kind: "ControlCenterLock", pushInfo: nil, control:...
What steps are needed to get a push token?