Hello,
I am trying to create a Home Screen widget for iOS that displays device usage statistics — similar to the built-in Screen Time widget Apple provides. The goal is to show the average device usage for a specified period (daily, weekly, or monthly) and optionally include a comparison with the previous period.
I noticed that Apple’s own Screen Time widget presents such information. However, after reviewing the public documentation, I could not find any available API that allows a developer to create a similar experience.
To explore possible alternatives, I implemented a SwiftUI view inside a com.apple.deviceactivityui.report-extension using the Family Controls and Device Activity frameworks. The view works fine within the main app and the report extension context, but when I attempted to use the same view in a WidgetKit extension, I received an error at runtime. This suggests that views from com.apple.deviceactivityui.report-extension are not usable inside widgets, which I understand may be due to sandboxing or limitations of how the extension points are designed.
So far, I’ve found no way to access cumulative or average usage data (screen time, app usage, etc.) from system APIs that can be shown in a widget context. My understanding is that Family Controls and Device Activity frameworks allow observing ongoing activity and building usage reports inside the app, but do not provide access to the same historical or summarized data that Apple’s own widgets display.
Could you please confirm:
Whether there is any supported way to access average device usage (screen time) data for use in a widget?
If not, is this an intentional limitation due to privacy concerns, or is there a roadmap for exposing such APIs in the future?
Are there any APIs or entitlements that could allow similar functionality via WidgetKit?
Thank you for your time and support.
Best regards,
Device Activity
RSS for tagMonitor web and app usage through custom time windows and events.
Posts under Device Activity tag
77 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I found that in iOS 18.5, DeviceActivityCenter startMonitoring cannot start multiple monitors, even if their names and periods are different, and restarting the phone cannot solve it. My app uses DeviceActivityCenter, which makes the entire function unusable. (I have less than 20 monitors)
Topic:
App & System Services
SubTopic:
General
Tags:
Family Controls
Device Activity
Managed Settings
Hi everyone,
I’m experimenting with the new ScreenTime DeviceActivityReport view in SwiftUI (iOS 17 / Xcode 15).
My goal is to show the report inside a Button (or, more generally, capture any tap on it) so that I can push a detail screen when the user selects it.
Here’s the minimal code that reproduces the issue:
import FamilyControls
import DeviceActivity
import SwiftUI
struct ScreenTimeView: View {
let center = AuthorizationCenter.shared
@State private var context: DeviceActivityReport.Context =
.init(rawValue: "Total Activity")
@State private var filter = DeviceActivityFilter(
segment: .hourly(
during: Calendar.current.dateInterval(of: .day, for: .now)!
),
users: .all,
devices: .init([.iPhone, .iPad])
)
var body: some View {
ZStack {
DeviceActivityReport(context, filter: filter)
}
.onAppear {
Task {
do {
try await center.requestAuthorization(for: .individual)
} catch {
print("Authorization failed:", error)
}
}
}
}
}
struct ContentView: View {
var body: some View {
ScrollViewReader { _ in
ScrollView(showsIndicators: false) {
VStack {
Button {
print("BUTTON TAPPED") // ← never fires
} label: {
ScreenTimeView()
.frame(height: UIScreen.main.bounds.height * 1.4)
}
}
}
}
}
}
**
What happens**
DeviceActivityReport renders correctly with hourly bars.
Tapping anywhere inside the Button does not trigger print("BUTTON TAPPED").
I’ve tried replacing Button with .onTapGesture, adding .contentShape(Rectangle()), and .allowsHitTesting(true), but nothing registers.
What I’ve checked
Authorisation succeeds—calling code in .onAppear prints no errors.
Removing DeviceActivityReport and replacing it with a plain Rectangle() lets the tap gesture fire, so the issue seems specific to DeviceActivityReport.
Hello! I am a relatively new Apple developer and am almost done with my first app. I am implementing the Screen Time API to my app because the app is designed to help the user digitally detox and I am trying to make it so the user can select which apps they would like to monitor from a list of their apps on their phone so I am using the family activity picker but I just can't extract the data needed to track the apps. I am wondering how to do this. Thank you!
Topic:
App & System Services
SubTopic:
General
Tags:
Frameworks
Family Controls
Device Activity
Screen Time
Hi, I have a released screentime app ScreenZen. The last few days I've seen a disturbing spike in bug reports coming from people with 17.4.1 and 17.5.1 phones with no update to the app itself. People reported they saw the issue immediately after updating their iOS version. Unfortunately it is not replicable on all phones with those versions, so we haven't been able to replicate it on our test phones.
It appears the issue is the ApplicationToken passed into ShieldActionExtension and ShieldConfigurationExtension does not match any of the ApplicationTokens that the user selected to block through FamilyControls. (The selected ApplicationTokens are being loaded through a group UserDefaults and they are indeed being loaded in the ShieldActionExtension in the bug reports).This is preventing the app from loading the correct settings and handling the blocking accordingly. I am trying to isolate this better with a new release with better logging, but would appreciate any help on this issue.
Hi I'm trying to build a screen time app and the Apple docs and APIs really leave something to be desired. I want to block apps on a schedule. That's it. To do this I need my DeviceActivityMonitorExt to know which apps to block. I need to somehow pass a DeviceActivitySelection from the main app into this DeviceActivityMonitorExt. How can I do this?
How can I use the Screen Time API to set a restriction for a child account from my app running on the parent’s account?
I’m creating an app with the Screen Time API and I would like to know how to make the app show a parental control editing view for parents and a view for child accounts that shows which apps are blocked. How can I do this?
I created a ShieldConfigurationExtension in Xcode 14.3 with File > New > Target > ShieldConfigurationExtension. This created the extension with all the necessary Info.plist values (correct NSExtensionPrincipalClass, etc.), with the extension included in embedded content in the host app target.
No matter what I try, the extension is not getting invoked when I shield applications from my host app. The custom UI does not show as the shield, and looking at the debugger, an extension process is never invoked.
I am shielding categories like this:
let managedSettings = ManagedSettingsStore()
...
managedSettings.shield.applicationCategories = .all()
And my extension code overrides all the ShieldConfigurationDataSource functions.
class ShieldConfigurationExtension: ShieldConfigurationDataSource {
override func configuration(shielding application: Application) -> ShieldConfiguration {
return ShieldConfiguration(
backgroundBlurStyle: UIBlurEffect.Style.systemThickMaterial,
backgroundColor: UIColor.white,
icon: UIImage(systemName: "stopwatch"),
title: ShieldConfiguration.Label(text: "You are in a Present Session", color: .yellow)
)
}
override func configuration(shielding application: Application, in category: ActivityCategory) -> ShieldConfiguration {
return ShieldConfiguration(
backgroundBlurStyle: UIBlurEffect.Style.systemThickMaterial,
backgroundColor: UIColor.white,
icon: UIImage(systemName: "stopwatch"),
title: ShieldConfiguration.Label(text: "You are in a Present Session", color: .yellow)
)
}
override func configuration(shielding webDomain: WebDomain) -> ShieldConfiguration {
return ShieldConfiguration(
backgroundBlurStyle: UIBlurEffect.Style.systemThickMaterial,
backgroundColor: UIColor.white,
icon: UIImage(systemName: "stopwatch"),
title: ShieldConfiguration.Label(text: "You are in a Present Session", color: .yellow)
)
}
override func configuration(shielding webDomain: WebDomain, in category: ActivityCategory) -> ShieldConfiguration {
return ShieldConfiguration(
backgroundBlurStyle: UIBlurEffect.Style.systemThickMaterial,
backgroundColor: UIColor.white,
icon: UIImage(systemName: "stopwatch"),
title: ShieldConfiguration.Label(text: "You are in a Present Session", color: .yellow)
)
}
}
What am I missing?
Topic:
App & System Services
SubTopic:
General
Tags:
Extensions
Managed Settings
Family Controls
Device Activity
There is an inconsistent issue when views are rendered from the Device Activity Report Extension. This issue is noticeable only on release versions and it works fine in debug mode.
Around 80% of the times, the Report Views return blank screen and this is only the case when a weekly/monthly filter is used. Although, it works as expected for daily report views.
My questions are:
How are all the Report Activity Views working fine in debug mode but not in release mode?
How the daily activity filter works fine in the release mode but the weekly/monthly filters don't work? Is this because of a memory limit issue in the extension?
As of now, I have the family-controls(distribution) entitlement only for the app and for the extensions I only have family-controls(development) entitlement. Do I need to request for family-controls(Distribution) entitlement even for the extensions?
I have seen threads on the forum mentioning the blank screen issue associated with the DeviceActivityReport but haven't found a solution to it. Any suggestions/feedback would be of great help, thanks.
Topic:
App & System Services
SubTopic:
General
Tags:
Extensions
Entitlements
Device Activity
Screen Time
I'm using FamilyActivityPicker to get consent for app/category management, which returns a FamilyActivitySelection object.
I serialize this FamilyActivitySelection object (just applicationTokens and categoryTokens) and pass it to my DeviceActivityMonitor extension via App Group UserDefaults. I am using the JSON encoder/decoder over PropertyList (though both seem to exhibit the same behavior).
After inspecting the FamilyActivitySelection object immediately after it's returned by FamilyActivityPicker in the main app, the application.bundleIdentifier property is consistently nil for every Application object within selection.applications. Similarly, category.localizedDisplayName is nil for ActivityCategory objects. This happens whether "Select All Apps" is used or if apps/categories are selected individually.
I understand that this is the intended behavior due to Apple's user privacy policies. I read on another post that my app can be provided with bundle identifiers and app names within Shield Configuration extensions and Device Activity Report extensions - I'm not sure which ones or how exactly to do this.
I am aware that I can use Label(applicationToken) SwiftUI view to display the app name/icon, but this doesn't give programmatic access to the bundleIdentifier string.
My app will not log or export these bundleIdentifiers outside of its sandbox. My goal is to create mappings to the FamilyActivitySelection with the publicly accessible bundleIdentifiers.
Any guidance, examples, or clarification on the intended workflow for this scenario would be greatly appreciated!
Hi everyone,
I'm following up on this post I made earlier about an issue I'm having with FamilyControls and the DeviceActivityMonitor extension not working for external TestFlight testers.
To briefly recap:
I have official Apple approval for the com.apple.developer.family-controls entitlement (distribution)
The entitlement is added to both my main app and the DeviceActivityMonitor extension
The App Group is correctly configured for both targets
On internal TestFlight builds, everything works as expected: app blocking works, the extension runs, and selected apps are shielded.
On external TestFlight builds, users get the Screen Time permission prompt, can select apps to block, but nothing is blocked.
Since that post, I submitted a Code Level Support request, and Apple asked me to file a bug report via Feedback Assistant. I did that almost a month ago.
The only reply I’ve received since is that they can’t give a timeframe or guarantee it will be resolved. I'm stuck in limbo with no updates and no fix.
This feature is critical to my app and I cannot launch without it. I’ve reached out to other developers who use app blocking, and none of them have run into this issue. My setup seems correct, and Apple has not said otherwise.
If anyone has experienced something similar, found a workaround, or knows how to get real movement on a bug report like this, I would really appreciate any help. It’s been weeks, and I just want to launch my app.
Thanks so much.
Topic:
Code Signing
SubTopic:
Certificates, Identifiers & Profiles
Tags:
TestFlight
Family Controls
Device Activity
For an iOS app that runs in both child and parent mode across iOS devices. On the child device, with Family Controls enabled using .child permissions via AuthorizationCenter.requestAuthorization(for: .child).
Is any way to display a list of recently used apps by the child on the parent (guardian) device, in a privacy-preserving and Apple-compliant way?
Hello everyone,
I’m working on an iOS app that uses the new DeviceActivity framework to monitor and report user screen‐time in an extension (DeviceActivityReportExtension). I need to persist my processed screen‐time data into a standalone SQLite database inside the extension, but I’m running into issues opening and writing to the database file.
Here’s what I’ve tried so far:
import UIKit
import DeviceActivity
import SQLite3
class DeviceActivityReportExtension: DeviceActivityReportExtension {
private var db: OpaquePointer?
override func didReceive(_ report: DeviceActivityReport) async {
// 1. Construct path in app container:
let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.mycompany.myapp")
let dbURL = containerURL?.appendingPathComponent("ScreenTimeReports.db")
// 2. Open database:
if sqlite3_open(dbURL?.path, &db) != SQLITE_OK {
print("❌ Unable to open database at \(dbURL?.path ?? "unknown path")")
return
}
defer { sqlite3_close(db) }
// 3. Create table if needed:
let createSQL = """
CREATE TABLE IF NOT EXISTS reports (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT,
totalScreenTime DOUBLE
);
"""
if sqlite3_exec(db, createSQL, nil, nil, nil) != SQLITE_OK {
print("❌ Could not create table: \(String(cString: sqlite3_errmsg(db)))")
return
}
// 4. Insert data:
let insertSQL = "INSERT INTO reports (date, totalScreenTime) VALUES (?, ?);"
var stmt: OpaquePointer?
if sqlite3_prepare_v2(db, insertSQL, -1, &stmt, nil) == SQLITE_OK {
sqlite3_bind_text(stmt, 1, report.date.description, -1, nil)
sqlite3_bind_double(stmt, 2, report.totalActivityDuration)
if sqlite3_step(stmt) != SQLITE_DONE {
print("❌ Insert failed: \(String(cString: sqlite3_errmsg(db)))")
}
}
sqlite3_finalize(stmt)
}
}
However:
Path issues: The extension’s sandbox is separate from the app’s. I’m not sure if I can use the same App Group container, or if there’s a better location for an on‐extension database.
Entitlements: I’ve added the App Group (group.com.mycompany.myapp) to both the main app and the extension, but the file never appears, and I still get “unable to open database” errors.
My questions are:
How do I correctly construct a file URL for an SQLite file in a DeviceActivityReportExtension?
Is SQLite the recommended approach here, or is there a more “Apple-approved” pattern for writing data from a DeviceActivity extension?
Any sample code snippets, pointers to relevant Apple documentation, or alternative approaches would be greatly appreciated!
I'm working with the FamilyControls and DeviceActivity frameworks in iOS (Swift).
In my app, I collect selected apps using a FamilyActivitySelection, and I access the selected apps via selection.applicationTokens, which gives me a Set.
I would like to get either the bundle identifier or the display name of the selected apps from these ApplicationTokens.
I tried creating an Application instance using:
let app = Application(token: token)
print(app.bundleIdentifier)
print(app.localizedDisplayName)
However, both bundleIdentifier and localizedDisplayName are always nil.
My questions are:
Outside the extension (in the main app), how can I get the bundleIdentifier or display name from an ApplicationToken?
Is there an Apple-recommended way to resolve a Token into something human-readable or usable?
If not, what is the best practice to store or identify user-selected apps for later use?
Environment:
iOS 17,
Swift 5,
Using FamilyControls and DeviceActivity APIs.
Thank you for any help!
All
After about 20 hours straight of working on this and having scrapped it twice I am realizing I should have asked everyone here for help.
I am just trying to get device activity report extension to work inside an existing app.
I have been heavily using family controls, managedsettings and deviceactivity and decided it would be nice to output some of the app usage so the User (parent) can see their children's app usage.
I installed the target via xcode, confirmed group names match, and think I have it embedded correctly but when I run the app and call the view within the extension to show minutes used by any apps it just shows no time has been used. In addition, when I put print statements into the extension they do not show up in console.
I have confirmed the main app target->Build phases->Link binary with Libraries has:
ManagedSettings.framework
FamilyControls.Framework
DeviceActivity.framework
I have confirmed in xcode that the main app target->Build phases -> Embed Foundation Extensions has:
ShieldConfiguration.appex
ShieldActionExtension.appex
DeviceActivityMonitor.appex
I have confirmed in xcode that the main app target->Build phases-> Embed ExtensionKit Extensions has:
UsageReportExtension.appex
I have used the apps I am trying to show data for extensively in the last 36 hours.
Here is my UsageReportExtension info.plist
EXAppExtensionAttributes
EXExtensionPointIdentifier
com.apple.deviceactivityui.report-extension
.entitlement
com.apple.developer.family-controls
com.apple.security.application-groups
group.com.jrp.EarnYourTurnMVP2.data
Here is the file in the app (timebankview.swift) calling the extension/showing the extension view(AppUsageReportView.swift)
import DeviceActivity
import ManagedSettings
struct TimeBankView: View {
@EnvironmentObject private var appState: AppState
@State private var reportInterval: DateInterval = {
let calendar = Calendar.current
let now = Date()
let yesterdayDate = calendar.date(byAdding: .day, value: -1, to: now) ?? now
return DateInterval(start: yesterdayDate, end: now)
}()
private var reportFilter: DeviceActivityFilter {
let selection = appState.screenTimeController.currentSelection
return DeviceActivityFilter(
segment: .daily(during: reportInterval),
users: .children,
devices: .all,
applications: selection.applicationTokens,
categories: selection.categoryTokens
// webDomains: selection.webDomains // Add if needed
)
}
var body: some View {
ZStack {
Color.appTheme.background(for: appState.isParentMode)
.edgesIgnoringSafeArea(.all)
ScrollView {
VStack(spacing: 20) {
Text("Time Bank")
DeviceActivityReport(.childUsageSummary, filter: reportFilter)
Here is AppUsageReportView.swift
import SwiftUI
struct AppUsageReportView: View {
let config: DetailedAppUsageConfiguration // Use the detailed config
var body: some View {
VStack {
Text("App Usage Details")
Text("Total Screen Time: \(config.totalDurationFormatted)")
if config.applicationsUsed.isEmpty {
Text("No specific app usage data available for the selected period/filter.")
} else {
Text("Apps Used:")
List {
ForEach(config.applicationsUsed) { appInfo in
HStack {
Image(systemName: "app.dashed")
Text(appInfo.appName)
.lineLimit(1)
Text(appInfo.durationFormatted)
Here is AppUsageReportScene.swift:
import SwiftUI
import ManagedSettings
struct AppInfo: Identifiable, Hashable {
let id = UUID()
let appName: String
let durationFormatted: String
}
struct DetailedAppUsageConfiguration {
var totalDurationFormatted: String = "Calculating..."
var applicationsUsed: [AppInfo] = []
}
struct AppUsageReportScene: DeviceActivityReportScene {
let context: DeviceActivityReport.Context = .childUsageSummary
let content: (DetailedAppUsageConfiguration) -> AppUsageReportView
func makeConfiguration(representing data: DeviceActivityResults<DeviceActivityData>) async -> DetailedAppUsageConfiguration {
var config = DetailedAppUsageConfiguration()
var appDurations: [String: TimeInterval] = [:]
var totalAggregatedDuration: TimeInterval = 0
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second]
formatter.unitsStyle = .abbreviated
formatter.zeroFormattingBehavior = .pad
var segmentCount = 0
var categoryCount = 0
var appCount = 0
for await activityData in data {
// Check segments
var tempSegmentCount = 0
for await segment in activityData.activitySegments {
segmentCount += 1
tempSegmentCount += 1
totalAggregatedDuration += segment.totalActivityDuration
var tempCategoryCount = 0
for await categoryActivity in segment.categories {
categoryCount += 1
tempCategoryCount += 1
var tempAppCount = 0
for await appActivity in categoryActivity.applications {
appCount += 1
tempAppCount += 1
let appName = appActivity.application.localizedDisplayName ?? "Unknown App"
let duration = appActivity.totalActivityDuration
appDurations[appName, default: 0] += duration
}}} }
config.totalDurationFormatted = formatter.string(from: totalAggregatedDuration) ?? "N/A"
config.applicationsUsed = appDurations
.filter { $0.value >= 1
.map { AppInfo(appName: $0.key, durationFormatted: formatter.string(from: $0.value) ?? "-") }
.sorted { lhs, rhs in
let durationLHS = appDurations[lhs.appName] ?? 0
let durationRHS = appDurations[rhs.appName] ?? 0
return durationLHS > durationRHS
}
if !config.applicationsUsed.isEmpty {
for (index, app) in config.applicationsUsed.enumerated() {
}
} else {
}
return config
}}
UsageReportExtension.swift
struct UsageReportExtension: DeviceActivityReportExtension {
init() {
print("🚀 [UsageReportExtension] Extension initialized at \(Date())")
print("🔍 [UsageReportExtension] Process info: \(ProcessInfo.processInfo.processName) PID: \(ProcessInfo.processInfo.processIdentifier)")
}
var body: some DeviceActivityReportScene {
let _ = print("📊 [UsageReportExtension] Building report scenes at \(Date())")
TotalActivityReport { totalActivity in
print("🕰️ [TotalActivityReport] Creating view with data: \(totalActivity)")
return TotalActivityView(totalActivity: totalActivity)
}}}
Topic:
App & System Services
SubTopic:
General
Tags:
Device Activity
Family Controls
Managed Settings
Screen Time
If I present "SFSafariViewController" when a "FamilyActivityPicker" is visible, it will automatically dismiss the "SFSafariViewController" and crash the "FamilyActivityPicker."
I'm assuming the cause of the bug is that each is in a separate process (aside from the app), and there's some hacks to try to stop "FamilyActivityPicker" from crashing, and this is causing the new bug because "SFSafariViewController" is also in a separate process.
(I'm not 100% if its just in 18.4 or iOS 18 overall...)
(I'll try to file a feedback soon, but its 100% reproducible for me across multiple devices on iOS 18.4)
Hi,
I'm trying to make use of the Device Activity Labels where you supply an ApplicationToken. I can successfully get it to show the icon + title of the Application (twitter in my case) but I cannot get the styling to work.
// Works
.labelStyle(.iconOnly)
.labelStyle(.titleOnly)
.border(...)

// Does NOT work
.font(.largeTitle)
.foregroundColor(.blue)
I have checked the same style (or just modifiers) against a standard Label and they actually do work in the code below.
// This is an application token. Some style not applied.
Label(targetApp)
.labelStyle(MyStyle())
// Showing the same style using a simple label. All styles correctly applied.
Label("Twitter", systemImage: "video.square.fill")
.labelStyle(MyStyle())
Is changing the font + color of the title for this Label(_ applicationToken:) supported?
Topic:
App & System Services
SubTopic:
General
Tags:
Managed Settings
Family Controls
Device Activity
Hello, I want to echo the DeviceActivityReport "concurrency" problems flagged in https://developer.apple.com/forums/thread/720549, and ask a related question. (Thanks to Kmart and other Apple dev support folks who have been monitoring these forums and responding diligently.)
I would like to display daily and weekly stats in the same view, broken down by specific apps (as in the native Screen Time). However, instantiating multiple DeviceActivityReport objects with different filters and/or different contexts leads to confusion, where the two views will incorrectly and intermittently swap data or duplicate data where it shouldn't (seemingly upon some interval when the extension provides fresh data). There isn't documentation on how to display multiple reports at once. Is the idea that logic for multiple reports should be embedded within the extension itself in the makeConfiguration() function and there should only be a single DeviceActivityReport in the main App, or is this a bug?
Even with a single DeviceActivityReport, I run into inconsistencies where the View provided by the extension takes multiple seconds to load or fails to load altogether. The behavior seems random...I will build the application with the same code multiple times and see different behavior each time.
Finally, a plug for better support in the Simulator for the entire set of Screen Time APIs.
Thanks!
Topic:
App & System Services
SubTopic:
General
Tags:
Family Controls
Device Activity
Screen Time
wwdc2022-110336
We developing a app called Parentgeenee. It's a Parental control app having any limitations on app block from child mobile. Trying to block more than 500 apps but not blocking if any particular method to block a bulk apps.
Topic:
App & System Services
SubTopic:
General
Tags:
Managed Settings
Family Controls
Screen Time
Device Activity