Scheduling doesn’t start and end automatically using the screentime API. The only way I have got it to work is calling MyModel.shared.setAppRestrictions() manually and then by calling MyModel.shared.stopAppRestrictions() manually to stop it.
I have tried calling setAppRestrictions from the DeviceActivityCenter too but that didn’t work. When I call setScedule() from AppDelegate then it blocks the app forever and never unblocks them on time. I am very lost.
Here are my files for both MyModel and MySchedule
import FamilyControls
import Foundation
import ManagedSettings
class MyModel: ObservableObject {
static let shared = MyModel()
let store = ManagedSettingsStore()
@Published var selectionToDiscourage: FamilyActivitySelection
private let decoder = PropertyListDecoder()
init() {
selectionToDiscourage = FamilyActivitySelection()
selectionToDiscourage = loadSelection() ?? FamilyActivitySelection()
}
func setShieldRestrictions() {
// Pull the selection out of the app's model and configure the application shield restriction accordingly
let applications = MyModel.shared.selectionToDiscourage
store.shield.applications = applications.applicationTokens.isEmpty ? nil : applications.applicationTokens
store.shield.applicationCategories = applications.categoryTokens.isEmpty
? nil
: ShieldSettings.ActivityCategoryPolicy.specific(applications.categoryTokens)
//store.application.denyAppRemoval = true
//store.dateAndTime.requireAutomaticDateAndTime = true
}
func stopAppRestrictions() {
// lock application
store.shield.applications = nil
store.shield.applicationCategories = nil
// more rules
store.media.denyExplicitContent = false
// prevent app removal
store.application.denyAppRemoval = false
print("deny app removal: ", store.application.denyAppRemoval ?? false)
// prevent set date time
store.dateAndTime.requireAutomaticDateAndTime = false
}
func loadSelection() -> FamilyActivitySelection? {
guard let data = UserDefaults.standard.data(forKey: "appModel") else {
return nil
}
return try? decoder.decode(FamilyActivitySelection.self, from: data)
}
}
import DeviceActivity
import Foundation
import SwiftUI
extension DeviceActivityName {
static let daily = Self("daily")
}
class MonitoringStatus: ObservableObject {
@Published var isMonitoring: Bool = false
}
// The Device Activity schedule represents the time bounds in which my extension will monitor for activity
enum MySchedule {
public static func unsetScedule() {
let center = DeviceActivityCenter()
if center.activities.isEmpty {
return
}
center.stopMonitoring()
}
public static func setSchedule() {
@AppStorage("fromTime") var fromTime = ""
@AppStorage("toTime") var toTime = ""
let selectedDays = getSelectedDays()
for day in selectedDays {
let weekdayNumber = dayToWeekdayNumber(day)
createSchedule(weekdayNumber: weekdayNumber, fromTime: fromTime, toTime: toTime)
}
}
private static func getSelectedDays() -> [Day] {
guard let dayStrings = UserDefaults.standard.object(forKey: "selectedDays") as? [String] else {
return []
}
return dayStrings.compactMap { Day(rawValue: $0) }
}
private static func createSchedule(weekdayNumber: Int, fromTime: String, toTime: String) {
let startTimeComponents = convertStringToDateComponents(fromTime, weekday: weekdayNumber)
let endTimeComponents = convertStringToDateComponents(toTime, weekday: weekdayNumber)
let schedule = DeviceActivitySchedule(
intervalStart: startTimeComponents,
intervalEnd: endTimeComponents,
repeats: true // This should probably be true for a weekly repeating schedule
)
let center = DeviceActivityCenter()
do {
let activityName = DeviceActivityName("daily_\(weekdayNumber)")
print("Mointor \(weekdayNumber) with activity name \(activityName)...")
try center.startMonitoring(activityName, during: schedule)
} catch {
print("Error monitoring schedule: ", error)
}
}
}
private func convertStringToDateComponents(_ timeString: String, weekday: Int) -> DateComponents {
var components = convertStringToDateComponents(timeString)
components.weekday = weekday
return components
}
private func convertStringToDateComponents(_ timeString: String) -> DateComponents {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm"
if let date = dateFormatter.date(from: timeString) {
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date)
let minute = calendar.component(.minute, from: date)
return DateComponents(hour: hour, minute: minute)
} else {
// Handle invalid string format or return a default value
return DateComponents()
}
}
private func dayToWeekdayNumber(_ day: Day) -> Int {
// Adjust based on your calendar's first weekday (e.g., Sunday = 1, Monday = 2, etc.)
switch day {
case .Sunday: return 1
case .Monday: return 2
case .Tuesday: return 3
case .Wednesday: return 4
case .Thursday: return 5
case .Friday: return 6
case .Saturday: return 7
}
}