The data displayed about a child’s apps can be outdated (DeviceActivityReport), leading to misinformation for the user. When I access the “Screen Time” section (for child in the parent device) in the iPhone settings, I see there is an update functionality to force load the actual data.
I have tried various workarounds, such as attempting to force an update on the child’s device to call DeviceActivityReport and opening system settings, but none of these have been successful :(
How can I implement something similar? Is there a way to force update this data ?
Family Controls
RSS for tagPrevent access to the Screen Time API without guardian approval and provide opaque tokens that represent apps and websites.
Posts under Family Controls tag
179 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hi, so, we grabbed a couple of nice new watches the other week (Ultra for dad, SE for teenage son). Mostly cool and working together (calls, messages, maps, walkie talkies, etc, etc). All good.
But then son said, "Dad, why can't I see my sleep stuff like you can..?". He was right, it wasn't working. Looking around a bit, it turns out that there are a bunch of things that are turned off or not available when pairing a kids watch with dad's phone.
From the Apple page: "The following features and apps are not available: Medications, respiratory rate, irregular heart rhythm notifications, ECG, AF History, Cycle Tracking, Sleep, Wrist Temperature, Blood Oxygen, Walking Steadiness, Audiobooks, Remote, News, Shortcuts and the double-tap gesture".
Now dev-me reacts to this situation with: "Ok, so let's just build a little standalone sleep app for son's watch. There must be lots of parents out there who would like the same thing". And there are also a bunch of other "family sharing" enabled apps that when you try and use them on kid's phone, say "iPhone requireed", i.e, they don't apparently work with just a watch hooked up to mum or dad's phone.
So before I dive into that kind of project, which seems like an obvious fix path from a dev and a parents' point of view: does anybody know if this from Apple's point of view is a hardware, a software or a legal/age limitation? What's the basic framework/dev/design issue here?
Is it something on the device(s) that prevents sleep data from even being collected on family/kids paired watches? (Therefore don't bother trying to build an app); I assume not becauses it's just a normal SE used by a kid; or
Is it "just" that Apple hasn't wanted to make that available without a kids iPhone too (Therefore you could certainly build a standalone app to do what Apple hasn't wanted to do); or
Netiher 1 nor 2, but Apple won't even allow Sleep data collection for kids for some legal/health data reason (Therefore don't bother trying to build the app).
I'd like to block the apps selected in FamilyActivityPicker individually when a certain threshold is met.
For example, let's say the threshold is 15 minutes, and I want to block both Photos and Freeform. If I spend 15 minutes on Photos, Photos should be blocked. Then, if I spend 15 minutes on Freeform, Freeform should also be blocked.
Currently, only Photos gets blocked after 15 minutes, but Freeform does not. How can I fix this problem so that each app is blocked individually when its respective 15-minute threshold is met?
Thank you in advance
File 1 :
class GlobalSelection {
static let shared = GlobalSelection()
var selection = FamilyActivitySelection()
private init() {}
}
extension DeviceActivityName{
static let daily = Self("daily")
}
@objc(DeviceActivityMonitorModule)
class DeviceActivityMonitorModule: NSObject {
private let store = ManagedSettingsStore()
@objc
func startMonitoring(_ limitInMinutes: Int) {
let schedule = DeviceActivitySchedule(
intervalStart: DateComponents(hour: 0, minute: 0),
intervalEnd: DateComponents(hour: 23, minute: 59),
repeats: true
)
let threshold = DateComponents(minute: limitInMinutes)
var events: [DeviceActivityEvent.Name: DeviceActivityEvent] = [:]
// Iterate over each selected application's token
for token in GlobalSelection.shared.selection.applicationTokens {
// Create a unique event name for each application
let eventName = DeviceActivityEvent.Name("dailyLimitEvent_\(token)")
// Create an event for this specific application
let event = DeviceActivityEvent(
applications: [token], // Single app token
threshold: threshold
)
// Add the event to the dictionary
events[eventName] = event
}
// Register the monitor with the activity name and schedule
do {
try DeviceActivityCenter().startMonitoring(.daily, during: schedule, events: events)
print("24/7 Monitoring started with time limit : \(limitInMinutes) m")
} catch {
print("Failed to start monitoring: \(error)")
}
}
@objc
static func requiresMainQueueSetup() -> Bool {
return true
}
}
FIle 2 :
class DeviceActivityMonitorExtension: DeviceActivityMonitor {
let store = ManagedSettingsStore()
var blockedApps: Set<ApplicationToken> = []
func scheduleNotification(with title: String) {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
if granted {
let content = UNMutableNotificationContent()
content.title = "Notification" // Using the custom title here
content.body = title
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false) // 5 seconds from now
let request = UNNotificationRequest(identifier: "MyNotification", content: content, trigger: trigger)
center.add(request) { error in
if let error = error {
print("Error scheduling notification: \(error)")
}
}
} else {
print("Permission denied. \(error?.localizedDescription ?? "")")
}
}
}
// Function to retrieve selected apps
func retrieveSelectedApps() -> FamilyActivitySelection? {
if let sharedDefaults = UserDefaults(suiteName: "group.timelimit.com.zerodistract") {
// Retrieve the encoded data
if let data = sharedDefaults.data(forKey: "selectedAppsTimeLimit") {
// Decode the data back into FamilyActivitySelection
let decoder = JSONDecoder()
if let selection = try? decoder.decode(FamilyActivitySelection.self, from: data) {
return selection
}
}
}
return nil // Return nil if there was an error
}
override func intervalDidStart(for activity: DeviceActivityName){
super.intervalDidStart(for: activity)
scheduleNotification(with: "Interval did start")
scheduleNotification(with: "\(retrieveSelectedApps())")
}
override func intervalDidEnd(for activity: DeviceActivityName) {
super.intervalDidEnd(for: activity)
}
override func eventDidReachThreshold(_ event: DeviceActivityEvent.Name, activity: DeviceActivityName) {
super.eventDidReachThreshold(event, activity: activity)
// Notify that the threshold is met
scheduleNotification(with: "Threshold met")
// Retrieve the selected apps
if let selectedApps = retrieveSelectedApps() {
// Extract the app token identifier from the event name
let appTokenIdentifier = event.rawValue.replacingOccurrences(of: "dailyLimitEvent_", with: "")
// Iterate over the selected application tokens
for appToken in selectedApps.applicationTokens {
// Convert the app token to a string representation (or use its debugDescription)
let tokenString = "\(appToken)"
// Check if the app token matches the token identifier in the event name
if tokenString == appTokenIdentifier {
blockedApps.insert(appToken)
// Block only the app associated with this event
store.shield.applications = blockedApps
scheduleNotification(with: "store.shield.applications = blockedApps is reached")
break
}
}
} else {
scheduleNotification(with: "No stored data for selectedAppsTimeLimit")
}
}
override func intervalWillStartWarning(for activity: DeviceActivityName) {
super.intervalWillStartWarning(for: activity)
// Handle the warning before the interval starts.
}
override func intervalWillEndWarning(for activity: DeviceActivityName) {
super.intervalWillEndWarning(for: activity)
// Handle the warning before the interval ends.
}
override func eventWillReachThresholdWarning(_ event: DeviceActivityEvent.Name, activity: DeviceActivityName) {
super.eventWillReachThresholdWarning(event, activity: activity)
// Handle the warning before the event reaches its threshold.
}
}
Hi guys, I’m writing an app that integrates Screen Time API more specifically one that has only DeviceActivityReport.
The app runs as expected but I have an issue when pushing it to AppStoreConnect:
Provisioning profile failed qualification: Profile doesn't support Family Controls (Development).
Provisioning profile failed qualification: Profile doesn't include the com.apple.developer.family-controls entitlement.
Provisioning profile failed qualification: Profile doesn't support Family Controls (Development).
Provisioning profile failed qualification: Profile doesn't include the com.apple.developer.family-controls entitlement.
My app has Family Controls (Development) set in Capabilities, entitlement file, provisioning profile and distribution certificates.
I have already requested Apple for the entitlement key for Family Controls (I’m waiting for them to reply).
So, I have few questions now:
Can I distribute a build for internal testers via TestFlight without waiting for Apple’s reply to my request?
Do I need to wait Apple’s reply to distribute the app to internal tester under Family Controls (Development) ?
Has anybody had the same issue and solved it without waiting for Apple to approve the request during development (no distribution)?
I have done a deep search about this topic but it is still not clear to me if an internal build could be distributed under Family Controls (Development) or if it is mandatory to wait for the distribution one
I need some assistance with the Screen Time API’s DeviceActivityReport extension. I know the extension is sandboxed but I need the data inside my app. Jomo is currently doing this so it’s not impossible. I see they’re saying it’s an estimate which is about 5 - 10 off of the actual screen time, but how are they doing this?
Any attempt to store the screen time data inside some sort of database or UserDefaults always fails of course due to the sandbox.
Any advice would be greatly appreciated!
Hello, I'm currently facing some technical difficulties in implementing features related to application restrictions using the ScreenTime API.
In our app, we allow users to set up restrictions for specific apps and app categories, with scheduled times and days (for example, Mondays and Thursdays, from 2pm to 5pm). The blocking sessions must run independently and simultaneously, allowing different sets of applications to be restricted at different times. However, I ran into two main problems:
1. Applying restrictions in the DeviceActivityMonitor extension:
Although I can enable and disable restrictions, I haven't found an effective way to apply multiple FamilyActivitySelections directly in the DeviceActivityMonitor extension. The extension has to manage different blocking sessions independently, restricting different sets of applications and categories simultaneously or separately.
I would like to know if it is possible to transmit this list of selected applications via UserDefaults or CoreData to the extension in order to facilitate this integra
To better illustrate, here is a snippet of the code I am using:
import Foundation
import FamilyControls
import ManagedSettings
import DeviceActivity
class AppBlockManager: ObservableObject {
private let store = ManagedSettingsStore()
private let center = DeviceActivityCenter()
@Published var activitySelection: FamilyActivitySelection
private var activityName: DeviceActivityName
private var schedule: DeviceActivitySchedule
init(selection: FamilyActivitySelection, activityName: DeviceActivityName, schedule: DeviceActivitySchedule) {
self.activitySelection = selection
self.activityName = activityName
self.schedule = schedule
}
func startBlock() {
do {
try center.startMonitoring(activityName, during: schedule)
if let applications = activitySelection.applications.isEmpty ? nil : activitySelection.applicationTokens {
store.shield.applications = applications
}
if let categories = activitySelection.categories.isEmpty ? nil : activitySelection.categoryTokens {
store.shield.applicationCategories = ShieldSettings
.ActivityCategoryPolicy
.specific(categories)
store.shield.webDomainCategories = ShieldSettings
.ActivityCategoryPolicy
.specific(categories)
}
if let webDomains = activitySelection.webDomains.isEmpty ? nil : activitySelection.webDomainTokens {
store.shield.webDomains = webDomains
}
} catch {
print("Error starting monitoring: \(error)")
}
}
func stopBlock() {
store.shield.applications = nil
store.shield.webDomains = nil
store.shield.applicationCategories = nil
store.shield.webDomainCategories = nil
center.stopMonitoring([activityName])
}
}
Currently, this AppBlockManager is part of the main app target, not within the DeviceActivityMonitor extension, which is currently empty. With this configuration, I can only have one blocking session active at a time, and when it is deactivated, all restrictions are removed. I tried using different ManagedSettingsStore instances, each named individually, but without success.
2. Problems with scheduling restrictions:
Currently, when setting up scheduled monitoring via DeviceActivitySchedule, the restrictions are activated immediately, ignoring the specific times scheduled (e.g. starting at 2pm and ending at 5pm). I need the schedule to work correctly, applying the restrictions only during the defined periods.
Alternatively, I've considered running a background task that checks whether active sessions (up to a maximum of 3) should apply the restrictions at that time, but I'm still looking for a more suitable solution.
In view of these challenges, I would like some guidance on the following points:
What would be the best way to configure the DeviceActivityMonitor extension to receive and apply different FamilyActivitySelections, ensuring that the blocking sessions are independent and can run simultaneously?
Is there a recommended approach to ensure that restrictions scheduled via DeviceActivitySchedule are applied and removed according to the times and days defined by the user, ensuring that applications are restricted only during the scheduled periods?
ActivityCategoryPolicy.all(except: allowList) blocks all apps, even the ones that should be exempted
Hello fellow Screen Time developers!
I have bee experimenting with the ActivityCategoryPolicy.all(except: allowList) API of the ManagedSettings framework in order to provide a digital detox feature to my app, where ALL apps would be blocked, except some important ones (phone, messages, maps).
The apps to be exempted can be configured by the user via the FamilyActivitySelection().
However, I am experiencing a strange bug, where all apps are restricted / blocked, even the ones that are set to be exempted and are part of the allowList Set.
Instead of not restricting allowed apps at all, these apps are restricted with a generic shield (screenshot below) – a concrete shield is not requested for them from my ShieldConfigurationDataSource.
I have also filed a radar under this number, attached is a minimalistic sample project and video that shows how to reproduce this: FB15500605
(no response so far)
I have also seen other people report on this issue, but couldn't find any useful information there:
https://forums.developer.apple.com/forums/thread/750988
https://forums.developer.apple.com/forums/thread/762287
Thanks a lot for your help, and have a nice day!
Hello,
I think it is quite a common use-case to open the parent app that owns the ShieldActionDelegate when the user selects an action in the Shield.
There are only three options available that we can do in response to an action:
ShieldActionResponse.none
ShieldActionResponse.close
ShieldActionResponse.defer
It would be great if this new one would be added as well:
ShieldActionResponse.openParentApp
While finding a workaround for now, the problem is that the ShieldActionDelegate is not a normal app extension. That means, normal tricks do not work to open the parent app from here.
For example, UIApplication.shared.open(url) does not work because we can’t access UIApplication from the ShieldActionDelegate unfortunately.
NSExtensionContext is also not available in the ShieldActionDelegate unfortunately, so that’s also not possible.
There are apps however, that managed to find a workaround, in my research I stumbled across these two:
https://apps.apple.com/de/app/applocker-passcode-lock-apps/id1132845904?l=en-GB
https://apps.apple.com/us/app/app-lock/id6448239603
Please find a screen recording (gif) attached.
Their workaround is 100% what I’m looking for, so there MUST be a way to do so that is compliant with the App Store guidelines (after all, the apps are available on the App Store!).
I had documented my feature request more than 2 years ago in this radar as well: FB10393561
Hello fellow Screen Time Fans!
I am encountering a strange problem since I started working with the Screen Time framework, and I don’t know what I’m doing wrong:
Imagine the app has two ManagedSettingsStores:
one to block apps during work hours (let’s say from 9am to 5pm) and one to block apps in the evening (let’s say from 5:30pm till midnight).
Imagine, the user has blocked Instagram in both.
When the user has Instagram open at 4:59pm it shows the Block during Work Hours Shield (so far, so good).
At 5pm, the shield is removed, and the user can use Instagram.
Then, at 5:30 the a shield is activated again: this time, the Instagram token is added to the evening store.
However, there is no new ShieldConfiguration requested from the ShieldConfigurationDataSource.
Instead, the previous shield from the work hour block is re-used and shown.
To me, it appears that the Framework does not request new shields, when the token is moved from one store to another while the app remains in foreground.
The Shield is only re-rendered when the user closes the shielded app and re-opens it.
This is really confusing behavior and I would like to fix it.
Did anyone here encounter something similar, and has a suggestion or workaround?
My feedback is also documented in FB14237883.
Core Data not returning results in ShieldConfiguration Extension, but works fine in other extensions
Hi everyone,
I’m using Core Data in several extensions (DeviceActivityMonitor, ShieldAction, and ShieldConfiguration). It works perfectly in DeviceActivityMonitor and ShieldAction. I’m able to successfully fetch data and log the correct count using a fetch request.
However, when I try the same setup in the ShieldConfiguration extension, the fetch request always returns 0 results. The CoreData and App Group setup appears to be correct since the first two extensions fetch the expected data.
I’ve also previously tested storing the CoreData objects separately in a JSON-FIle using FileManager and it worked without issues—though I’d prefer not to handle manual encoding/decoding if possible.
The documentation mentions that the extension runs in a sandbox, restricting network requests or moving sensitive content. But shouldn’t reading data (from a shared App Group, for instance) still be possible within the sandbox, as it is the case with the Files, what is the difference there? In my case, I only need to read the data, as modifications can be handled via ShieldActionExtension.
Any help would be greatly appreciated!
Even on iOS 18 (16-17 also repo) we are seeing a crash on the FamilyActivityPicker when users tap on "Other" or just at random times. We see the follow debug message but no other way of identifying the issue in code.
[u 3C8AF272-DC4E-55C4-B8C6-34826D2BEB5B:m (null)] [com.apple.FamilyControls.ActivityPickerExtension(1150.1)] Connection to plugin invalidated while in use.
Even with the most basic implementation of FamilyActivityPicker (example below) we can repro this crash consistently.
Big applications (think Opal) see the same issue but it seems like they see it less, and are able to intercept the disconnect in order to show an error to the user. My two questions are
How can we intercept this crash/disconnect in order to alert our user and restart the experience?
Is this EVER gonna get fixed properly?
Usage Example:
var body: some View {
NavigationView {
ZStack {
familyPickerErrorView
.opacity(isHidden ? 0 : 1)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation {
isHidden = false
}
}
}
VStack {
Color.clear
.frame(height: 1)
.background(Color(UIColor.systemBackground))
FamilyActivityPicker(
headerText: "Select Apps To Be Blocked (Maximum of 50)",
footerText: "Want to block Safari? Check our FAQs",
selection: $familySelection)
.ignoresSafeArea(.all)
}
}
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
isPresented = false
}) {
Text("Cancel")
.foregroundColor(.black)
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
isPresented = false
}) {
Text("Done")
}
}
}
.navigationBarTitleDisplayMode(.inline)
.alert(isPresented: $showAlert) {
Alert(title: Text("Family Activity Picker Issue"), message: Text(alertMessage), dismissButton: .default(Text("OK")))
}
.onAppear {
isPresented = true
}
}
I am developing an app using DeviceActivity.
let schedule = DeviceActivitySchedule(
intervalStart: DateComponents(hour : 0, minute : 0, second: 1),
intervalEnd: DateComponents(hour : 23, minute : 59, second: 59),
repeats: true
)
I found that on the second day, intervalDidStart(for: DeviceActivityName) gets called multiple times.
I also tried
let schedule = DeviceActivitySchedule(
intervalStart: DateComponents(hour : 0, minute : 0, second: 1),
intervalEnd: DateComponents(hour : 23, minute : 59, second: 59),
repeats: false
)
and started monitoring for the next day in the intervalDidEnd(for: DeviceActivityName) method.
but , intervalDidStart(for: DeviceActivityName) still gets called multiple times.
How should I resolve this issue?
Hi, I noticed that all my apps are sometimes blocked even if I don’t have set up screen time for them. How can I fix it?
I use a parental control app and it should block apps only if I enable child mode, but after the iOS 18 update, it blocks apps even when I am still in parent mode.
But only until the update to iOS 18.0.1 came along just a few weeks later. EVERY TIME there's an update it breaks family share and screen time. EVERY TIME single time! And to fix, you must have the kid's phone in hand (easier for parents with little kids, not so easy with teens, also not easy for kids whose parents are split up and kid spends one week with you and one week with other parent). Then you have to make sure they are both on the same iOS version, then you have to log into iCloud and set up family sharing all over again. THIS SUCKS. Apple you can do better.
Hello Apple Developer Community,
We're developing a parental control app using Apple's ScreenTime API and Family Sharing capabilities. We've encountered several persistent issues that are affecting our users' experience. We've found similar reports from other developers, suggesting these might be widespread problems. We're hoping to get some insight or solutions from the community or Apple experts.
Issues we're facing:
Parent apps visible on child's device:
After granting Family Sharing permission on the family picker, sometimes the parent's apps are visible instead of the child's apps.
Related issue: https://forums.developer.apple.com/forums/thread/749672
Inconsistent app visibility on family picker:*
The behaviour of the family picker is unpredictable:
Sometimes, no apps are visible at all, only categories.
Other times, categories are displayed and upon selection, enforcement works correctly.
In some instances, the stream delivering updates to the selection from the app extension doesn't send anything.
Related issue: https://forums.developer.apple.com/forums/thread/729198
Rules not enforced with different OS versions:
When the parent and child devices are running different iOS versions (both above iOS 16), ScreenTime rules don't seem to work correctly.
General inconsistencies:
We've observed various other inconsistencies in the behavior of the ScreenTime API. These issues are less predictable but contribute to an overall unreliable user experience.
Steps to Reproduce:
Add a child device using a parental control app (in our case, Adora for Kids).
Grant the Family Sharing permission.
Open the family picker to set up ScreenTime rules. Observe the inconsistent behaviour:
a. Sometimes no apps are visible.
b. Sometimes only categories are visible.
c. Sometimes both categories and apps are visible.
When categories are visible, select a category and attempt to enforce a rule.
Enforce a ScreenTime rule. Ensure the parent and child devices are running different iOS versions (both iOS 16+).
Test various ScreenTime rules and observe their enforcement across different device configurations.
Questions:
Are these known issues with the ScreenTime API?
What could be causing the inconsistent behavior in the family picker and the stream of updates from the app extension?
Are there any workarounds or best practices to mitigate these problems?
Is there any additional information we can provide to help investigate these issues?
Are there any plans to improve the stability and consistency of the ScreenTime API in future iOS releases?
We've tried researching these issues through various channels, including Apple's documentation and community forums, but haven't found definitive solutions. Any insights or assistance would be greatly appreciated.
Thank you for your time and help!
Environment:
Development: Xcode 15.4, macOS 14.2.1
Runtime: iOS 16+
App: Adora (App Store ID: 1671825554)
Hello! I am developing an app using the Screen Time API. Everything is good, but I have a problem with DeviceActivityReport. On the child’s device, stats are synced to the app in about 1-5 minutes. However, on the parent’s device, it can take around an hour or more. How can I make the stats sync faster between the child’s device and the parent’s device?
How I Implemented It
@Published var context: DeviceActivityReport.Context = .init("Time Limit")
let filter = DeviceActivityFilter(
segment: .daily(
during: Calendar.current.dateInterval(of: .day, for: .now)!
),
users: .children,
devices: .all,
applications: applicationTokens
)
DeviceActivityReport(viewModel.context, filter: viewModel.filter)
.frame(maxHeight: viewModel.maxReportHeight)
In the Report Extension (Test Code)
for await d in data {
result += "device.name=\(d.device.name ?? "")"
result += "\nuser.appleID=\(d.user.appleID ?? "")"
result += "\ngivenName=\(d.user.nameComponents?.givenName ?? "")"
result += "\nrole=\(d.user.role.rawValue)"
for await activity in d.activitySegments {
result += "\nactivitySegments hashValue=\(activity.hashValue)"
result += "\ntotalActivityDuration=\(activity.totalActivityDuration)"
result += "\ndateInterval=\(activity.dateInterval)"
for await category in activity.categories {
result += "\ncategory=\(category.category.localizedDisplayName ?? "")"
for await app in category.applications {
result += "\napp=\(app.application.bundleIdentifier ?? ""), time=\(app.totalActivityDuration)"
}
}
}
}
Problems:
activity.categories can be empty for a long time
app.totalActivityDuration contains outdated information
I need to wait about 1+ hours to get “actual” information.
Family activity picker looses icon and incudes parent apps after 20 seconds...and you never see the kids icons again until you reboot. Any ideas?
family controls authorized on both apps
Share across devices activated
Web & activity activated.
Before...
...and after 22 seconds.
About 5 weeks ago, I requested a Family Controls Entitlement using this form:
https://developer.apple.com/contact/request/family-controls-distribution
I have yet to hear anything back. About a week ago, I submitted a support request through my Apple developer account, asking about the entitlement, and have also not heard anything back.
Is it normal to wait this long? Is there a way to check on the status of my request? Is there some other channel I should be using to ask for information about this process?
A few weeks ago, I submitted a request for the Family Controls & Personal Device Usage Entitlement to enable my app to access the Managed Settings and Device Activity frameworks in the Screen Time API. The app is nearly complete, with the only remaining component being the Family Controls capability.
As of now, I haven’t received a response regarding the request. I’m wondering if anyone else has experienced a similar situation or can provide insight into the following:
• How long does it typically take to receive feedback on this type of entitlement request?
• Is there a way to check if a request is still active and under review?
• Is a finished website required for the entitlement to be granted?
Any information or guidance would be greatly appreciated!
Tl:dr What are some reasons my bundleIDs aren't showing up and does anyone have good resources to setup the screentime API/DeviceActivityMonitorExtension?
I'm working on an iOS app that uses the FamilyControls and DeviceActivity frameworks to monitor and restrict app usage. The app allows users to select apps and set usage limits. When a limit is reached, a DeviceActivityMonitorExtension should block the selected apps.
My App setup: Have a model that is called when users select apps to manage these app bundle IDs are then serialized and sent to the Device Monitor Extension via App Group so it can be used when the event threshold is reached. Cant use Application Tokens because they are not serielizable and cant be passed to the extension.
Problem: While testing, I’m unable to retrieve the bundleIdentifier and localizedDisplayName from the Application objects after selecting apps. Instead, these properties are nil or empty, preventing me from saving the bundle IDs to share with the extension via App Groups.
Assumptions: I suspect this issue is due to missing the com.apple.developer.screentime.api entitlement, which might be required to access these properties even during development. I've requested for the entitlement but its still under review.
Key Code Snippets:
Authorization Request:
class ScreenTimeManager: ObservableObject {
static let shared = ScreenTimeManager()
@Published var isAuthorized: Bool = false
func requestAuthorization() async {
do {
try await AuthorizationCenter.shared.requestAuthorization(for: .individual)
DispatchQueue.main.async {
self.isAuthorized = AuthorizationCenter.shared.authorizationStatus == .approved
print("Authorization status: \(AuthorizationCenter.shared.authorizationStatus)")
}
} catch {
DispatchQueue.main.async {
print("Authorization failed: \(error.localizedDescription)")
self.isAuthorized = false
}
}
}
}
Accessing bundleIdentifier:
print("addAppGroup() Called")
let managedApps = selection.applications.compactMap { application -> ManagedApp? in
guard let token = application.token else {
print("No token for application: \(application)")
return nil
}
let app = Application(token: token)
print("New Application instance: \(app)")
guard let bundleID = app.bundleIdentifier, !bundleID.isEmpty else {
print("Bundle identifier is empty or nil for application: \(app)")
return nil
}
let displayName = app.localizedName ?? "Unknown App"
print("Processing application with bundleIdentifier: '\(bundleID)' and displayName: '\(displayName)'")
return ManagedApp(
bundleIdentifier: bundleID,
applicationToken: token,
localizedDisplayName: displayName
)
}
if managedApps.isEmpty {
print("No managed apps created. Exiting addAppGroup().")
return
}
// Continue with creating DeviceActivityEvent...
}
Logs - Shows application token but never bundleID or LocalizedDisplayname
Application(bundleIdentifer: nil, token: Optional(128 byte <TOKEN_PRESENT>), localizedDisplayName: nil)
What I've Tried:
Ensured Screen Time is enabled on the device.
Verified App Group configuration in both app and extension.
Checked that authorization is being requested and the status is .approved.
Cleaned and rebuilt the project.
Questions:
Is the com.apple.developer.screentime.api entitlement required to access bundleIdentifier and localizedDisplayName when using .individual authorization?
Is there a way to access these properties without the entitlement, or am I missing a configuration step?
Has anyone faced a similar issue and found a solution?
Lastly, is there a good place for additional resources on the screentime API??