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! :)

Post not yet marked as solved Up vote post of mageswarankk Down vote post of mageswarankk
464 views
  • I can get intervalDidStart to call (verified both through the debugger and by taking an obvious action; in my case shielding all apps), but eventDidReachThreshold won't. I am 99% sure this is because the iOS Simulator doesn't track DeviceActivity (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.

Add a Comment

Replies

Just a point of note, I understand the code is a bit messy and that there is probably redundant code; I apologize, I ended up just trying a bunch of kinda random things to try and get it to work :_)

I am having the same issue, did you ever figure it out?

Add a Comment