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! :)
I can get
intervalDidStart
to call (verified both through the debugger and by taking an obvious action; in my case shielding all apps), buteventDidReachThreshold
won't. I am 99% sure this is because the iOS Simulator doesn't trackDeviceActivity
(at least, Settings > Screen Time on the Simulator never reports app usage); so the schedules themselves will run (including their lifecycle methods), but any lifecycle methods around events won't. Only workaround was to debug on device.If I'm right please let me know and I'll post it as a proper answer below!
I should have put this in the post, but yes, I am aware that you have to test on device since iOS Simulator doesn't track device activity. In my case, even trying to shield all selected apps with intervalDidStart doesn't work on my device so I am definitely doing something wrong.