Device Activity

RSS for tag

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

Posts under Device Activity tag

93 Posts
Sort by:
Post not yet marked as solved
1 Replies
517 Views
Is it possible, through Screen time API to get the iPhone usage time raw data for each app programmatically? I want to create an app which tells me how much time I spend on each app on my iPhone.
Posted
by
Post not yet marked as solved
0 Replies
1k Views
Can't find much right now, but maybe Apple devs can share some updates ;) Documentation: https://developer.apple.com/documentation/ManagedSettings?changes=latest_minor https://developer.apple.com/documentation/DeviceActivity?changes=latest_minor https://developer.apple.com/documentation/FamilyControls?changes=latest_minor Videos: https://developer.apple.com/wwdc23/sessions/?q=screen%20time
Posted
by
Post not yet marked as solved
0 Replies
545 Views
I'm trying to populate DeviceActivityReportScene from DeviceActivityData. I have a list of custom objects of type 'DailyLimit' and I want to create separate instances of 'DeviceActivityReportScene' for each item inside the array. The following code works. This creates two separate instances of DeviceActivityReportScene for the item 0 and item 1 in the array. @main struct ReportingExtension: DeviceActivityReportExtension { let limits = Store.shared.getDailyLimits() var body: some DeviceActivityReportScene { DailyLimitActivityReport(dailyLimit: limits[0], context: DeviceActivityReport.Context(rawValue: limits[0].id)) { report in DailyLimitActivityView(activityReport: report) } DailyLimitActivityReport(dailyLimit: limits[1], context: DeviceActivityReport.Context(rawValue: limits[1].id)) { report in DailyLimitActivityView(activityReport: report) } } } struct DailyLimitActivityReport: DeviceActivityReportScene { let dailyLimit: DailyLimit let context: DeviceActivityReport.Context let content: (ActivityReport) -> DailyLimitActivityView func makeConfiguration(representing data: DeviceActivityResults<DeviceActivityData>) async -> ActivityReport { ... } } But I want to create the list of DeviceActivityReportScene dynamically like the following. @main struct ReportingExtension: DeviceActivityReportExtension { let limits = Store.shared.getDailyLimits() var body: some DeviceActivityReportScene { ForEach(limits) { limit in DailyLimitActivityReport(dailyLimit: limit, context: DeviceActivityReport.Context(rawValue: limit.id)) { report in DailyLimitActivityView(activityReport: report) } } } } But this throws an error 'No exact matches in reference to static method 'buildExpression' Any ideas to fix this issue will be highly appreciated & thanks in advance.
Posted
by
Post not yet marked as solved
0 Replies
490 Views
I am using Managed Settings to block all apps during a set period of time using the below method. override func intervalDidStart(for activity: DeviceActivityName) { super.intervalDidStart(for: activity) ManagedSettingsStore().shield.applicationCategories = .all() } When a user opens an app during that time, they should be allowed to press a button on the shield which lets them "continue using that app", just like the default iOS screen time allows for. However, this doesn't seem possible right now when the below delegate is called for ActivityCategoryToken ShieldAction: override func handle(action: ShieldAction, for category: ActivityCategoryToken, completionHandler: @escaping (ShieldActionResponse) -> Void) { switch action { case .primaryButtonPressed: completionHandler(.close) case .secondaryButtonPressed: ManagedSettingsStore().shield.applicationCategories = .all(except: category) //this is not possible right now. completionHandler(.none) @unknown default: fatalError() } } This is because the .all(except:) method takes a set of application tokens, but the handle delegate method on activityCategoryTokens only provides application category tokens. Is there no way to get around this? It would be very helpful if either a) the delegate method for ActivityCategoryToken ShieldAction also provided the ApplicationToken that was blocked (this would be preferred), or b) the .all(except: ) method also accepts ActivityCategoryTokens as an input.
Posted
by
Post not yet marked as solved
0 Replies
330 Views
[Question] Sometimes, Device Activity Report Extension provides duplicated values in DeviceActivityResults, we want to know why those duplicated DeviceActivityResults values exist? Is the value of DeviceActivityResults dependent on the setting of the function call to DeviceActivityCenter().startMonitoring?
Posted
by
Post not yet marked as solved
0 Replies
377 Views
We want to include Screen Time API access codes in not only main app (container app) but also app extension such like Location Push Service Extension or Widget Extension. On app extension side, are there any restrictions to create and use objects of below classes in either cases , main app is running or not running ? ManagedSettingsStore DeviceActivitySchedule DeviceActivityCenter and also, do they do the same thing that works on the main app side like below ? shield application create a device activity schedule startMonitoring
Posted
by
Post not yet marked as solved
0 Replies
388 Views
We're developing a Health app where we would like to track the Screen Time of the user to display relevant correlations with other health and lifestyle data. Using the Device Activity API we're able to subscribe to events when a user reaches certain thresholds, so to track this we've set up events every x minutes to be able to track progress during the day, like this: user_reached_5_minutes user_reached_10_minutes and so on.. This works fine and is stable (as long as it works). But I suspect we're running into some kind of rate limiting issue since it stops working during the day, and after that it seems to need a new day for it to start working again. Worth noting is that our monitoring interval is repeating daily, so that might very well be related. I've not been able to find any documentation on this, and no error messages indicating we're running into a limit in the logs. The only related thing I've seen is this - but it seems to suggest this should fail when calling startMonitoring and not silently fail like we're experiencing: Attempting to monitor too many activities or activities that are too tightly scheduled can cause this method to throw an error. The system throws an error if the attempt to monitor the device activity failed. To avoid errors, reduce the number of unique, tightly-scheduled activities. For example, consider using the warningTime property of an activity’s schedule. Does anyone have an idea of what is going on here?
Posted
by
Post not yet marked as solved
2 Replies
691 Views
I'm having trouble with my DeviceActivityMonitorExtension. The intervalDidStart function is not being called when the scheduler is created. Does anyone have an idea why this is? let schedule = DeviceActivitySchedule( intervalStart: DateComponents(hour: 15, minute: 23), intervalEnd: DateComponents(hour: 16, minute: 55), repeats: true ) class MySchedule { static public func setSchedule() { let center = DeviceActivityCenter() center.stopMonitoring([.daily]) do { try center.startMonitoring(.daily, during: schedule) } catch { print("Error monitoring schedule: ", error) } } } class DeviceActivityMonitorExtension: DeviceActivityMonitor { override func intervalDidStart(for activity: DeviceActivityName) { super.intervalDidStart(for: activity) SelectedApps.shared.setRestrictions() } private let _SelectedApps = SelectedApps() class SelectedApps: ObservableObject{ @Published var selection: FamilyActivitySelection let store = ManagedSettingsStore() init() { if let savedSelection = UserDefaults.standard.object(forKey: "savedSelection") as? Data { let decoder = JSONDecoder() if let loadedSelection = try? decoder.decode(FamilyActivitySelection.self, from: savedSelection) { selection = loadedSelection } else { selection = FamilyActivitySelection(includeEntireCategory: true) } } else { selection = FamilyActivitySelection(includeEntireCategory: true) } } class var shared: SelectedApps { return _SelectedApps } func setRestrictions(){ let applications = selection store.shield.applications = applications.applicationTokens.isEmpty ? nil : applications.applicationTokens store.shield.applicationCategories = applications.categoryTokens.isEmpty ? nil : ShieldSettings.ActivityCategoryPolicy.specific(applications.categoryTokens) }
Posted
by
Post not yet marked as solved
1 Replies
641 Views
I added the extension via targets. And I added code in the intervalDidStart() override to print and send a notification. But it never seems to run. Am I supposed to do any other steps after adding the extension? // Make sure that your class name matches the NSExtensionPrincipalClass in your Info.plist. class DeviceActivityMonitorExtension: DeviceActivityMonitor { func getSelectionFromUserDefaults() -> FamilyActivitySelection { let defaults = UserDefaults.standard if let encodedData = defaults.data(forKey: "ScreenTimeSelection") { let decoder = PropertyListDecoder() if let selection = try? decoder.decode(FamilyActivitySelection.self, from: encodedData) { return selection } } return FamilyActivitySelection() } override func intervalDidStart(for activity: DeviceActivityName) { super.intervalDidStart(for: activity) print("HERE!") let content = UNMutableNotificationContent() content.title = "Feed the cat" content.subtitle = "It looks hungry" content.sound = UNNotificationSound.default // show this notification five seconds from now let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false) // choose a random identifier let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger) // add our notification request UNUserNotificationCenter.current().add(request) let selection = getSelectionFromUserDefaults() let applications = selection.applicationTokens let categories = selection.categoryTokens let webCategories = selection.webDomainTokens let store = ManagedSettingsStore() store.shield.applications = applications.isEmpty ? nil : applications store.shield.applicationCategories = ShieldSettings.ActivityCategoryPolicy.specific(categories, except: Set()) store.shield.webDomainCategories = ShieldSettings.ActivityCategoryPolicy.specific(categories, except: Set()) }
Posted
by
Post not yet marked as solved
1 Replies
573 Views
I am trying to filter my DeviceActivityReport to only show activity for the specific app tokens I pass in. Right now, it shows activity for all apps. Is there something else I need to do in makeConfiguration so that it only filters the application tokens that I'm filtering by? filter = DeviceActivityFilter(segment: .hourly(during: dateInterval), applications: task.selection.applicationTokens) struct TotalActivityReport: DeviceActivityReportScene { // Define which context your scene will represent. let context: DeviceActivityReport.Context = .totalActivity // Define the custom configuration and the resulting view for this report. let content: (String) -> TotalActivityView func makeConfiguration(representing data: DeviceActivityResults<DeviceActivityData>) async -> String { // Reformat the data into a configuration that can be used to create // the report's view. let formatter = DateComponentsFormatter() formatter.allowedUnits = [.minute, .second] formatter.unitsStyle = .abbreviated formatter.zeroFormattingBehavior = .dropAll let totalActivityDuration = await data.flatMap { $0.activitySegments }.reduce(0, { $0 + $1.totalActivityDuration }) return formatter.string(from: totalActivityDuration) ?? "No activity data" } }
Posted
by
Post not yet marked as solved
1 Replies
526 Views
Hey 👋 I have an app that uses Device Activity Monitor. For the previous releases there was no problem. However I made some improvements in my app and send it to review. Review team getting following crash log but I couldn't find the issue. It's working both in my simulator and a real devices.(iPhone, iPad). I removed some logs from here because of character limitations. Exception Codes: 0x0000000000000000, 0x0000000000000000 Termination Reason: FRONTBOARD 2343432205 <RBSTerminateContext| domain:10 code:0x8BADF00D explanation:scene-create watchdog transgression: application<>:15592 exhausted real (wall clock) time allowance of 19.97 seconds ProcessVisibility: Foreground ProcessState: Running WatchdogEvent: scene-create WatchdogVisibility: Foreground WatchdogCPUStatistics: ( "Elapsed total CPU time (seconds): 7.760 (user 4.070, system 3.690), 5% CPU", "Elapsed application CPU time (seconds): 0.111, 0% CPU" ) reportType:CrashLog maxTerminationResistance:Interactive> Triggered by Thread: 0 Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0 Crashed: 0 libsystem_kernel.dylib 0x1d8256ca4 mach_msg2_trap + 8 1 libsystem_kernel.dylib 0x1d8269b74 mach_msg2_internal + 80 2 libsystem_kernel.dylib 0x1d8269e4c mach_msg_overwrite + 540 3 libsystem_kernel.dylib 0x1d82571e8 mach_msg + 24 4 libdispatch.dylib 0x1a108320c _dispatch_mach_send_and_wait_for_reply + 548 5 libdispatch.dylib 0x1a108359c dispatch_mach_send_with_result_and_wait_for_reply + 60 6 libxpc.dylib 0x1f8fa9218 xpc_connection_send_message_with_reply_sync + 240 7 Foundation 0x193f6ff18 __NSXPCCONNECTION_IS_WAITING_FOR_A_SYNCHRONOUS_REPLY__ + 16 8 Foundation 0x193f032c4 -[NSXPCConnection _sendInvocation:orArguments:count:methodSignature:selector:withProxy:] + 2192 9 Foundation 0x193f01ac0 -[NSXPCConnection _sendSelector:withProxy:arg1:] + 116 10 Foundation 0x193f019f8 _NSXPCDistantObjectSimpleMessageSend1 + 60 11 FamilyControls 0x1e772388c 0x1e76ee000 + 219276 12 FamilyControls 0x1e7722b0c 0x1e76ee000 + 215820 13 libdispatch.dylib 0x1a1067eac _dispatch_client_callout + 20 14 libdispatch.dylib 0x1a10696ec _dispatch_once_callout + 32 15 FamilyControls 0x1e7722d18 0x1e76ee000 + 216344 16 MyApp 0x102314bfc AppUsagesViewModel.init(dependencies:) + 510972 (AppUsagesViewModel.swift:29) 17 MyApp 0x1022b2b00 closure #1 in MainTabBarController.setupTabbar() + 109312 (MainTabBarController.swift:55) 18 MyApp 0x1022b3904 specialized Sequence.compactMap<A>(_:) + 112900 (<compiler-generated>:0) 19 MyApp 0x1022b2750 MainTabBarController.setupTabbar() + 108368 (MainTabBarController.swift:47) 20 MyApp 0x1022b29a8 @objc MainTabBarController.viewDidLoad() + 108968 (<compiler-generated>:0) 21 UIKitCore 0x19bf381f4 -[UITabBarController initWithNibName:bundle:] + 156 22 MyApp 0x1022b3a7c specialized static MainTabBarController.build(_:) + 113276 (MainTabBarController.swift:96) 23 MyApp 0x102313f80 specialized AppDelegate.application(_:didFinishLaunchingWithOptions:) + 507776 (AppDelegate.swift:45) 24 MyApp 0x1023132d8 @objc AppDelegate.application(_:didFinishLaunchingWithOptions:) + 504536 (<compiler-generated>:17) As far as I understood from the log the crash is happened right there AppUsagesViewModel.swift:29. I have a following variable in that line. private let parentalControls = AuthorizationCenter.shared What could be the reason of that crash? If someone can help me I really appreciate it. Thanks!
Posted
by
Post marked as solved
1 Replies
775 Views
I am trying to develop an application that can enforce Screen Time restrictions on the target device, however I get stuck at the part where I have scheduled callbacks for my class that implements the extension for DeviceActivityMonitor. For debugging, I first attach to the app extension target process via Debug > Attach to Process > Process Name. But after scheduling monitoring that receives a callback immediately, the process exits with the following error: Thread 1: EXC_RESOURCE (RESOURCE_TYPE_MEMORY: high watermark memory limit exceeded) (limit=6 MB) Is the maximum allowed memory for Device Activity Monitor extensions really just 6 MB, or am I doing something wrong? If it is indeed the case, is there any way to increase it? 6 MB seems quite restrictive to me.
Posted
by
Post not yet marked as solved
5 Replies
1.2k Views
When I try to add an extension to my Xcode project, I get the following warning: Activate “MyMonitor” scheme? This scheme has been created for the “(null)” target. Choose Activate to use this scheme for building and debugging. Schemes can be chosen in the toolbar or Product menu. I'm trying to understand a few things about this error: What exactly does the "(null)" target refer to? Could it be that Xcode is creating the scheme before the target is complete? Could this "(null)" target be the reason my code doesn't seem to be connecting properly to my app extension? How can I correct this issue? I ran into this during the process of trying to add a Device Activity Monitor Extension to my iOS project. Any hints are welcome, I have been stuck on this for too long.
Posted
by
Post not yet marked as solved
1 Replies
534 Views
We are seeing cases where DeviceActivityMonitoring extension is crashing permanently. This is due to a now-fixed bug that was causing a stack overflow. On devices where the extension crashed, it appears permanently unable to recover - even weeks after the crash and after multiple restarts of the device. It looks like recovery is being attempted, as the same four NSLog activities are emitted roughly every second that the phone is active. Logs: Process: DeviceActivityMonitoring Activity: beginning extension request Message: NSExtensionPrincipalClass `<private>` does not conform to NSExtensionRequestHandling protocol! Process: DeviceActivityMonitoring Activity: Loading Preferences From User Session CFPrefsD Message: Process: DeviceActivityMonitoring Activity: container_system_group_path_for_identifier Message: Requesting container lookup; class = 13, identifier = <private>, group_identifier = <private>, create = 1, temp = 0, euid = 501, uid = 501 container_query_get_single_result: success container_system_group_path_for_identifier: success Process: DeviceActivityMonitoring Activity: container_create_or_lookup_app_group_path_by_app_group_identifier Message: Requesting app group container lookup; personaid = 1000, type = DEFAULT, name = 9F7F4BA7-79CF-453C-B81C-568E96ADB711, origin [pid = 29, personaid = 199], proximate [pid = 3505, personaid = 199], identifier = <private>, euid = 501, uid = 501, platform = 2 containermanagerd stat [<private>]: exists: 1, isDirectory: 0, fsNode: <~~~> containermanagerd <private> is sandboxed. Issuing token for path: [<private>] (extension class: com.apple.sandbox.application-group) containermanagerd [0] command=38, client=<<~~~>, u=<501/501/~~/0/1000>, uid=501, pid=5595, sandboxed=1, platform=2 (1/1/0) [<~~~>], cached, cs cached>, error=(null) Consuming sandbox extension; path = [<private>], key = 0 Revoking sandbox extension; key = 0 Can anyone provide some insight into what is going on with the recovery attempt here? Practically, how can we recover the extension in these cases? Will an app update force the extension to restart properly? Hard reset of the device? Something else? Is there any way to check on the health of an extension from the main app so that we can deterministically know if the extension is healthy or not before relying on it?
Posted
by
Post marked as solved
1 Replies
508 Views
I recently got approved for the Family Controls (Distribution) entitlement, and since then it seems that I cannot use the DeviceActivityMonitor extension on the dev environment anymore. I've tried attaching a debugger to the DeviceActivityMonitor process but it's never called so does not attach, and I can't see why it is not attaching. I've tried reverted back to old versions which I know definitely worked, and it's still not working... However, the DeviceActivityReport extension seems to be working fine. Any help or advice on how I can actually debug this would be greatly appreciated!
Posted
by
Post not yet marked as solved
1 Replies
557 Views
Hello! Rookie Swift developer here! I am trying to make an app that uses the Screen Time API to block a list of websites. Specifically, I want it to take a list of web domains in String format and restrict them instead of using the FamilyActivityPicker. So far, I have been unsuccessful. When using the FamilyActivityPicker, I have been able to restrict websites by modifying the shield instance variable of the ManagedSettingsStore. let store = ManagedSettingsStore() // Got selection from familyActivityPicker. store.shield.webDomains = selection.webDomainTokens "store.shield.webDomains" is of type "Set<Token>" and as far as I know, you can create a WebDomain struct with a String url but not Token. I was only able to get a token using the FamilyActivityPicker. Hence, I was wondering if it was even possible to do this and if so, how you'd go about it. Thanks!
Posted
by
Post not yet marked as solved
3 Replies
761 Views
Hello! I'm new to iOS development and am developing an app that blocks certain websites. At the moment, I'm thinking of using the Network Extension capability to do the job. From what I have read, in the production version of the app, you'd need to make use of MDM profiles since NE filtering only works on supervised devices. So, I'm here to ask the community if there are better options than using this method. As far as screen time api is concerned, I believe it requires the user to specify which websites they want blocked by themselves using the activity picker so that doesn't quite work for me since i want to allow the app to block groups of websites by itself based on the user's preference. Thanks!
Posted
by
Post not yet marked as solved
2 Replies
634 Views
Hello, I have an app that you can select apps and then start monitoring. When I restrict the apps by button click, and monitor the activity, the scheduled time works and intervalDidEnd cancels shielding apps. But when I schedule shielding apps, intervalDidStart doesn't start shielding. What am I missing here? I have already added FamilyControls capability. import SwiftUI @main struct TestingScreenTimeAPIApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } } import SwiftUI struct ContentView: View { @StateObject var model = MyModel.shared @State var isPresented = false var body: some View { VStack { Button("Select Apps") { isPresented = true } .familyActivityPicker(isPresented: $isPresented, selection: $model.selectionToDiscourage) Button("Start monitoring") { model.startMonitoring() } .padding() } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } import Foundation import FamilyControls import DeviceActivity class MyModel: ObservableObject { static let shared = MyModel() private init() {} var selection: FamilyActivitySelection? = nil var selectionToDiscourage = FamilyActivitySelection() { willSet { selection = newValue } } func startMonitoring() { let intervalStart = DateComponents(hour: 11, minute: 09) let intervalEnd = DateComponents(hour: 13, minute: 14) let schedule = DeviceActivitySchedule( intervalStart: intervalStart, intervalEnd: intervalEnd, repeats: true) let center = DeviceActivityCenter() do { try center.startMonitoring(.activity, during: schedule) } catch { print ("Error: \(error)") } } } extension DeviceActivityName { static let activity = Self("activity") } import UIKit import FamilyControls class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { Task { do { try await AuthorizationCenter.shared.requestAuthorization(for: .individual) } catch { print("Error: \(error.localizedDescription)") } } return true } } import DeviceActivity import FamilyControls import ManagedSettings class DeviceActivityMonitorExtension: DeviceActivityMonitor { let store = ManagedSettingsStore() override func intervalDidStart(for activity: DeviceActivityName) { super.intervalDidStart(for: activity) let model = MyModel.shared if model.selection != nil { let applications = model.selection!.applicationTokens store.shield.applications = applications.isEmpty ? nil : applications } } override func intervalDidEnd(for activity: DeviceActivityName) { super.intervalDidEnd(for: activity) store.shield.applications?.removeAll() } }
Posted
by