I am developing an app that can help users disable selected apps at a specified time, so that users can get away from their phones and enjoy real life. Here is my data structure:
extension ActivityModel {
@NSManaged public var id: UUID
@NSManaged public var name: String
@NSManaged public var weeks: Data
@NSManaged public var weekDates: Data
@NSManaged public var appTokens: Data
}
Among them, weeks is of [Bool] type, indicating which weeks from Sunday to Saturday are effective; weekDates is of [[Date,Date]] type, indicating the effective time period; appTokens is of Set<ApplicationTokens> type, indicating the selected apps。
At the beginning, I will open a main monitor:
let deviceActivityCenter = DeviceActivityCenter()
do{
try deviceActivityCenter.startMonitoring(
DeviceActivityName(activityModel.id),
during: DeviceActivitySchedule(
intervalStart: DateComponents(hour: 0,minute: 0,second: 0),
intervalEnd: DateComponents(hour: 23,minute: 59,second: 59),
repeats: true
)
)
}catch {
return false
}
Since the time range may be different every day, I will start the sub-monitoring of the day every time the main monitoring starts:
override func intervalDidStart(for activity: DeviceActivityName) {
super.intervalDidStart(for: activity)
if activity.rawValue.hasPrefix("Sub-") {
ActivityModelManager.disableApps(
Tools.getUUIDFromString(activity.rawValue)
)
return
}
let weekIndex = Calendar.current.component(.weekday, from: .now)
let weeks = ActivityModelManager.getWeeks(activity.rawValue)
if weeks[weekIndex] {
let weekDates =
ActivityModelManager.getWeekDates(activity.rawValue)
let deviceActivityCenter = DeviceActivityCenter()
do{
try deviceActivityCenter.startMonitoring(
DeviceActivityName("Sub-" + activityModel.id),
during: DeviceActivitySchedule(
intervalStart: getHourAndMinute(weekDates[weekIndex][0]),
intervalEnd: getHourAndMinute(weekDates[weekIndex][1]),
repeats: false
)
)
}catch {
return
}
}esle {
return
}
}
I will judge whether it is main monitoring or sub monitoring based on the different activity names. When the sub-monitor starts, I will get the bound application and then disable it:
static func disableApps(_ id : UUID){
let appTokens = ActivityModelManager.getLimitAppById(id)
let name = ManagedSettingsStore.Name(id.uuidString)
let store = ManagedSettingsStore(named: name)
store.shield.applications = appTokens
return
}
When the child monitoring is finished, I resume the application:
static func enableApps(_ id : UUID){
let name = ManagedSettingsStore.Name(id.uuidString)
let store = ManagedSettingsStore(named: name)
store.shield.applications = []
}
The above is my code logic. When using DeviceActivityMonitorExtension, I found the following problems:
- intervalDidStart may be called multiple times, resulting in several sub-monitors being started.
- After a period of time, the monitoring is turned off.
- The static methods enableApps and disableApps are sometimes not called