I did a lot of researches, but cannot understand why methods inside DeviceActivityMonitor extension are not calling.
What I've seen related to this question and already checked:
Do you starting monitoring for schedules?
Yes, I do by calling center.startMonitoring() as it has been explained on WWDC2021-10123.
I was searching an issue while creating the extension. Walk through steps that are described here.
Everything looks good for me.
Did you add Family Control Capability to extension's target?
Yes, I did.
Is your device authorized with FamilyControls?
Yes. The only doubt I have here that I'm authorising FamilyControls for .individual . But as I've asked previously, it shouldn’t be a reason.
I check logs from the extension in Xcode -> Debug -> Attach to Process -> ... then in Console App trying to find logs from the extension there?
What could be wrong?
I've been through this - just posting the issues I had:
Here's my code:
In the ViewController.swift:
import UIKit
import FamilyControls
import SwiftUI
import DeviceActivity
import Combine
let schedule = DeviceActivitySchedule(
intervalStart: DateComponents(hour: 0, minute: 0, second: 0),
intervalEnd: DateComponents(hour: 23, minute: 59, second: 59),
repeats: true,
warningTime: DateComponents(minute: 14)
)
func description(for selection: FamilyActivitySelection) -> String {
var result = "Include Entire Category: \(selection.includeEntireCategory ? "Yes" : "No")\n"
// Assuming you can access the names or descriptions of applications, categories, etc.
result += "Application Tokens: \(selection.applicationTokens.count)\n"
result += "Category Tokens: \(selection.categoryTokens.count)\n"
result += "Web Domain Tokens: \(selection.webDomainTokens.count)"
return result
}
class ScreenTimeSelectAppsModel: ObservableObject {
@Published var activitySelection = FamilyActivitySelection()
init() { }
}
struct ScreenTimeSelectAppsContentView: View {
@State private var pickerIsPresented = false
@ObservedObject var model: ScreenTimeSelectAppsModel
var body: some View {
VStack {
Button {
pickerIsPresented = true
} label: {
Text("Select Apps")
}
.familyActivityPicker(
isPresented: $pickerIsPresented,
selection: $model.activitySelection
)
Text("Selected Activities: \(description(for: model.activitySelection))")
}
}
}
class ViewController: UIViewController {
let model = ScreenTimeSelectAppsModel()
private var cancellables = Set<AnyCancellable>()
// Used to encode codable to UserDefaults
private let encoder = PropertyListEncoder()
// Used to decode codable from UserDefaults
private let decoder = PropertyListDecoder()
private let userDefaultsKey = "ScreenTimeSelection"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let ac = AuthorizationCenter.shared
Task {
do {
try await ac.requestAuthorization(for: .individual)
}
catch {
print("Error getting auth for Family Controls")
}
}
// DispatchQueue.global().asyncAfter(deadline: .now() + 5) {
// print("5 seconds have passed!")
// }
let rootView = ScreenTimeSelectAppsContentView(model: model)
let controller = UIHostingController(rootView: rootView)
addChild(controller)
view.addSubview(controller.view)
controller.view.frame = view.frame
controller.didMove(toParent: self)
// Set the initial selection
model.activitySelection = savedSelection() ?? FamilyActivitySelection()
model.$activitySelection.sink { selection in
self.saveSelection(selection: selection)
}
.store(in: &cancellables)
let selection: FamilyActivitySelection = savedSelection()!
print("Selection is", selection)
let event = DeviceActivityEvent(
applications: selection.applicationTokens,
categories: selection.categoryTokens,
webDomains: selection.webDomainTokens,
threshold: DateComponents(minute: 15)
)
print("Event is", event)
print("Event applications", event.applications)
print("Schedule is", schedule)
let center = DeviceActivityCenter()
center.stopMonitoring()
let activity = DeviceActivityName("MyApp.ScreenTime")
let eventName = DeviceActivityEvent.Name("MyApp.SomeEventName")
print("Starting monitoring")
do {
try center.startMonitoring(
activity,
during: schedule,
events: [
eventName: event
]
)
} catch {
print("Error in do catch block")
}
}
func saveSelection(selection: FamilyActivitySelection) {
let defaults = UserDefaults.standard
defaults.set(
try? encoder.encode(selection),
forKey: userDefaultsKey
)
}
func savedSelection() -> FamilyActivitySelection? {
let defaults = UserDefaults.standard
guard let data = defaults.data(forKey: userDefaultsKey) else {
return nil
}
return try? decoder.decode(
FamilyActivitySelection.self,
from: data
)
}
}
And in DeviceActivityMonitorExtension.swift:
import DeviceActivity
import UserNotifications
// Optionally override any of the functions below.
// Make sure that your class name matches the NSExtensionPrincipalClass in your Info.plist.
class DeviceActivityMonitorExtension: DeviceActivityMonitor {
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 = title // Using the custom title here
content.body = "Here is the body text of the notification."
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 ?? "")")
}
}
}
override func intervalDidStart(for activity: DeviceActivityName) {
super.intervalDidStart(for: activity)
// Handle the start of the interval.
print("Interval began")
scheduleNotification(with: "interval did start")
}
override func intervalDidEnd(for activity: DeviceActivityName) {
super.intervalDidEnd(for: activity)
// Handle the end of the interval.
print("Interval ended")
scheduleNotification(with: "interval did end")
}
override func eventDidReachThreshold(_ event: DeviceActivityEvent.Name, activity: DeviceActivityName) {
super.eventDidReachThreshold(event, activity: activity)
// Handle the event reaching its threshold.
print("Threshold reached")
scheduleNotification(with: "event did reach threshold warning")
}
override func intervalWillStartWarning(for activity: DeviceActivityName) {
super.intervalWillStartWarning(for: activity)
// Handle the warning before the interval starts.
print("Interval will start")
scheduleNotification(with: "interval will start warning")
}
override func intervalWillEndWarning(for activity: DeviceActivityName) {
super.intervalWillEndWarning(for: activity)
// Handle the warning before the interval ends.
print("Interval will end")
scheduleNotification(with: "interval will end warning")
}
override func eventWillReachThresholdWarning(_ event: DeviceActivityEvent.Name, activity: DeviceActivityName) {
super.eventWillReachThresholdWarning(event, activity: activity)
// Handle the warning before the event reaches its threshold.
print("Interval will reach threshold")
scheduleNotification(with: "event will reach threshold warning")
}
}
Hope this helps! Took me a while to figure out and not much on the internet.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With