Our app uses a 24-hour DeviceActivityMonitor repeating schedule to send users notifications for every hour of screen time they spend on their phone per day. Notifications are sent from eventDidReachThreshold callbacks at 1, 2, 3, etc, hour thresholds to keep them aware of their screen time.
We have recently received an influx of emails from our users that after updating to iOS 17.6.1 their DeviceActivityMonitor notifications are saying their screen time was much higher than what is shown in DeviceActivityReport and their device's Screen Time settings.
These users have disabled "Share Across Devices" - but I suspect the DeviceActivityMonitor is still getting screen time from their other devices even though that setting is turned off.
Has anybody else noticed this, understands what is causing this, or could recommend a fix that we can tell our users to do?
Post
Replies
Boosts
Views
Activity
I've been using DeviceActivityMonitor for 2 years, and recently noticed the following issue, starting in iOS 17.5 (another user also reported here).
For a sizable percentage of my users, device activity event thresholds get triggered together. My app sends notifications for every hour of screen time during the DeviceActivitySchedule using event thresholds. Often users will get, for example, the 1, 2, and 3 hour screen time notifications all at the same time.
I have a hypothesis for why this is happening: the system sometimes terminates the app extension for various reasons, one being if the 6MB memory limit is reached. It seems as though the retry policy is to retry the failed threshold at the next event threshold. And if the following threshold also fails, they can pile up until the next one succeeds. I think this is a new retry policy since iOS 17, and I believe this because:
There used to be a bug where the same threshold was triggered multiple times in a row, indicating that the failed threshold was retried immediately. This bug is no longer around and it's been replaced by the one I am reporting.
According to my logs, thresholds that get triggered together are also called earlier when they are supposed to be called - but the callback function does not complete. So this indicates that the threshold isn't just called late, but that it is called once and then retried again later.
If anyone could answer the following questions I'd be super grateful:
Is there ANY way to log when the system terminates the app extension and for what reason? And not just on my own device, but for all our users in production (because it's hard to reproduce this issue, as it only happens for some portion of our users). Maybe some kind of crash report or failure callback that will allow my to ping my server?
Could anyone at Apple could confirm my hypothesis about the new retry policy causing this issue?
I use App Groups to share UserDefaults data between my host app and DeviceActivityMonitor extension.
On iOS 17, it appears that reading @AppStorage variables are causing my DeviceActivityMonitor extension callback functions to crash. Weirdly, writing values is okay.
I see this in the extension logs:
Couldn't read values in CFPrefsPlistSource<0x10fe251d0> (Domain: GROUP_NAME, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd
However, through searching this log message on the internet and the fact that it also appears in my host app logs without crashing, this seems to be a warning - possibly indicating an issue but also a possible red herring.
But the fact remains that when I don't read UserDefaults values or variables decorated with @AppStorage in the DeviceActivityMonitor extension, everything works fine.
Are UserDefaults, and specifically @AppStorage decorators supported in the DeviceActivityMonitor extension?
Our app uses DeviceActivityReport to display the user’s screen time. The performance of DeviceActivityReport is often very poor. These issue occur commonly:
Screen time is reported as 0 minutes
DeviceActivityReport View appears completely blank
The host app loses connection with the DeviceActivityReport altogether
I have implemented several workarounds which only slightly improve the performance, to varying degrees depending on the device iOS:
Inside the DeviceActivityReport code - retry fetching screen time data until it returns a non-zero result
Implement a “refresh” button which reloads the DeviceActivityReport view from the host app
However, due to the sandboxed nature of DeviceActivityReport, there is no way for the host application to tell if the DeviceActivityReport extension is experiencing these performance issues. It results in a really bad user experience.
I am building the app with Xcode 14 due to another issue where DeviceActivityReport fails to load for all iOS 16 devices when built with Xcode 15 (this is a know issue, here’s a link to a discussion on the developer forums: https://developer.apple.com/forums/thread/735915). However, when testing on iOS 17 devices with Xcode 15 builds, the above issues still occur.
I have received no crash reports from DeviceActivityReport.
These issues are known bugs and have been discussed on the developer forums, but I haven't yet seen a solution. Other screen time apps exist that use the DeviceActivityReport seemingly flawlessly, so I know that there is a way to improve the performance of DeviceActivityReport in my app.
Please help! I have been dealing with this poor performance for a long time now with almost no improvement!
We have an app that uses the Screen Time APIs to block certain apps set by the user on a schedule:
We use ManagedSettings to shield selected apps
We use DeviceActivityMonitor to shield the apps automatically on a schedule set by the user. The shielding starts during the intervalDidStart callback function and ends during the intervalDidEnd callback function
We are getting reports from the majority of our iOS 17 users that the app blocking schedules no longer work on iOS 17. We have tested this on our own iOS 17 devices and reproduced the behavior. But the feature still works consistently on iOS 16 devices.
The app is still built using Xcode 14 instead of Xcode 15 due to another issue - the DeviceActivityReport is blank for all iOS 16 users when built in Xcode 15 (link to issue on the developer forums: https://developer.apple.com/forums/thread/735915). When testing with Xcode 15 builds, the bug appears to improve - however it still occurs intermittently.
Are there any other mechanisms to run tasks on repeating schedules? For this specific feature, we don't need to eventDidReachThreshold callbacks, which is the main purpose of DeviceActivityMonitor. So we really don't need any Device Activity integration at all, just setting and disabling ManagedSettings shields at certain times. Would love if anyone could suggest and alternative to DeviceActivityMonitor.
My app uses DeviceActivitySchedule to let users set schedules to block certain apps. I naturally want to understand how my users are using the feature, so I capture analytics events using Segment.
Ever since the release of iOS 17.1, analytics events from DeviceActivityMonitor have stopped firing. I believe this is due to the fact that the app extension that DeviceActivityMonitor runs in does not support asynchronous network requests. (However, I'm not sure why the analytics were working correctly with iOS 16).
What is the best way to capture analytics inside the DeviceActivityMonitor app extension in iOS 17?
My app sends screen time awareness notifications based on DeviceActivityMonitor thresholds.
Often, users receive two notifications in a row for the same screen time threshold. This means that the app extension is triggering the same eventDidReachThreshold callback function twice for the same threshold. I've made sure that there is only one activity schedule being monitored. This happens often, but not every time (over 50% of the time).
Anybody else experience this issue, and any way to mitigate it?
I'm encountering a problem with the DeviceActivityMonitor framework on iOS 17 that wasn't present in iOS 16. The app extension sends analytics events via the Segment SDK whenever the extension's functions are called. This setup worked flawlessly on iOS 16. However, since upgrading to iOS 17, it appears that the vast majority of my iOS 17 users are not triggering the expected analytics events.
I'm aware that network requests in app extensions are not officially supported, yet somehow, Segment's SDK managed to function without issues in the previous OS version. Could there have been any changes in iOS 17 related to app extensions or the DeviceActivityMonitor framework that might be influencing this change in analytics behavior? Or perhaps there's a new restriction or modification in the app extension's network capabilities that I might be overlooking?
Any insights into these changes or guidance on how to ensure analytics events are triggered reliably within the app extension on iOS 17 would be greatly appreciated.
Thank you in advance for your help!
Hello Apple Developer Community,
We're experiencing a critical issue with the Screen Time frameworks, and it's affecting one of our users severely. I'm hoping someone here can provide guidance or a potential solution.
Details:
Our app offers a feature using the ManagedSettings shield that lets users block all apps based on a set schedule.
After the scheduled block ends, the apps are expected to become accessible again.
In one case, a user reported that the apps did not unblock after the schedule ended.
Upon trying to manually end the session from within our app, the app only displays a blank white screen.
The user attempted to disable Screen Time access for our app via the iOS settings, but the apps remained blocked.
Even after completely disabling Screen Time from the settings or restarting the phone, the apps stayed blocked.
Interestingly, I attempted to replicate the issue on my end by toggling Screen Time settings and restarting, but everything worked as expected and I could not reproduce the problem.
This issue, though seemingly isolated, has rendered a user's phone virtually unusable, and highlights a potential high-impact bug within the Screen Time framework. It feels necessary for there to be a "master off-switch" or a fail-safe mechanism in these scenarios.
Any insights, solutions, or workarounds would be deeply appreciated. It's crucial for us to support our user and resolve this promptly.
Thank you in advance!
Hello,
I've been working with an app that leverages a DeviceActivityReport app extension. I want to ensure that the main app can gracefully handle scenarios where the connection to the app extension might get terminated unexpectedly.
Can anyone guide me on how to detect from the main app when the connection to the app extension gets terminated? I believe being able to detect this would allow for better error handling and potentially reduce instances where the UI appears blank due to a lost connection.
Thank you for any insights or advice on this topic.
Hello,
I have an iOS app using the DeviceActivityReport app extension. Occasionally, the extension gets terminated (likely due to reaching memory limit**), which in turn disrupts the connection with the main app.
How can the main app detect when the DeviceActivityReport extension has been terminated due to memory constraints, and what's the best way to re-establish the connection afterward?
Thanks in advance for your insights and suggestions!
** I'm also unsure why the extension should be reaching its memory limit. When profiling the extension in XCode, it never reaches above 15MB, and I know the limit is 100MB. It's not doing anything out of the ordinary - and I've seen very similar complaints about DeviceActivityReport terminating on these forums. Any thoughts on how to prevent termination in the first place would also be super helpful!
I created a ShieldConfigurationExtension in Xcode 14.3 with File > New > Target > ShieldConfigurationExtension. This created the extension with all the necessary Info.plist values (correct NSExtensionPrincipalClass, etc.), with the extension included in embedded content in the host app target.
No matter what I try, the extension is not getting invoked when I shield applications from my host app. The custom UI does not show as the shield, and looking at the debugger, an extension process is never invoked.
I am shielding categories like this:
let managedSettings = ManagedSettingsStore()
...
managedSettings.shield.applicationCategories = .all()
And my extension code overrides all the ShieldConfigurationDataSource functions.
class ShieldConfigurationExtension: ShieldConfigurationDataSource {
override func configuration(shielding application: Application) -> ShieldConfiguration {
return ShieldConfiguration(
backgroundBlurStyle: UIBlurEffect.Style.systemThickMaterial,
backgroundColor: UIColor.white,
icon: UIImage(systemName: "stopwatch"),
title: ShieldConfiguration.Label(text: "You are in a Present Session", color: .yellow)
)
}
override func configuration(shielding application: Application, in category: ActivityCategory) -> ShieldConfiguration {
return ShieldConfiguration(
backgroundBlurStyle: UIBlurEffect.Style.systemThickMaterial,
backgroundColor: UIColor.white,
icon: UIImage(systemName: "stopwatch"),
title: ShieldConfiguration.Label(text: "You are in a Present Session", color: .yellow)
)
}
override func configuration(shielding webDomain: WebDomain) -> ShieldConfiguration {
return ShieldConfiguration(
backgroundBlurStyle: UIBlurEffect.Style.systemThickMaterial,
backgroundColor: UIColor.white,
icon: UIImage(systemName: "stopwatch"),
title: ShieldConfiguration.Label(text: "You are in a Present Session", color: .yellow)
)
}
override func configuration(shielding webDomain: WebDomain, in category: ActivityCategory) -> ShieldConfiguration {
return ShieldConfiguration(
backgroundBlurStyle: UIBlurEffect.Style.systemThickMaterial,
backgroundColor: UIColor.white,
icon: UIImage(systemName: "stopwatch"),
title: ShieldConfiguration.Label(text: "You are in a Present Session", color: .yellow)
)
}
}
What am I missing?
When using ManagedSettingsStore to shield apps, no system apps are shielded even when specifying all application categories. Here is my code:
managedSettings.shield.applicationCategories = .all()
Even when using the FamilyActivityPicker and selecting "All Apps & Categories" system apps like Messages do not get shielded.
managedSettings.shield.applicationCategories = .specific(selectedCategories)
I find this strange, since Messages exists inside the Social category, and is tracked fine using DeviceActivityMonitor. Why can't it be shielded using app categories?
I'd like to be able to shield all apps, including Messages, without having the user to specifically select the apps using FamilyActivityPicker. Is that possible?
The DeviceActivityReport is often laggy or sometimes doesn't show up at all. Quitting and re-opening the app usually fixes this problem.
I'm wondering, is there a way to programmatically terminate and then re-launch the DeviceActivityReport App Extension from the host app? This way, that could act as a "refresh" when the DeviceActivityReport fails to show up.
DeviceActivityReport view often shows up blank in the host app. What's the best way to refresh the view when this occurs?