Device Activity

RSS for tag

Monitor web and app usage through custom time windows and events.

Posts under Device Activity tag

200 Posts

Post

Replies

Boosts

Views

Activity

Loading indicator until DeviceActivityReport renders?
There is frequently a delay of a few seconds before a DeviceActivityReport renders its view generated from the DeviceActivityReportExtension. It will also sometimes flash with zero data before hydrating with the real activity data (tested with extension code taken directly from XCode boilerplate) Is there a way to be notified when the DeviceActivityReport renders successfully or is still processing, i.e. so a loading indicator can be presented while the extension runs? Thanks!
3
1
2.3k
Apr ’24
DeviceActivityReport Lag
There have been several posts (i.e. here, here) about the lagginess of the DeviceActivityReport extension. Often it takes a few seconds for the view to load, or sometimes doesn't show up at all. I've confirmed this is not a case of excessive memory usage in the extension (exceeding 100MB), because I've profiled the extension and it consistently maxes out at 10MB. I've placed a loading screen behind the DeviceActivityReport inside a ZStack in the host app in order to see if the lag is because it takes some time for the extension to spin up - but the loading screen does not appear, indicating that the extension is running right away, but receiving the view from the extension in the host app is where the lag happens. It's been extremely difficult to debug because the lag only occurs a fraction of the time, and DeviceActivityReport is pretty much a black box. There's no documentation about how the host app and extension actually communicate. I've also combed through the logs using the Console app on Mac with no indication of any issues, (but I do see the message "Connection to view service was invalidated" coming from the extension even when there is no lag). I'm pretty convinced that the problem lies in the host app, because when I strip everything away from the host app, DeviceActivityReport never lags. I suspect that there are processes running (network requests, async tasks, or state updates) that block the report view from being received in the host app. Could you please help me understand why this could be happening, with as many details as you could provide? Any details on how the host and extension communicate, what processes could block the view from appearing, or anything else. Seems like this is a common issue but plenty of apps also don't experience it. Any guidance you can provide would be extremely helpful, as I've been trying to fix this bug every since I've been working with this API with no luck. Thanks in advance!
4
7
2.0k
Apr ’24
Device activity/screen time api
I have a request from a client that would like to create a solution where a central component is the ability to retrieve the users app usage. I can see that there are various api's todo that - one example is the Device Activity API. Another hope from the client is that this should be a web solution and not an app. I haven't quite been able to figure out if this is possible or not. I have three questions: Is it possible to retrieve app usage information through a REST api (or similar) outside an iOS app? Is it possible to use the appleId as a kind of auth solution for a web application Are there any similar APIs for older versions than iOS 15?
0
0
609
Apr ’24
Device Activity Report for multiple children
How can I differentiate between multiple children in order to display screentime data to the user separately for a parent with multiple children? As far as I can see, the options are .children, and .all? I understand I can retrieve some info from within the extension so I can group data separately, but is there any way to reliably filter between different children from within my app, say using a DeviceActivityFilter? Many thanks!
0
1
670
Apr ’24
Background Safari Tab contributing to DeviceActivityMonitor usage threshold?
I am currently debugging an issue with DeviceActivityMonitor where the threshold is reached even though the target app (e.g. Instagram) is not being used actively. I noticed that the device with the unexpected behavior had the instagram.com website opened in the Safari web browser (among hundreds of other tabs). That tab was not actively used either (not in foreground, Safari app neither used). However, I was wondering if it can happen that this website is contributing towards the threshold as well even though it is in background and not used? Otherwise I cannot explain myself this strange behavior.
0
0
700
Apr ’24
I would like to create several schedules for an activity can I do that
currently when I try to set several schedules to the same activity the latest schedule I set is the only one I understand I can have only 1 schedule for activity. ? I thought about setting a new schedule on the intervalDidEnd but, I get no interval did start if the current time is in the middle of the interval I set For example, now it is 15:00, and my previous interval started at 14:00 and ends at 16:00 but the user sets a new interval from 14:30 - 16:40 I call the deviceActivityCenter.stopMonitoring([someActivityName]) and get noIntervalDidEnd event Then I set the new interval successfully with deviceActivityCenter.startMonitoring(someActivityName, during: deviceActivitySchedule) and get no intervalDidStartEvent So how can I achieve several intervals? If I had gotten the events of the start and end it would be possible Thanks for the help
3
0
1.6k
Apr ’24
eventDidReachThreshold is working as expected only when the app is in debug mode
As per our code, we have the apps to be shielded whenever the threshold is reached. According to this use-case, our code in DeviceActivityExtension looks something like: override func eventDidReachThreshold(_ event: DeviceActivityEvent.Name, activity: DeviceActivityName) { super.eventDidReachThreshold(event, activity: activity) defaults?.setValue(event.rawValue, forKey: "appLimitEventName") defaults?.setValue(true, forKey: "appLimitReached") defaults?.synchronize() // using darwinNotificationCenter to trigger callback in the application let darwinNotificationCenter = DarwinNotificationsManager.sharedInstance() darwinNotificationCenter.postNotification(withName: "nextAppLimitInitiated") // using Notifications to debug since print doesn't work scheduleNotification(with: "interval threshold reached") } And in our application, we have the shielding logic in place, init() { let darwinNotificationCenter = DarwinNotificationsManager.sharedInstance() darwinNotificationCenter.register(forNotificationName: "nextAppLimitInitiated"){ print("callback received") let appLimitReached = self.defaults?.bool(forKey: "appLimitReached") let appLimitEventName = self.defaults?.string(forKey: "appLimitEventName") if appLimitReached ?? false, appLimitEventName != "" { // this sends the notification when callback is received self.scheduleNotification(with: "init start") self.defaults?.setValue(false, forKey: "appLimitReached") guard var dataArray = self.defaults?.array(forKey: "appLimitdataArray"), !dataArray.isEmpty else { return } let appLimitData = dataArray.first as! NSDictionary let appLimitKey = appLimitData["appLimitId"] as! String let data = self.getSchedule(key: appLimitEventName ?? "") if let appTokens = data?.applicationTokens { for token in appTokens { if !self.applicationTokens.contains(appTokens) { self.applicationTokens.insert(token) } } } self.store.shield.applications = self.applicationTokens self.store.shield.applicationCategories = ShieldSettings.ActivityCategoryPolicy.specific(self.categoryTokens, except: Set()) dataArray.removeFirst() //dataArray.append(appLimitData) self.defaults?.set(dataArray, forKey: "appLimitdataArray") self.initiateMonitoring(initiateAgain: true) self.scheduleNotification(with: "init end") } } } This works as expected for multiple App Limits but only when the device is connected to the Xcode. If we disconnect the device from Xcode/ stop application from Xcode/ try in release mode, the callback is not received from extension to the app/init block. When the device is connected to Xcode, if the apps hit the threshold, they are shielded automatically. But if the device is disconnected/ app is in release mode, the apps are not shielded automatically even after the threshold is reached. It is shielded later only after opening our app once. Please let me know if I'm doing anything wrong in receiving callback or in my shielding logic. If I need to place the shielding logic in the extension, please tell me how I can handle multiple appTokens.
2
0
1.2k
Mar ’24
DeviceActivityMonitor Schedule Pause
I am trying to understand how to approach 'x minute' pauses for a DeviceActivitySchedule. For instance, I would like to let the user pause for 5 minutes from an active schedule (meaning un-shielding the apps and re-applying the shield after the 5 min has passed). The only way that came to my mind was calling the following: Calling .startMonitoring to start monitoring a new event with the same apps starting .now and ending .now + 5 minutes; Calling in the intervalDidStart, store.shield.applications.subtract(apps) so that the apps are removed from the shield. Calling in the intervalDidEnd, store.shield.applications = apps so that the apps are now shielded again. The problem is that, from the Apple Developer Documentation: The minimum interval length for monitoring device activity is fifteen minutes. So the minimum pause I could offer to the user would be 15 minutes. And that tells me this approach is most likely wrong, because all other Screen Time apps, like Opal, Jomo, AppBlock offer also 5 min pause. Does anyone know / can think of a different and better approach?
1
0
1.5k
Mar ’24
all my DeviceActivitySchedules are killed when granting unrelated 4th party app access to screen time API permission
I have two repeating DeviceActivitySchedules running in my app and noticed a super strange behavior: whenever I modify Screen Time API permission settings (granting an additional app permission, or removing permission for a different app (completely different unrelated screen time apps from App Store)) all my DeviceActivitySchedules are cancelled and the intervalDidEnd callbacks are called my permission is not modified in this scenario this is a super strange and unexpected behavior is anyone able to reproduce this? for me this happens 100% of the times I do this any known workarounds?
0
0
790
Mar ’24
In DeviceActivityReportScene count numberOfPickups for given Application
How can I count the number of pickups for a certain application? The docs say that there is a struct ApplicationActivity within the DeviceActivityData: https://developer.apple.com/documentation/deviceactivity/deviceactivitydata Using this code I receive a warning in XCode: Value of type 'DeviceActivityData' has no member 'applicationActivity' struct ApplicationPickupCountReport: DeviceActivityReportScene { // Define which context your scene will represent. let context: DeviceActivityReport.Context = .applicationPickupCount // Define the custom configuration and the resulting view for this report. let content: (String) -> ApplicationPickupCountView func makeConfiguration(representing data: DeviceActivityResults<DeviceActivityData>) async -> String { var totalPickups = 0 let totalPickups = await data.flatMap { $0.applicationActivity }.reduce(0, { $0 + $1.numberOfPickups }) return "\(totalPickups)" } }
0
0
725
Mar ’24
DeviceActivityEvent with threshold spanning over midnight → threshold not working
Hello, I am using a DeviceActivityEvent to limit access to an app after the user has spent x minutes on it. Sometimes it can happen that a DeviceActivitySchedule spans from 11:55pm - 12:25am (just an example). In these cases, I have noticed, the DeviceActivityMonitorExtension’s eventDidReachThreshold is not called after the user has reached the threshold time interval in the observed app. I assume this is because we have transitioned over midnight and something weird happens to the DeviceActivitySchedule. I’ve tried adding the day components to the DeviceActivitySchedule as well, but then it fails completely. Any advice how to handle this? I could, of course, create two separate DeviceActivitySchedules: one for before midnight, and one for after. But depending on the user’s real app usage that could lead to slightly different (and unexpected) behavior.
0
0
782
Mar ’24
eventDidReachThreshold triggered while target app is in background
Hello! I am using the eventDidReachThreshold callback in the DeviceActivityMonitor in order to shield a target app after the user has spend x amount of time on it (e.g. x = 5 minutes). Many times this works fine, and I can trigger my shield after the specified threshold has been met. However sometimes, when they leave the target app before the threshold has been reached, the eventDidReachThreshold callback gets called randomly while they are doing something else on their phone (e.g. using a different app, on the Home Screen, phone locked…). From my perspective this does not make sense, since they are not actively spending time on the target app, and that time should not be counted towards the target app’s threshold. And it is also very confusing for the users because they will then find a blocked target app even though they haven’t used their time budget completely. This is not related to the intervalDidStart / intervalDidEnd callbacks, because they are not correlating with the timing of when the eventDidReachThreshold callback is called unexpectedly. Any ideas what this could be related to?
0
0
698
Mar ’24
DeviceActivityCenter - couldn’t communicate with a helper application.
Hi there, In rare cases (about 0.2% of the time?), I'm seeing calls to startMonitoring on an instance of DeviceActivityCenter throw an error with localizedDescription "couldn’t communicate with a helper application." I am certain I am passing valid parameters to the startMonitoring call because sometimes a naive retry after a few seconds succeeds. Similarly, I am certain that FamilyControls permissions have been granted by the user. Was hoping to get more color from the systems team on what some causes of this error are and if it is avoidable by the programmer.
3
0
1.8k
Mar ’24
[iOS 17.4] Default value for DeviceActivityEvent's includesPastActivity should be set to true
Starting with iOS 17.4, the DeviceActivityEvent initializer includes a new parameter named includesPastActivity.

The default value for this parameter is set to false, whereas device activity events have behaved as though this parameter were set to true up until now. This breaking change is a MAJOR ISSUE for developers who used device activity events in their apps before iOS 17.4 because their apps might not work the way they intended after the update. They'll have to release new app versions that specifically set includesPastActivityto true. In my opinion, the default value for includesPastActivity should be true to avoid disrupting events scheduled on older versions of iOS. I have filed an enhancement report (FB13573556) about this. I really hope this is changed before the official iOS 17.4 release.
4
4
1.8k
Mar ’24
DeviceActivityMonitor does not trigger UI updates
There is a way to allow DeviceActivityMonitor to make UI changes? Currently I have some code in the intervalDidStart and intervalDidEnd that updates Core Data entities, and it works correctly. However, the changes are only reflected if the app is terminated and opened again. Thus, if the app is opened and one of the methods inside the DeviceActivityMonitor is being called, the UI won't be updated. PS: I am using @FetchRequest to retrieve the core data entities called BlockEntity, and I have a little circle in the UI which should indicate the status of the block: active/inactive, so it would be nice to update the color in real time when intervalDidStart or intervalDidEnd are called.
0
0
715
Mar ’24
Providing Pre-Selections to FamilyActivityPicker?
I am able to correctly select and store the tokens of apps and categories my users will block, but I am unable to pre-populate Picker whenever the app is rebooted with previously selected and stored tokens. Below is the selection variable I am passing to the picker... var selectionsToBlock = FamilyActivitySelection() { willSet { saveSelection(selection: newValue) blockersSelected = true } } Is there any way I can provide my existing blockers (shown below) so that the user can easily edit their list of restricted apps from the Picker? func savedBlockers() -> FamilyActivitySelection? { let defaults = UserDefaults.standard guard let data = defaults.data(forKey: userDefaultsKey) else { return nil } return try? decoder.decode( FamilyActivitySelection.self, from: data ) }
2
0
949
Feb ’24
DeviceActivityMonitor Extension not working
I am developing a project with the Screen Time API, and I cannot understand why the methods inside DeviceActivityMonitor extension are not being called. Some points to note: I do start by requesting authorization from the User (this is working as expected when opening the app) Both the DeviceActivityMonitor Extension and the main app are under the same App Group I have added Family Controls capability to both targets and have the developer account to use it. I start by calling center.startMonitoring() I have overridden the original methods. Yet, when startMonitoring is called, there is no action that is supposed to be taken that is being taken, and as far as I can tell, the methods aren't even being called. Here are some relevant snippets of code: // EXT class DeviceActivityMonitorExtension: DeviceActivityMonitor { override func intervalDidStart(for activity: DeviceActivityName) { super.intervalDidStart(for: activity) let store = ManagedSettingsStore(named: .daily) } override func intervalDidEnd(for activity: DeviceActivityName) { super.intervalDidEnd(for: activity) let store = ManagedSettingsStore(named: .daily) store.clearAllSettings() } override func eventDidReachThreshold(_ event: DeviceActivityEvent.Name, activity: DeviceActivityName) { super.eventDidReachThreshold(event, activity: activity) let store = ManagedSettingsStore(named: .daily) let model = BeeFreeModel.shared let applications = model.selectionToDiscourage.applicationTokens let categories = model.selectionToDiscourage.categoryTokens let webDomains = model.selectionToDiscourage.webDomainTokens store.shield.applications = applications.isEmpty ? nil : applications store.shield.applicationCategories = categories.isEmpty ? nil : ShieldSettings.ActivityCategoryPolicy.specific(categories) store.shield.webDomains = webDomains.isEmpty ? nil : webDomains } } // APP extension DeviceActivityName { static let daily = Self("daily") } extension DeviceActivityEvent.Name { static let discouraged = Self("discouraged") } let schedule = DeviceActivitySchedule( intervalStart: DateComponents(hour: 0, minute: 0, second: 0), intervalEnd: DateComponents(hour: 23, minute: 59, second: 59), repeats: true ) class BeeFreeSchedule { static public func setSchedule() { print("Setting schedule...") print("Hour is: ", Calendar.current.dateComponents([.hour, .minute], from: Date()).hour!) let events: [DeviceActivityEvent.Name: DeviceActivityEvent] = [ .discouraged: DeviceActivityEvent( applications: BeeFreeModel.shared.selectionToDiscourage.applicationTokens, categories: BeeFreeModel.shared.selectionToDiscourage.categoryTokens, webDomains: BeeFreeModel.shared.selectionToDiscourage.webDomainTokens, threshold: BeeFreeModel.shared.thresholdToDiscourage ) ] let center = DeviceActivityCenter() do { print("Try to start monitoring...") // Call startMonitoring with the activity name, schedule, and events try center.startMonitoring(.daily, during: schedule, events: events) } catch { print("Error monitoring schedule: ", error) } } } // APP class BeeFreeModel: ObservableObject { // Import ManagedSettings to get access to the application shield restriction let store = ManagedSettingsStore() //@EnvironmentObject var store: ManagedSettingsStore @Published var selectionToDiscourage: FamilyActivitySelection @Published var thresholdToDiscourage: DateComponents @Published var setOfApps: [String] init() { selectionToDiscourage = FamilyActivitySelection() thresholdToDiscourage = DateComponents() var setOfAppIdentifiers: Set<String?> = Set<String?>() setOfApps = [String]() if selectionToDiscourage.applicationTokens.isEmpty {} else { for application in BeeFreeModel.shared.selectionToDiscourage.applications { setOfAppIdentifiers.insert(application.localizedDisplayName) setOfApps = setOfAppIdentifiers.compactMap { $0 }.sorted() } } } class var shared: BeeFreeModel { return _BeeFreeModel } func setSelection() { let applications = BeeFreeModel.shared.selectionToDiscourage } func changeThreshold(threshold: DateComponents) { thresholdToDiscourage = threshold } } // APP /// ... Button("Select Apps to Discourage") { isDiscouragedPresented = true } .familyActivityPicker(isPresented: $isDiscouragedPresented, selection: $model.selectionToDiscourage) } .onChange(of: model.selectionToDiscourage) { BeeFreeModel.shared.setSelection() } // ... // ... Button("Save Time") { saveTime() let new_threshold = DateComponents(hour: savedTime?.hours, minute: savedTime?.minutes, second: savedTime?.seconds) model.changeThreshold(threshold: new_threshold) } } .onChange(of: model.thresholdToDiscourage) { BeeFreeSchedule.setSchedule() // ... I believe I am using the latest stable version of Xcode (recently deleted and reinstalled). I'm really new to mobile development to Swift so any help is greatly appreciated. Thank you! :)
5
1
1.5k
Feb ’24
Shield Configuration Extension sometimes doesn't update on app switcher
Sometimes the app switcher doesn't show the correct shield configuration if there is like 7 or more apps in the app switcher. if I clear all apps from app switcher cache and leave 4 apps, the correct shield configuration will show every time instantly when I change the shield configuration from my parent app and go back to the app switcher; but if there is like 7 or more apps in the app switcher, I can see some of the app's shield configurations don't update instantly and I have to scroll past the app in the app switcher and go back to for it to update to the correct shield if it does at all. This is misleading to users and in the case of my app detrimental to one of its core functionalities override func configuration(shielding application: Application) -> ShieldConfiguration { let sharedUserDefaults = UserDefaults(suiteName: SharedUserDefaults.suiteName.rawValue)! let isPaidOn = sharedUserDefaults.bool(forKey: SharedUserDefaults.paidSwitchKey.rawValue) if isPaidOn == true { return ShieldConfig.paid } else { return ShieldConfig.free } }
0
0
763
Feb ’24
App with DeviceActivityReport extension either won't install on device or won't upload to TestFlight
We have integrated DeviceActivityReport to our Family Controll app. All capabilities are added. We built the app and tested it, but when we want to upload to TestFlight the following error happened (see attached image) When we are adding NSExtensionMainStoryboard or NSExtensionPrincipalClass the following error appears during installation process. DeviceReport.appex with id com.example.example.DeviceReport defines either an NSExtensionMainStoryboard or NSExtensionPrincipalClass key, which is not allowed for the extension point com.apple.deviceactivityui.report-extension
2
2
791
Jan ’24
Screen Time API Access
Having tried to gain access to the total daily screen time API using the text/code from the below thread, we were unable to get any data access. Has anyone else had the same issue and any resolutions? Not looking for application limits, just statistics to inform user through our 3rd party application. Thanks for any guidance around areas we can attempt https://developer.apple.com/documentation/deviceactivity/deviceactivitydata/activitysegment
0
0
706
Jan ’24
Loading indicator until DeviceActivityReport renders?
There is frequently a delay of a few seconds before a DeviceActivityReport renders its view generated from the DeviceActivityReportExtension. It will also sometimes flash with zero data before hydrating with the real activity data (tested with extension code taken directly from XCode boilerplate) Is there a way to be notified when the DeviceActivityReport renders successfully or is still processing, i.e. so a loading indicator can be presented while the extension runs? Thanks!
Replies
3
Boosts
1
Views
2.3k
Activity
Apr ’24
DeviceActivityReport Lag
There have been several posts (i.e. here, here) about the lagginess of the DeviceActivityReport extension. Often it takes a few seconds for the view to load, or sometimes doesn't show up at all. I've confirmed this is not a case of excessive memory usage in the extension (exceeding 100MB), because I've profiled the extension and it consistently maxes out at 10MB. I've placed a loading screen behind the DeviceActivityReport inside a ZStack in the host app in order to see if the lag is because it takes some time for the extension to spin up - but the loading screen does not appear, indicating that the extension is running right away, but receiving the view from the extension in the host app is where the lag happens. It's been extremely difficult to debug because the lag only occurs a fraction of the time, and DeviceActivityReport is pretty much a black box. There's no documentation about how the host app and extension actually communicate. I've also combed through the logs using the Console app on Mac with no indication of any issues, (but I do see the message "Connection to view service was invalidated" coming from the extension even when there is no lag). I'm pretty convinced that the problem lies in the host app, because when I strip everything away from the host app, DeviceActivityReport never lags. I suspect that there are processes running (network requests, async tasks, or state updates) that block the report view from being received in the host app. Could you please help me understand why this could be happening, with as many details as you could provide? Any details on how the host and extension communicate, what processes could block the view from appearing, or anything else. Seems like this is a common issue but plenty of apps also don't experience it. Any guidance you can provide would be extremely helpful, as I've been trying to fix this bug every since I've been working with this API with no luck. Thanks in advance!
Replies
4
Boosts
7
Views
2.0k
Activity
Apr ’24
Device activity/screen time api
I have a request from a client that would like to create a solution where a central component is the ability to retrieve the users app usage. I can see that there are various api's todo that - one example is the Device Activity API. Another hope from the client is that this should be a web solution and not an app. I haven't quite been able to figure out if this is possible or not. I have three questions: Is it possible to retrieve app usage information through a REST api (or similar) outside an iOS app? Is it possible to use the appleId as a kind of auth solution for a web application Are there any similar APIs for older versions than iOS 15?
Replies
0
Boosts
0
Views
609
Activity
Apr ’24
Device Activity Report for multiple children
How can I differentiate between multiple children in order to display screentime data to the user separately for a parent with multiple children? As far as I can see, the options are .children, and .all? I understand I can retrieve some info from within the extension so I can group data separately, but is there any way to reliably filter between different children from within my app, say using a DeviceActivityFilter? Many thanks!
Replies
0
Boosts
1
Views
670
Activity
Apr ’24
Background Safari Tab contributing to DeviceActivityMonitor usage threshold?
I am currently debugging an issue with DeviceActivityMonitor where the threshold is reached even though the target app (e.g. Instagram) is not being used actively. I noticed that the device with the unexpected behavior had the instagram.com website opened in the Safari web browser (among hundreds of other tabs). That tab was not actively used either (not in foreground, Safari app neither used). However, I was wondering if it can happen that this website is contributing towards the threshold as well even though it is in background and not used? Otherwise I cannot explain myself this strange behavior.
Replies
0
Boosts
0
Views
700
Activity
Apr ’24
I would like to create several schedules for an activity can I do that
currently when I try to set several schedules to the same activity the latest schedule I set is the only one I understand I can have only 1 schedule for activity. ? I thought about setting a new schedule on the intervalDidEnd but, I get no interval did start if the current time is in the middle of the interval I set For example, now it is 15:00, and my previous interval started at 14:00 and ends at 16:00 but the user sets a new interval from 14:30 - 16:40 I call the deviceActivityCenter.stopMonitoring([someActivityName]) and get noIntervalDidEnd event Then I set the new interval successfully with deviceActivityCenter.startMonitoring(someActivityName, during: deviceActivitySchedule) and get no intervalDidStartEvent So how can I achieve several intervals? If I had gotten the events of the start and end it would be possible Thanks for the help
Replies
3
Boosts
0
Views
1.6k
Activity
Apr ’24
eventDidReachThreshold is working as expected only when the app is in debug mode
As per our code, we have the apps to be shielded whenever the threshold is reached. According to this use-case, our code in DeviceActivityExtension looks something like: override func eventDidReachThreshold(_ event: DeviceActivityEvent.Name, activity: DeviceActivityName) { super.eventDidReachThreshold(event, activity: activity) defaults?.setValue(event.rawValue, forKey: "appLimitEventName") defaults?.setValue(true, forKey: "appLimitReached") defaults?.synchronize() // using darwinNotificationCenter to trigger callback in the application let darwinNotificationCenter = DarwinNotificationsManager.sharedInstance() darwinNotificationCenter.postNotification(withName: "nextAppLimitInitiated") // using Notifications to debug since print doesn't work scheduleNotification(with: "interval threshold reached") } And in our application, we have the shielding logic in place, init() { let darwinNotificationCenter = DarwinNotificationsManager.sharedInstance() darwinNotificationCenter.register(forNotificationName: "nextAppLimitInitiated"){ print("callback received") let appLimitReached = self.defaults?.bool(forKey: "appLimitReached") let appLimitEventName = self.defaults?.string(forKey: "appLimitEventName") if appLimitReached ?? false, appLimitEventName != "" { // this sends the notification when callback is received self.scheduleNotification(with: "init start") self.defaults?.setValue(false, forKey: "appLimitReached") guard var dataArray = self.defaults?.array(forKey: "appLimitdataArray"), !dataArray.isEmpty else { return } let appLimitData = dataArray.first as! NSDictionary let appLimitKey = appLimitData["appLimitId"] as! String let data = self.getSchedule(key: appLimitEventName ?? "") if let appTokens = data?.applicationTokens { for token in appTokens { if !self.applicationTokens.contains(appTokens) { self.applicationTokens.insert(token) } } } self.store.shield.applications = self.applicationTokens self.store.shield.applicationCategories = ShieldSettings.ActivityCategoryPolicy.specific(self.categoryTokens, except: Set()) dataArray.removeFirst() //dataArray.append(appLimitData) self.defaults?.set(dataArray, forKey: "appLimitdataArray") self.initiateMonitoring(initiateAgain: true) self.scheduleNotification(with: "init end") } } } This works as expected for multiple App Limits but only when the device is connected to the Xcode. If we disconnect the device from Xcode/ stop application from Xcode/ try in release mode, the callback is not received from extension to the app/init block. When the device is connected to Xcode, if the apps hit the threshold, they are shielded automatically. But if the device is disconnected/ app is in release mode, the apps are not shielded automatically even after the threshold is reached. It is shielded later only after opening our app once. Please let me know if I'm doing anything wrong in receiving callback or in my shielding logic. If I need to place the shielding logic in the extension, please tell me how I can handle multiple appTokens.
Replies
2
Boosts
0
Views
1.2k
Activity
Mar ’24
DeviceActivityMonitor Schedule Pause
I am trying to understand how to approach 'x minute' pauses for a DeviceActivitySchedule. For instance, I would like to let the user pause for 5 minutes from an active schedule (meaning un-shielding the apps and re-applying the shield after the 5 min has passed). The only way that came to my mind was calling the following: Calling .startMonitoring to start monitoring a new event with the same apps starting .now and ending .now + 5 minutes; Calling in the intervalDidStart, store.shield.applications.subtract(apps) so that the apps are removed from the shield. Calling in the intervalDidEnd, store.shield.applications = apps so that the apps are now shielded again. The problem is that, from the Apple Developer Documentation: The minimum interval length for monitoring device activity is fifteen minutes. So the minimum pause I could offer to the user would be 15 minutes. And that tells me this approach is most likely wrong, because all other Screen Time apps, like Opal, Jomo, AppBlock offer also 5 min pause. Does anyone know / can think of a different and better approach?
Replies
1
Boosts
0
Views
1.5k
Activity
Mar ’24
all my DeviceActivitySchedules are killed when granting unrelated 4th party app access to screen time API permission
I have two repeating DeviceActivitySchedules running in my app and noticed a super strange behavior: whenever I modify Screen Time API permission settings (granting an additional app permission, or removing permission for a different app (completely different unrelated screen time apps from App Store)) all my DeviceActivitySchedules are cancelled and the intervalDidEnd callbacks are called my permission is not modified in this scenario this is a super strange and unexpected behavior is anyone able to reproduce this? for me this happens 100% of the times I do this any known workarounds?
Replies
0
Boosts
0
Views
790
Activity
Mar ’24
In DeviceActivityReportScene count numberOfPickups for given Application
How can I count the number of pickups for a certain application? The docs say that there is a struct ApplicationActivity within the DeviceActivityData: https://developer.apple.com/documentation/deviceactivity/deviceactivitydata Using this code I receive a warning in XCode: Value of type 'DeviceActivityData' has no member 'applicationActivity' struct ApplicationPickupCountReport: DeviceActivityReportScene { // Define which context your scene will represent. let context: DeviceActivityReport.Context = .applicationPickupCount // Define the custom configuration and the resulting view for this report. let content: (String) -> ApplicationPickupCountView func makeConfiguration(representing data: DeviceActivityResults<DeviceActivityData>) async -> String { var totalPickups = 0 let totalPickups = await data.flatMap { $0.applicationActivity }.reduce(0, { $0 + $1.numberOfPickups }) return "\(totalPickups)" } }
Replies
0
Boosts
0
Views
725
Activity
Mar ’24
DeviceActivityEvent with threshold spanning over midnight → threshold not working
Hello, I am using a DeviceActivityEvent to limit access to an app after the user has spent x minutes on it. Sometimes it can happen that a DeviceActivitySchedule spans from 11:55pm - 12:25am (just an example). In these cases, I have noticed, the DeviceActivityMonitorExtension’s eventDidReachThreshold is not called after the user has reached the threshold time interval in the observed app. I assume this is because we have transitioned over midnight and something weird happens to the DeviceActivitySchedule. I’ve tried adding the day components to the DeviceActivitySchedule as well, but then it fails completely. Any advice how to handle this? I could, of course, create two separate DeviceActivitySchedules: one for before midnight, and one for after. But depending on the user’s real app usage that could lead to slightly different (and unexpected) behavior.
Replies
0
Boosts
0
Views
782
Activity
Mar ’24
eventDidReachThreshold triggered while target app is in background
Hello! I am using the eventDidReachThreshold callback in the DeviceActivityMonitor in order to shield a target app after the user has spend x amount of time on it (e.g. x = 5 minutes). Many times this works fine, and I can trigger my shield after the specified threshold has been met. However sometimes, when they leave the target app before the threshold has been reached, the eventDidReachThreshold callback gets called randomly while they are doing something else on their phone (e.g. using a different app, on the Home Screen, phone locked…). From my perspective this does not make sense, since they are not actively spending time on the target app, and that time should not be counted towards the target app’s threshold. And it is also very confusing for the users because they will then find a blocked target app even though they haven’t used their time budget completely. This is not related to the intervalDidStart / intervalDidEnd callbacks, because they are not correlating with the timing of when the eventDidReachThreshold callback is called unexpectedly. Any ideas what this could be related to?
Replies
0
Boosts
0
Views
698
Activity
Mar ’24
DeviceActivityCenter - couldn’t communicate with a helper application.
Hi there, In rare cases (about 0.2% of the time?), I'm seeing calls to startMonitoring on an instance of DeviceActivityCenter throw an error with localizedDescription "couldn’t communicate with a helper application." I am certain I am passing valid parameters to the startMonitoring call because sometimes a naive retry after a few seconds succeeds. Similarly, I am certain that FamilyControls permissions have been granted by the user. Was hoping to get more color from the systems team on what some causes of this error are and if it is avoidable by the programmer.
Replies
3
Boosts
0
Views
1.8k
Activity
Mar ’24
[iOS 17.4] Default value for DeviceActivityEvent's includesPastActivity should be set to true
Starting with iOS 17.4, the DeviceActivityEvent initializer includes a new parameter named includesPastActivity.

The default value for this parameter is set to false, whereas device activity events have behaved as though this parameter were set to true up until now. This breaking change is a MAJOR ISSUE for developers who used device activity events in their apps before iOS 17.4 because their apps might not work the way they intended after the update. They'll have to release new app versions that specifically set includesPastActivityto true. In my opinion, the default value for includesPastActivity should be true to avoid disrupting events scheduled on older versions of iOS. I have filed an enhancement report (FB13573556) about this. I really hope this is changed before the official iOS 17.4 release.
Replies
4
Boosts
4
Views
1.8k
Activity
Mar ’24
DeviceActivityMonitor does not trigger UI updates
There is a way to allow DeviceActivityMonitor to make UI changes? Currently I have some code in the intervalDidStart and intervalDidEnd that updates Core Data entities, and it works correctly. However, the changes are only reflected if the app is terminated and opened again. Thus, if the app is opened and one of the methods inside the DeviceActivityMonitor is being called, the UI won't be updated. PS: I am using @FetchRequest to retrieve the core data entities called BlockEntity, and I have a little circle in the UI which should indicate the status of the block: active/inactive, so it would be nice to update the color in real time when intervalDidStart or intervalDidEnd are called.
Replies
0
Boosts
0
Views
715
Activity
Mar ’24
Providing Pre-Selections to FamilyActivityPicker?
I am able to correctly select and store the tokens of apps and categories my users will block, but I am unable to pre-populate Picker whenever the app is rebooted with previously selected and stored tokens. Below is the selection variable I am passing to the picker... var selectionsToBlock = FamilyActivitySelection() { willSet { saveSelection(selection: newValue) blockersSelected = true } } Is there any way I can provide my existing blockers (shown below) so that the user can easily edit their list of restricted apps from the Picker? func savedBlockers() -> FamilyActivitySelection? { let defaults = UserDefaults.standard guard let data = defaults.data(forKey: userDefaultsKey) else { return nil } return try? decoder.decode( FamilyActivitySelection.self, from: data ) }
Replies
2
Boosts
0
Views
949
Activity
Feb ’24
DeviceActivityMonitor Extension not working
I am developing a project with the Screen Time API, and I cannot understand why the methods inside DeviceActivityMonitor extension are not being called. Some points to note: I do start by requesting authorization from the User (this is working as expected when opening the app) Both the DeviceActivityMonitor Extension and the main app are under the same App Group I have added Family Controls capability to both targets and have the developer account to use it. I start by calling center.startMonitoring() I have overridden the original methods. Yet, when startMonitoring is called, there is no action that is supposed to be taken that is being taken, and as far as I can tell, the methods aren't even being called. Here are some relevant snippets of code: // EXT class DeviceActivityMonitorExtension: DeviceActivityMonitor { override func intervalDidStart(for activity: DeviceActivityName) { super.intervalDidStart(for: activity) let store = ManagedSettingsStore(named: .daily) } override func intervalDidEnd(for activity: DeviceActivityName) { super.intervalDidEnd(for: activity) let store = ManagedSettingsStore(named: .daily) store.clearAllSettings() } override func eventDidReachThreshold(_ event: DeviceActivityEvent.Name, activity: DeviceActivityName) { super.eventDidReachThreshold(event, activity: activity) let store = ManagedSettingsStore(named: .daily) let model = BeeFreeModel.shared let applications = model.selectionToDiscourage.applicationTokens let categories = model.selectionToDiscourage.categoryTokens let webDomains = model.selectionToDiscourage.webDomainTokens store.shield.applications = applications.isEmpty ? nil : applications store.shield.applicationCategories = categories.isEmpty ? nil : ShieldSettings.ActivityCategoryPolicy.specific(categories) store.shield.webDomains = webDomains.isEmpty ? nil : webDomains } } // APP extension DeviceActivityName { static let daily = Self("daily") } extension DeviceActivityEvent.Name { static let discouraged = Self("discouraged") } let schedule = DeviceActivitySchedule( intervalStart: DateComponents(hour: 0, minute: 0, second: 0), intervalEnd: DateComponents(hour: 23, minute: 59, second: 59), repeats: true ) class BeeFreeSchedule { static public func setSchedule() { print("Setting schedule...") print("Hour is: ", Calendar.current.dateComponents([.hour, .minute], from: Date()).hour!) let events: [DeviceActivityEvent.Name: DeviceActivityEvent] = [ .discouraged: DeviceActivityEvent( applications: BeeFreeModel.shared.selectionToDiscourage.applicationTokens, categories: BeeFreeModel.shared.selectionToDiscourage.categoryTokens, webDomains: BeeFreeModel.shared.selectionToDiscourage.webDomainTokens, threshold: BeeFreeModel.shared.thresholdToDiscourage ) ] let center = DeviceActivityCenter() do { print("Try to start monitoring...") // Call startMonitoring with the activity name, schedule, and events try center.startMonitoring(.daily, during: schedule, events: events) } catch { print("Error monitoring schedule: ", error) } } } // APP class BeeFreeModel: ObservableObject { // Import ManagedSettings to get access to the application shield restriction let store = ManagedSettingsStore() //@EnvironmentObject var store: ManagedSettingsStore @Published var selectionToDiscourage: FamilyActivitySelection @Published var thresholdToDiscourage: DateComponents @Published var setOfApps: [String] init() { selectionToDiscourage = FamilyActivitySelection() thresholdToDiscourage = DateComponents() var setOfAppIdentifiers: Set<String?> = Set<String?>() setOfApps = [String]() if selectionToDiscourage.applicationTokens.isEmpty {} else { for application in BeeFreeModel.shared.selectionToDiscourage.applications { setOfAppIdentifiers.insert(application.localizedDisplayName) setOfApps = setOfAppIdentifiers.compactMap { $0 }.sorted() } } } class var shared: BeeFreeModel { return _BeeFreeModel } func setSelection() { let applications = BeeFreeModel.shared.selectionToDiscourage } func changeThreshold(threshold: DateComponents) { thresholdToDiscourage = threshold } } // APP /// ... Button("Select Apps to Discourage") { isDiscouragedPresented = true } .familyActivityPicker(isPresented: $isDiscouragedPresented, selection: $model.selectionToDiscourage) } .onChange(of: model.selectionToDiscourage) { BeeFreeModel.shared.setSelection() } // ... // ... Button("Save Time") { saveTime() let new_threshold = DateComponents(hour: savedTime?.hours, minute: savedTime?.minutes, second: savedTime?.seconds) model.changeThreshold(threshold: new_threshold) } } .onChange(of: model.thresholdToDiscourage) { BeeFreeSchedule.setSchedule() // ... I believe I am using the latest stable version of Xcode (recently deleted and reinstalled). I'm really new to mobile development to Swift so any help is greatly appreciated. Thank you! :)
Replies
5
Boosts
1
Views
1.5k
Activity
Feb ’24
Shield Configuration Extension sometimes doesn't update on app switcher
Sometimes the app switcher doesn't show the correct shield configuration if there is like 7 or more apps in the app switcher. if I clear all apps from app switcher cache and leave 4 apps, the correct shield configuration will show every time instantly when I change the shield configuration from my parent app and go back to the app switcher; but if there is like 7 or more apps in the app switcher, I can see some of the app's shield configurations don't update instantly and I have to scroll past the app in the app switcher and go back to for it to update to the correct shield if it does at all. This is misleading to users and in the case of my app detrimental to one of its core functionalities override func configuration(shielding application: Application) -> ShieldConfiguration { let sharedUserDefaults = UserDefaults(suiteName: SharedUserDefaults.suiteName.rawValue)! let isPaidOn = sharedUserDefaults.bool(forKey: SharedUserDefaults.paidSwitchKey.rawValue) if isPaidOn == true { return ShieldConfig.paid } else { return ShieldConfig.free } }
Replies
0
Boosts
0
Views
763
Activity
Feb ’24
App with DeviceActivityReport extension either won't install on device or won't upload to TestFlight
We have integrated DeviceActivityReport to our Family Controll app. All capabilities are added. We built the app and tested it, but when we want to upload to TestFlight the following error happened (see attached image) When we are adding NSExtensionMainStoryboard or NSExtensionPrincipalClass the following error appears during installation process. DeviceReport.appex with id com.example.example.DeviceReport defines either an NSExtensionMainStoryboard or NSExtensionPrincipalClass key, which is not allowed for the extension point com.apple.deviceactivityui.report-extension
Replies
2
Boosts
2
Views
791
Activity
Jan ’24
Screen Time API Access
Having tried to gain access to the total daily screen time API using the text/code from the below thread, we were unable to get any data access. Has anyone else had the same issue and any resolutions? Not looking for application limits, just statistics to inform user through our 3rd party application. Thanks for any guidance around areas we can attempt https://developer.apple.com/documentation/deviceactivity/deviceactivitydata/activitysegment
Replies
0
Boosts
0
Views
706
Activity
Jan ’24