DeviceActivityMonitor.intervalDidEnd never getting called

I'm trying to build an app with a DeviceActivityMonitor extension that executes some code after 15 minutes. I can confirm that the extension is set up correctly and that intervalDidStart is executed, but for some reason the intervalDidEnd method never gets called. What I'm doing in both is just registering a local notification.

class DeviceActivityMonitorExtension: DeviceActivityMonitor {
    let store = ManagedSettingsStore()

    override func intervalDidStart(for activity: DeviceActivityName) {
        createPushNotification(
            title: "Session activated!",
            body: ""
        )

        super.intervalDidStart(for: activity)
    }

    override func intervalDidEnd(for activity: DeviceActivityName) {
        createPushNotification(
            title: "Session ended",
            body: ""
        )

        super.intervalDidEnd(for: activity)
    }

    private func createPushNotification(title: String, body: String) {
        let content = UNMutableNotificationContent()
        content.title = title
        content.body = body

        // Configure the recurring date.
        var dateComponents = Calendar.current.dateComponents([.era, .year, .month, .day, .hour, .minute, .second], from: Date().addingTimeInterval(1.0))
        dateComponents.calendar = Calendar.current
        dateComponents.timeZone = TimeZone.current

        // Create the trigger as a repeating event.
        let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)

        let uuidString = UUID().uuidString
        let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger)

        // Schedule the request with the system.
        let notificationCenter = UNUserNotificationCenter.current()
        notificationCenter.add(request)
    }
}

And this is the method that is starting the monitoring session:

    @objc public static func startSession() -> String? {
        // Calculate start and end times
        let center = DeviceActivityCenter()
        let minutes = 15
        let startDate = Date().addingTimeInterval(1)

        guard let endDate = Calendar.current.date(byAdding: .minute, value: minutes, to: startDate) else {
            return "Failed to create end date?"
        }

        // Create date components and explicitly set the calendar and timeZone
        let startComponents = Calendar.current.dateComponents([.era, .year, .month, .day, .hour, .minute, .second], from: startDate)
        let endComponents = Calendar.current.dateComponents([.era, .year, .month, .day, .hour, .minute, .second], from: endDate)

        // Create schedule
        let schedule = DeviceActivitySchedule(
            intervalStart: startComponents,
            intervalEnd: endComponents,
            repeats: false
        )

        print("Now", Date())
        print("Start", startDate, startComponents)
        print("End", endDate, endComponents)
        print(schedule.nextInterval)

        do {
            // Use a consistent activity name for our simple implementation
            let activity = DeviceActivityName("SimpleSession")
            try center.startMonitoring(activity, during: schedule)
            return nil
        } catch {
            return "Failed to start monitoring: \(error)"
        }
    }

I can confirm my dates & date components make sense with the 4 print statements. Here is the output:

Now 2025-02-12 04:21:32 +0000
Start 2025-02-12 04:21:33 +0000 era: 1 year: 2025 month: 2 day: 11 hour: 20 minute: 21 second: 33 isLeapMonth: false 
End 2025-02-12 04:36:33 +0000 era: 1 year: 2025 month: 2 day: 11 hour: 20 minute: 36 second: 33 isLeapMonth: false 
Optional(2025-02-12 04:21:33 +0000 to 2025-02-12 04:36:33 +0000)

I get the Session activated! notification but never get the Session ended notification. Half an hour later, I've tried debugging the DeviceActivityCenter by printing out the activities property and can see that it is still there. When I try to print out the nextInterval property on the schedule object i get from calling center.schedule(for:), it returns nil.

I'm running this on an iPhone 8 testing device with developer mode enabled. It has iOS 16.7.10. I'm totally lost as to how to get this to work.

some additional information:

I tried to add a warning to happen 10 minutes after the start time as well:

        guard let endDate = Calendar.current.date(byAdding: .minute, value: minutes, to: startDate),
              let warningDate = Calendar.current.date(byAdding: .minute, value: 10, to: startDate) else {
            return "Failed to create end date?"
        }

        let warningComponents = Calendar.current.dateComponents([.era, .year, .month, .day, .hour, .minute, .second], from: warningDate)

        // Create schedule
        let schedule = DeviceActivitySchedule(
            intervalStart: startComponents,
            intervalEnd: endComponents,
            repeats: false,
            warningTime: warningComponents
        )

I have the intervalWillEndWarning method in my DeviceActivityMonitorExtension class schedule a local notification and found that it gets triggered immediately after the intervalDidStart method gets triggered. Again, intervalDidEnd never gets called.

However, if I manually call center.stopMonitoring() for my session, I get the intervalDidEnd notification.

It looks like my schedule is not getting used properly but I'm not sure why..

DeviceActivityMonitor.intervalDidEnd never getting called
 
 
Q