Hi,
we start and update Live Activities with ActivityKit push notifications in our app, but want to do so only if the user is logged in. Therefore we only send the pushToStartToken to the server when a user logs in (or when the token changed and the user is still logged in.)
When the user logs out, we remove that start token from our server so that no LA can be started while the app is in the logged out state. This means that the logout isn't happening immediately but is waiting for that deletion request to succeed. This could also fail and lead to the use rnot being able to log out, e.g. if the user has no internet access.
If that deletion request would be fire and forget, we would end up in a state where the server still has the token and might start LAs without any user being logged in.
The token flow for Remote Push Notifications is different, on the other hand: requesting a token asynchronously via UIApplication.shared.registerForRemoteNotifications() but invalidating it synchronously (at least from the app's perspective) on logout via UIApplication.shared.unregisterForRemoteNotifications(), which makes it way easier for us to make sure the app does not get notifications when no user is logged in.
We're wondering if we're just holding it wrong or if our way of handling the LA token deletion is indeed the intended one?
Widgets & Live Activities
RSS for tagDiscuss how to manage and implement Widgets & Live Activities.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I'm building out a live activity that has a button which is meant to update the content state of the Live Activity. It calls a LiveActivityIntent that runs in the app process.
The push server starts my live activity and the buttons work just fine. I pass the push token back to the server for further updates and when the next update is pushed by the server the buttons no longer work. With the debugger I'm able to verify the app intent code runs and passes the updated state to the activity. However the activity never updates or re-renders. There are no logs in Xcode or Console.app that indicates what the issue could be or that the update is ignored.
I have also tried adding the frequent updates key to my plist with no change.
I'm updating the live activity in the LiveActivityIntent like this:
public func perform() async throws -> some IntentResult {
let activities = Activity<WidgetExtensionAttributes>.activities
for activity in activities {
let currentState = activity.content.state
let currentIndex = currentState.pageIndex ?? 0
let maxIndex = max(0, currentState.items.count - 1)
let newIndex: Int
if forward {
newIndex = min(currentIndex + 1, maxIndex)
} else {
newIndex = max(currentIndex - 1, 0)
}
var newState = currentState
newState.pageIndex = newIndex
await activity.update(
ActivityContent(
state: newState,
staleDate: nil
),
alertConfiguration: nil,
timestamp: Date()
)
}
return .result()
}
To sum up:
Push to start -> tap button on activity -> All good!
Push to start -> push update -> tap button -> No good...
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Tags:
APNS
Intents
App Intents
ActivityKit
Some of the users of my app reported that, the widgets cannot be loaded, event after restarts and re-installs. It seems that it is not rare, I have tens of reports on this (and probably much more who didn't report).
It seems the widget is blank even on the gallery screen while you are first adding it:
struct SingleWidgetProvider: AppIntentTimelineProvider {
@Environment(\.widgetFamily) var family
private let viewModel: WidgetViewModel = WidgetViewModel()
func placeholder(in context: Context) -> SingleEntry {
SingleEntry(date: Date(), habit: .singleSample)
}
func snapshot(for configuration: SingleWidgetConfigurationIntent, in context: Context) async -> SingleEntry {
guard let habit = Array(PersistenceManager.shared.retrieveHabitList().habits).first else {
return SingleEntry(date: Date(), habit: .singleSample)
}
let displayable = viewModel.displayableFromHabit(habit, separateComponents: true, secondOffset: 0)
return SingleEntry(date: Date(), habit: displayable)
}
func timeline(for configuration: SingleWidgetConfigurationIntent, in context: Context) async -> Timeline<SingleEntry> {
var entries: [SingleEntry] = []
guard let counter = configuration.currentCounter else {
return Timeline(entries: [SingleEntry(date: Date(), habit: .singleSample)], policy: .atEnd)
}
guard let habit = PersistenceManager.shared.retrieveHabit(habitKey: counter.id) else {
return Timeline(entries: [SingleEntry(date: Date(), habit: .singleSample)], policy: .atEnd)
}
let currentDate = Date()
for secondOffset in 0 ..< 100 {
let displayable = viewModel.displayableFromHabit(
habit,
separateComponents: true,
secondOffset: secondOffset,
symbolName: habit.habitSymbol?.name ?? "",
overrideColor: configuration.currentColor.color,
overrideButtonVisibility: configuration.currentButtonVisibility,
overrideDisplayOption: configuration.currentDisplayOption
)
let entryDate = Calendar.current.date(byAdding: .second,
value: secondOffset,
to: currentDate)!
let entry = SingleEntry(date: entryDate, habit: displayable)
entries.append(entry)
}
return Timeline(entries: entries, policy: .atEnd)
}
}
struct SingleEntry: TimelineEntry {
let date: Date
let habit: HabitDisplayable
}
static var singleSample: HabitDisplayable {
return HabitDisplayable(habitKey: nil,
title: LS("sampleWidgetHabitTitle"),
dates: [Date(timeIntervalSince1970: 1507158360)],
displayOption: .dayMonthYear,
secondOffset: 0,
separateComponents: true)
}
func displayableFromHabit(
_ habit: Habit,
separateComponents: Bool,
secondOffset: Int,
symbolName: String = "",
overrideColor: Color? = nil,
overrideButtonVisibility: WidgetButtonVisibility? = nil,
overrideDisplayOption: WidgetDisplayOption? = nil
) -> HabitDisplayable {
let latestDates: [HabitDate]
let displayOption: DisplayOption
if let overrideDisplayOption {
if overrideDisplayOption == .sameAsCounter {
displayOption = habit.settings?.toValue.displayOption ?? .dayMonthYear
} else {
displayOption = DisplayOption(rawValue: overrideDisplayOption.rawValue - 1) ?? .dayMonthYear
}
} else {
displayOption = habit.settings?.toValue.displayOption ?? .dayMonthYear
}
let displayMode = displayOption.mode
switch displayMode {
case .timePassed, .date:
latestDates = PersistenceManager.shared.latestDate(habit: habit).map { [$0] } ?? []
case .activity:
latestDates = PersistenceManager.shared.retrieveHabitDates(habit: habit, limitDate: displayOption.limitDate())
}
let displayButton: Bool
if let overrideButtonVisibility {
if overrideButtonVisibility == .sameAsCounter {
displayButton = habit.settings?.toValue.buttonType == .onRow
} else {
displayButton = overrideButtonVisibility == .show
}
} else {
displayButton = false
}
return HabitDisplayable(
habitKey: habit.habitKey,
title: habit.title,
dates: latestDates.map { $0.date },
displayOption: displayOption,
secondOffset: secondOffset,
separateComponents: separateComponents,
color: overrideColor ?? habit.habitColor?.color ?? .appPrimary,
symbolName: symbolName,
displayButton: displayButton
)
}
I provided a large portion of my code, let me know if you need more. The strange thing here is, even if the DB connection is broken somehow, it should have shown the default option (singleSample).
I am not able to reproduce/fix this for months now, so any help is very appreciated.
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Hello Apple Developer Technical Support,
I’m following up on case #102807413324 and submitting this as a code-level support request.
We are integrating iOS Live Activities (ActivityKit + WidgetKit extension written in SwiftUI) into an Expo/React Native app. We’re seeing behavior where the Live Activity UI shown on the Lock Screen appears to “stick” to an older layout and ignores updated SwiftUI code and/or bundled assets, even after rebuilding, reinstalling, and removing existing Live Activities before testing again.
Environment
Device: iPhone 13
iOS: 26.2
macOS: 15.7.3 (24G419)
Xcode: 16.4 (16F6)
Expo SDK: 52
React Native: 0.76.9
expo-live-activity: ^0.4.2
Build type: Ad-Hoc signed IPA (EAS local build)
Summary
We have a WidgetKit extension target (LiveActivity.appex, bundle id: stimul8.LiveActivity) using ActivityConfiguration(for: LiveActivityAttributes.self).
The extension contains multiple SwiftUI views selected via a “route” (derived from deepLinkUrl / title / subtitle), and uses images/backgrounds from the extension asset catalog (Assets.xcassets). We also support loading images from an App Group container with a fallback to the asset catalog.
After shipping updates, the Live Activity UI shown on the Lock Screen continues to resemble an older/default layout (example: a progress-bar-like element remains visible even after removing ProgressView usage from LiveActivityView.swift). Some custom backgrounds/images also fail to display as expected.
Routing (examples)
/streak -> StreakLiveActivityView
/streak-urgent -> StreakUrgentLiveActivityView
/lesson/create -> AILessonLiveActivityView1
/lesson/reminder -> AILessonLiveActivityView2
default -> LiveActivityView
Steps to reproduce (high-level)
Install/build and trigger a Live Activity.
Modify the SwiftUI layout in the extension (e.g., remove ProgressView and change obvious UI elements), rebuild, and reinstall.
Remove any existing Live Activities from the Lock Screen, then trigger a new Live Activity again.
Observed: Lock Screen Live Activity still renders the prior/older-looking UI and/or ignores updated assets.
Troubleshooting already done
Verified the extension (LiveActivity.appex) is included in the IPA and properly signed.
Verified Assets.car is present in the extension and PNG assets are present in the build artifacts.
Ensured SwiftUI source files used by the extension are overwritten during prebuild so the intended versions are present in ios/LiveActivity.
Cleared DerivedData related to LiveActivity builds.
Reinstalled the app and removed existing Live Activities from the Lock Screen before re-triggering new ones.
Questions
Is there any known caching behavior where Live Activities can continue to display a previous UI layout after an app/extension update, even when the activity is re-created?
Are there recommended steps to force the system to load the newest widget extension binary/UI beyond reinstalling and removing existing Live Activities?
What’s the recommended way to confirm which exact extension binary/UI version is being rendered on-device (e.g., specific Console logs, sysdiagnose signals, or other indicators)?
Are there any known constraints with Assets.xcassets usage for Live Activities that could cause bundled assets not to render even when present?
We can provide
A minimal reproduction Xcode project (preferred)
The IPA build
Build logs (Xcode/EAS)
Screenshots/video and a sysdiagnose captured after reproduction
Thank you for your guidance.
Best regards
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Hey!
I'm working on enabling remotely started live activities. I'm running into 2 crashes:
Upon initializing ActivityAuthorizationInfo
Upon calling Activity<...>.activities array
Both stack traces look like this:
0 libsystem_kernel.dylib +0xce0 _mach_msg2_trap
1 libsystem_kernel.dylib +0x4398 _mach_msg2_internal
2 libsystem_kernel.dylib +0x42b4 _mach_msg_overwrite
3 libsystem_kernel.dylib +0x40fc _mach_msg
4 libdispatch.dylib +0x1cc04 __dispatch_mach_send_and_wait_for_reply
5 libdispatch.dylib +0x1cfa4 _dispatch_mach_send_with_result_and_wait_for_reply
6 libxpc.dylib +0x107ec _xpc_connection_send_message_with_reply_sync
7 BoardServices +0xaea8 -[BSXPCServiceConnectionMessage _sendWithMode:]
8 BoardServices +0x17938 -[BSXPCServiceConnectionMessage sendSynchronouslyWithError:]
9 BoardServices +0xeef0 ___71+[BSXPCServiceConnectionProxy createImplementationOfProtocol:forClass:]_block_invoke
They happen to a limited number of users, but not insignificant. Most are on iOS 18.6.2 and iOS 26.1, but there are others in the mix. I don't have a repro myself. It looks like the main thread gets blocked after we receive no response from these ActivityKit APIs. Both of these are called inside application:didFinishLaunchingWithOptions:.
For ActivityAuthorizationInfo, we need the app to communicate with the server whether the user has live activities enabled; hence, calling this object's init as early as possible in the app.
For activities array, I'd like to do some logging whenever the live activity is started or ended (for example, if activities array no longer contains any activities, we can log the activity as dismissed). For this logging to happen, as far as I understand, it has to happen inside didFinishLaunchingWithOptions since this is the only method being called upon the terminated app receiving background runtime when the live activity starts/ends remotely.
After some research, one potential reason is ActivityKit APIs are just not ready to return values via xpc connection at app startup, so moving these methods to applicationDidBecomeActive could resolve the problem. That's fine for ActivityAuthorizationInfo init, but for accessing activities, there is no other place in the lifecycle to see if an activity has been dismissed (especially in the scenario where app is terminated, so we get only 30 seconds ish of background runtime).
Curious if anyone has run into this or has any insights into ActivityKit API behavior.
Summary
A crash occurs in ViewBridge framework when a TUINSRemoteViewController object
receives -release message after being deallocated. This appears to be a
reference counting issue within the ViewBridge framework's internal
autorelease pool management.
Environment
macOS Version: [15.0.0]
Application: Custom Qt-based application using Chromium Embedded Framework (xcode version: xcode 15; QT version: 6.5.4 ; CEF version: 138.0.47+g2728d53+chromium-138.0.7204.221)
Steps to Reproduce
Users are typically using the app normally, but a crash occasionally occurs when they activate and click on the application. This happens infrequently, but it occurs roughly every day. Currently, only a few specific users experience this crash, and it may appear for several consecutive days before disappearing for several days.
We cannot reliably reproduce this issue but have collected crash logs
from affected users.
Crash Analysis
Zombie Detection Log:
Zombie <TUINSRemoteViewController: 0x338708020> received -release
Deallocation Stack Trace (when object was first released):
0 Chromium Embedded Framework 0x000000014283a7f4 rust_png$cxxbridge1$ResultOfWriter$operator$sizeof + 933592
1 AppKit 0x000000019eac0d80 -[NSResponder dealloc] + 340
2 AppKit 0x000000019ebb5e34 -[NSViewController dealloc] + 276
3 ViewBridge 0x00000001a3f6ab9c -[NSRemoteViewController dealloc] + 92
4 AppKit 0x000000019eada4b4 -[NSViewController release] + 236
5 ViewBridge 0x00000001a3eda130 -[NSRemoteViewController release] + 380
6 libobjc.A.dylib 0x000000019aa8806c objc_autoreleasePoolPop + 56
7 CoreFoundation 0x000000019aefc7c0 _CFAutoreleasePoolPop + 32
8 Foundation 0x000000019c0d14f4 -[NSAutoreleasePool drain] + 136
9 ViewBridge 0x00000001a3ed609c __CONSIDER_WHO_REQUESTED_THIS_WAIT_BEFORE_SENDING_BUG_TO_VIEWBRIDGE__ + 24
10 ViewBridge 0x00000001a3f82a10 deferredBlockOpportunity_block_invoke_2 + 436
11 CoreFoundation 0x000000019af3dfa0 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 28
12 CoreFoundation 0x000000019af3deb0 __CFRunLoopDoBlocks + 356
13 CoreFoundation 0x000000019af3d330 __CFRunLoopRun + 2432
14 CoreFoundation 0x000000019af3c334 CFRunLoopRunSpecific + 572
15 HIToolbox 0x00000001a63740cc RunCurrentEventLoopInMode + 292
16 HIToolbox 0x00000001a6379ebc ReceiveNextEventCommon + 636
17 HIToolbox 0x00000001a637a020 _BlockUntilNextEventMatchingListInModeWithFilter + 76
18 AppKit 0x000000019ea7fa70 _DPSNextEvent + 660
19 AppKit 0x000000019f3a57b8 -[NSApplication(NSEventRouting) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 688
Crash Stack Trace (second release attempt):
0 Chromium Embedded Framework 0x14a03a37c rust_png$cxxbridge1$ResultOfWriter$operator$sizeof + 932448
1 Chromium Embedded Framework 0x14a03a04c rust_png$cxxbridge1$ResultOfWriter$operator$sizeof + 931632
2 CoreFoundation 0x19af1cbe8 ___forwarding___ + 200
3 CoreFoundation 0x19af1ca60 _CF_forwarding_prep_0 + 96
4 libobjc.A.dylib 0x19aa8bd94 AutoreleasePoolPage::releaseUntil(objc_object**) + 204
5 libobjc.A.dylib 0x19aa88138 objc_autoreleasePoolPop + 260
6 CoreFoundation 0x19aefc7c0 _CFAutoreleasePoolPop + 32
7 Foundation 0x19c0d14f4 -[NSAutoreleasePool drain] + 136
8 ViewBridge 0x1a3ed609c __CONSIDER_WHO_REQUESTED_THIS_WAIT_BEFORE_SENDING_BUG_TO_VIEWBRIDGE__ + 24
9 ViewBridge 0x1a3f82a10 deferredBlockOpportunity_block_invoke_2 + 436
10 CoreFoundation 0x19af3dfa0 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 28
11 CoreFoundation 0x19af3deb0 __CFRunLoopDoBlocks + 356
12 CoreFoundation 0x19af3d330 __CFRunLoopRun + 2432
13 CoreFoundation 0x19af3c334 CFRunLoopRunSpecific + 572
14 HIToolbox 0x1a63740cc RunCurrentEventLoopInMode + 292
15 HIToolbox 0x1a6379ebc ReceiveNextEventCommon + 636
16 HIToolbox 0x1a637a020 _BlockUntilNextEventMatchingListInModeWithFilter + 76
17 AppKit 0x19ea7fa70 _DPSNextEvent + 660
18 AppKit 0x19f3a57b8 -[NSApplication(NSEventRouting) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 688
19 AppKit 0x19ea72b7c -[NSApplication run] + 480
20 libqcocoa.dylib 0x1057b7514 QCocoaEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 2156
21 QtCore 0x109bb55c4 QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) + 532
22 QtCore 0x109babff8 QCoreApplication::exec() + 112
23 HoYowave 0x1009fc740 QWidgetFrameAdapter::RunMessageLoop() + 132
24 HoYowave 0x1008ae908 0x10088c000 + 141576
25 HoYowave 0x1008b7038 0x10088c000 + 176184
26 HoYowave 0x1008a3544 0x10088c000 + 95556
27 HoYowave 0x1008a294c 0x10088c000 + 92492
28 dyld 0x19aad4274 start + 2840
Technical Analysis
The crash pattern indicates:
A TUINSRemoteViewController was autoreleased multiple times
During autorelease pool drain, the first release triggered dealloc
Subsequent release attempts accessed the deallocated object (zombie)
This is entirely within ViewBridge framework's internal code path
The function name __CONSIDER_VIEW_SERVICE_BEFORE_SENDING_BUG_TO_VIEWBRIDGE__
in the stack trace suggests this is a known problematic area.
Questions
Is this a known issue in ViewBridge framework?
Is there any workaround or mitigation we can apply from the application side?
Could you provide guidance on what might trigger this condition?
I have report this in Feedback Assistant FB21809243
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Hello,
in my widget the user displays images filling the whole widget with overlayed texts (via ZStack). Via shadows or text background color the text gets better readable.
However, when a user chooses transparent or tinted colors for the home screen, the text is barely or not readable anymore since e.g. white text on white image background. How to resolve this issue?
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Tags:
SwiftUI
Visual Design
WidgetKit
I configured my app to show a Live Activity when an alarm rings using AlarmKit. However, if I dismiss the Live Activity by tapping somewhere other than the X button, and then long-press the Dynamic Island, a new Live Activity appears that is long but contains no information.
Currently, the only way I can remove this empty Live Activity is to press the X button while the alarm is in the snooze state. Pressing the X button on the initial alarm does not remove it.
Is there any way to prevent this behavior or properly handle / clean up this empty Live Activity?
I was very excited to see the addition of push notifications for widgets. However upon further inspection, the way it is implemented seems too limiting for real life apps.
I have an app for time tracking with my own backend. The app syncs with my backend in the main executable (main target). My widgets are more lightweight as they only access data in the shared app container, but they don't perform sync with the server directly to avoid race conditions with the main app.
I was under the impression that the general direction of the platform is to be doing most things in the main app target (also App Intents work that way for the most part), so the fact that the WidgetPushHandler just calls the widget's method to reload the timeline is very unfortunate. In an ideal scenario I also need the main app to be 'woken up' to perform the sync with the server, and once that's done I'd update the widget's timeline and where I would just read data from the shared app container.
So, my questions are:
What is the recommended way of updating the widgets when this push notification arrives in the case that the main app target needs to perform the sync first?
Is there any way how to detect that the method
func timeline(for configuration: InteractiveTrackingWidgetConfigurationAppIntent, in context: Context)
was called as a result of the push notification being received?
Can I somehow schedule a background task from the widget's reloadTimeline() function?
How can I get the push token later, in case that I don't save it right away the first time the WidgetPushHandler's pushTokenDidChange() is called?
Thank you for your work on this and hopefully for your answers.
FB19356256
I’m implementing a Live Activity that shows some text and a button.
When the user taps the button, I want to open the host app.
What I’ve done so far:
Implemented a LiveActivityIntent to handle the button tap.
The intent is triggered successfully. However, the app does not open by using deep link/universal app link.
From what I can tell, LiveActivityIntent seems limited to system/background execution and doesn’t bring the app to the foreground.
Questions:
Is it possible for a LiveActivityIntent to open the app?
Is this behavior a documented/intentional limitation?
If not supported, is using a Universal Link or deep link the recommended solution for opening the app from a Live Activity button?
Any official clarification or recommended best practice would be helpful.
Issue Description:
Tapping the app widget sometimes triggers the Universal Link twice, causing duplicate navigation or actions within the app
Steps to Reproduce:
Add the app widget to the home screen
Tap the widget to open the app via the Universal Link
Observe that the Universal Link is sometimes fired twice
Expected Behavior:
Tapping the widget should trigger the Universal Link only once.
Actual Behavior:
Universal Link is triggered twice, causing duplicate
navigation or actions.
Since watchOS 26, watch face sharing has stopped working completely.
I tested the following:
Sharing via CLKWatchFaceLibrary
Other public APIs
Sharing via iMessage
In all cases, the watch face cannot be reinstalled after being shared.
This issue is not limited to my app. Large third-party apps such as Facer and other major watch face platforms are also affected, which suggests this is a system-level change or bug.
Everything worked correctly before watchOS 26.
Has Apple officially acknowledged this issue?
Is there a recommended place to report or track this bug?
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Tags:
Face Sharing
watchOS
Watch Complications
WidgetKit
Hello everybody!
Does anybody know how to set default values for string array intent field provided dynamically? I want to have preset array field values just after widget added
I have a simple accessory widget with circular and rectangular representation (the first one is for 1 currency value and the second one is for 3 values).
I created CurrencyWidgets.intentdefinition and added AccessoryCurrency custom intent. Here I added string parameter field currencyCode. For this parameter I set the following options:
Supports Multiple Values
Fixed Size (AccessoryCircular = 1, AccessoryRectangular = 3)
User can edit value in Shortcuts
Options are provided dynamically
Then I created CurrencyTypeIntent extension and added IntentHandler for my custom intent AccessoryCurrency. The code is below
class IntentHandler: INExtension, AccessoryCurrencyIntentHandling {
override func handler(for intent: INIntent) -> Any { self }
func provideCurrencyCodeOptionsCollection(for intent: AccessoryCurrencyIntent) async throws -> INObjectCollection<NSString> {
return INObjectCollection(items: [NSString("USD"), NSString("EUR"), NSString("RUB"), NSString("CNY")])
}
func defaultCurrencyCode(for intent: AccessoryCurrencyIntent) -> [String]? {
return ["USD", "EUR", "RUB"]
}
}
The problem is in func defaultCurrencyCode(...): when I return something except nil (for example ["USD"] or ["USD", "EUR", "RUB"]) then I got a broken widget. It hangs in a placeholder state in lock screen and at add widget UI (see the image below).
Otherwise when I return nil then my widget works fine. But when I try to customise widget then I don't have default values for my currencyCode field, only Chose placeholders.
At the same time everything works fine for the single string parameter (without "Supports Multiple Values"). Does anybody know how to make default parameters work for array (multiple) field?
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Tags:
WidgetKit
Intents
App Intents
Subject: Request to maintain the "Status Bar Breadcrumb" visibility while Dynamic Island is active
Hi,
I am developing an app using ActivityKit and Dynamic Island. I've noticed a significant UX regression regarding the system status bar behavior.
Current Behavior: When a Live Activity is active in the Dynamic Island (specifically in the compactLeading and compactTrailing presentation), the system automatically hides the "Back to [Previous App]" breadcrumb button (the small arrow and app name) in the top-left corner of the status bar.
This breadcrumb only reappears when the Live Activity switches to the minimal presentation or is dismissed.
The Issue: This behavior makes multitasking cumbersome for users. For example, if a user navigates from Safari to my app and then locks the screen or goes to Home, they expect the "Back to Safari" button to remain accessible. However, because my app's Live Activity is running in the Dynamic Island, the OS removes this navigation shortcut, forcing users to use the app switcher instead.
Why this should be changed:
Screen Real Estate: On larger devices (e.g., iPhone 15/16 Pro Max), there is significant whitespace in the top-left corner between the time and the Dynamic Island's leading edge. The breadcrumb could easily fit there without overlapping the Live Activity content.
Inconsistent UX: Users are confused why the navigation button disappears solely because a background activity is running.
Request: Please consider updating the status bar layout logic to allow the "Back to App" breadcrumb to remain visible if there is no physical overlap with the compactLeading view of the Dynamic Island.
Thank you.
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
How do I set a custom icon for an app control that appears in Control Shortcuts (swipe down from iOS) ?
Where is the documentation for size and where to put the image, format etc?
Thank you.
Working Code (sfsymbol)
import Foundation
import AppIntents
import SwiftUI
import WidgetKit
// MARK: - Open App Control
@available(iOS 18.0, *)
struct OpenAppControl: ControlWidget {
let kind: String = "OpenAppControl"
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(kind: kind, content: {
ControlWidgetButton(action: OpenAppIntent()) {
Label("Open The App", systemImage: "clock.fill")
}
}
})
.displayName("Open The App") // This appears in the shortcuts view
}
}
Sample Image
These apps use their own image. How can I use my own image?
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Tags:
SwiftUI
WidgetKit
App Intents
I've been implemented App Shortcuts into my apps which are localized for a variety of languages.
The WWDC23 "Spotlight your app with App Shortcuts" has been extremely helpful in resolving my localized trigger phrases issue, but before I continue filling out all of the trigger phrases for my application I am concerned about a limitation that was mention in the video and need some additional information about it.
The limitations noted in the video at minute mark 21:26 states that:
Maximum 10 App Shortcuts (OK)
Maximum 1000 trigger phrases...
If I have 1 app and 10 shortcuts, and each shortcut only uses (.applicationName), this means I get to have 100 trigger phrases for each shortcut (for the sake of the discussion).
What I'm unsure about is when I begin providing localization do the localized triggered phrases count toward the trigger phrase limit? Essentially, for every language I support do I have to drop 1/2 of all of my trigger phrases to stay under the limit?
At the moment, my app is supporting 40 languages and I would like to know how localization affects the trigger phrase limit.
Thank you!
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Tags:
Shortcuts
Intents
App Intents
(I truly appreciate all the responses you all have written for me :bow: )
I was under the assumption that for Live Activity, in order for you to be able to update the Activity, you need an update token. And for the OS to issue you the update token, user must hit the "Allow" from the lock screen.
However based on these screenshots it seems that you don't need to hit "Allow" to be able to update the Live Activity.
Live Activity was updated — even without the user hitting "Allow"
So now I'm wondering if:
Is hitting Allow required for the update token to get issued? Or that assumption is incorrect? In our tests (when connected to Proxyman, the OS emits the update token after user hits "Allow" / "Always Allow")
If you don't hit allow, are there alternate ways to update the Live Activity without having the update token?
I'm guessing you could set a short stale time and then when the OS launches the app in the background you query the server and then update the Live Activity. Is that a worthy approach?
I also noticed that the "The Philly Inquirer" App has 'Background App Refresh" enabled, but this happened in 2 minutes. In our architecture assessments, after reviewing Apple's docs on 'Background Processing", we didn't think of it as a viable option, because it can't guarantee if the OS is given time in the next 2 minutes or 10 hours later when the phone is getting charged again.
Are any of these workarounds viable or are there alternate approaches?
Our requirement is:
be able to use Live Activity between 2-72hrs after app install. (I mention this because perhaps Apple may impost some restrictions for new installs)
be able to update an active Live Activity within 1-2 minutes after it has began.
I want to make a app that indicates that the app is recording in the background using a indicator on the dynamic island or on the top of the phone screen but i can't figure it out on Xcode. Saw a guy do it by making live activities true but can't seem to find that option in background mode in capabilities. any ideas?
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
We are using the MPNowPlayingInfoCenter API to provide information to the “Now Playing” system UI. This works as expected, except that when the app is manually terminated, the information is removed from the UI.
Our question is: Some apps (for example, Audible) are able to appear in the “Recently Played” section of the UI. This section seems to show a history of apps that previously provided “Now Playing” information but are not currently playing anything.
We would like to know which API is used to achieve this behavior.
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Hi,
We design a Live Activity for our app.We find that in iOS26 system, the widget can not display the correct system display model(Light mode or dark mode), always display with dark mode. When our app run in other system ,such as iOS 17, iOS18 ,it work fine.
I find other developer had post a topic three month ago , but it seems there is not any new response about the feedback.
https://developer.apple.com/forums/thread/799684?answerId=857377022#857377022
Anyone have idea?
Thanks .
Below is my code template:
struct BroadcastLiveActivityBackgroundView: View {
@Environment(\.colorScheme) var colorScheme: ColorScheme
var body:some View {
LinearGradient(
stops: [
Gradient.Stop(color: LiveActivityColor.backgroundColors(self.colorScheme).last!, location: 0.00),
Gradient.Stop(color: LiveActivityColor.backgroundColors(self.colorScheme).first!, location: 1.00),
],
startPoint: UnitPoint(x: 1, y: 0),
endPoint: UnitPoint(x: 0.82, y: 1.11)
)
}
}