Unable to get DeviceActivityMonitor to Shield Apps at Schedule

77 Views Asked by At

I'm a new Swift Developer so I apologise if this is a basic question but I couldn't find the answer anywhere.

I created a basic app to shield selected apps using FamilyControls. Note, this is for an individual phone and not a use case of parents blocking apps on a childs iPhone.

In the iOS Target, I have a Restrictions Manager class which loads apps that user wants restricted and shields them. It can remove restrictions as well.

import Foundation
import FamilyControls
import ManagedSettings
//import Combine

private let _RestrictionsManager = RestrictionsManager()

class RestrictionsManager: ObservableObject {
    @Published var activitySelection = FamilyActivitySelection()
    
    let store = ManagedSettingsStore()
    
    //private var cancellables = Set<AnyCancellable>()
    private let encoder = PropertyListEncoder()
    private let decoder = PropertyListDecoder()

    init() {
        loadSelection()
    }
    
    class var shared: RestrictionsManager {
        return _RestrictionsManager
    }
    
    func saveSelection(selection: FamilyActivitySelection) {
        
        let defaults = UserDefaults.standard
        do {
            let data = try encoder.encode(selection)
            defaults.set(data, forKey: UserDefaultsKeys.selectedApplications)
            //print("Save Selection / Applications Saved")
            
            store.shield.applications = activitySelection.applicationTokens.isEmpty ? nil : activitySelection.applicationTokens
        } catch {
            print("Failed to save selection: \(error)")
        }
    }
    
    func loadSelection() {
        let defaults = UserDefaults.standard
        guard let data = defaults.data(forKey: UserDefaultsKeys.selectedApplications) else { return }
        do {
            activitySelection = try decoder.decode(FamilyActivitySelection.self, from: data)
            //print("Load Selection / Applications Loaded")
        } catch {
            print("Failed to load selection: \(error)")
        }
        
        store.shield.applications = activitySelection.applicationTokens.isEmpty ? nil : activitySelection.applicationTokens
    }
    
    func removeRestrictions() {
        store.shield.applications = nil
    }
}

I saw some tutorial and also created this schedule file

import Foundation
import DeviceActivity

extension DeviceActivityName {
    static let daily = Self("daily")
}

extension DeviceActivityEvent.Name {
    static let blocked = Self("blocked")
}

let schedule = DeviceActivitySchedule(
    intervalStart: DateComponents(hour: 17, minute: 21),
    intervalEnd: DateComponents(hour: 23, minute: 59),
    repeats: true
)

class MySchedule {
    static public func setSchedule() {
        
        let events: [DeviceActivityEvent.Name: DeviceActivityEvent] = [
            .blocked: DeviceActivityEvent (
                applications: RestrictionsManager.shared.activitySelection.applicationTokens,
                threshold: DateComponents(second: 10)
            )
        ]
        
        let center = DeviceActivityCenter()
        do {
            try center.startMonitoring(.daily, during: schedule, events: events)
            print("Activity Monitoring Schedule Set")
        } catch {
            print ("Error monitoring schedule: \(error.localizedDescription)")
        }
        
    }
}

In the main app file, I'm setting the schedule

import SwiftUI
import FamilyControls
import ManagedSettings

@main
struct Exercise_MommyApp: App {
    @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
    
    let persistenceController = PersistenceController.shared
    @StateObject var restrictionManager = RestrictionsManager.shared
    @StateObject var store = ManagedSettingsStore()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
                .environmentObject(restrictionManager)
                .environmentObject(store)
        }
    }
}

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        
        MySchedule.setSchedule()
        
        return true
    }
}

Content View to take family controls permission

import SwiftUI
import FamilyControls

struct ContentView: View {
    @State private var isPermissionGranted = false
    
    var body: some View {
        VStack {
            if isPermissionGranted {
                AppSelectionView()
            } else {
                Text("Requesting Screen Time permissions...")
                    .padding()
                    .onAppear {
                        requestScreenTimePermissions()
                    }
            }
        }
    }
    
    private func requestScreenTimePermissions() {
        let ac = AuthorizationCenter.shared
        
        Task {
            do {
                try await ac.requestAuthorization(for: .individual)
                isPermissionGranted = true
            } catch {
                print("Error requesting Screen Time permissions: \(error)")
                // Handle permission error
            }
        }
    }
}

Application Selection View

import SwiftUI
import FamilyControls
import DeviceActivity

struct AppSelectionView: View {
    @State private var pickerIsPresented = false
    @EnvironmentObject var restrictionManager: RestrictionsManager

    var body: some View {
        VStack {
            Text("Select Apps to Monitor")
                .font(.headline)
                .padding()
            Button("Select Apps") {
                pickerIsPresented = true
            }
            .familyActivityPicker(
                isPresented: $pickerIsPresented,
                selection: $restrictionManager.activitySelection
            )
            .padding()
            
            Button("Remove Restrictions") {
                restrictionManager.removeRestrictions()
            }
            
            Button("Add Restrictions") {
                restrictionManager.loadSelection()
            }
        }
        .onChange(of: restrictionManager.activitySelection) {
            restrictionManager.saveSelection(selection: restrictionManager.activitySelection)
            restrictionManager.loadSelection()
        }
    }
}

Now the application selection view is working fine in terms of taking permissions on which apps to block, saving it. and the buttons to lock and unlock shields is working fine. The problem is in the DeviceActivityMonitor extenstion. Unable to understand how to get access to the applications list here to apply shields.

import DeviceActivity
import ManagedSettings
import SwiftUI

class DeviceActivityMonitorExtension: DeviceActivityMonitor {
    let store = ManagedSettingsStore()
    
    override func intervalDidStart(for activity: DeviceActivityName) {
        print("intervalDidStart")
        super.intervalDidStart(for: activity)
    }
    
    override func intervalDidEnd(for activity: DeviceActivityName) {
        print("intervalDidEnd")
        super.intervalDidEnd(for: activity)
    }
    
    override func eventDidReachThreshold(_ event: DeviceActivityEvent.Name, activity: DeviceActivityName) {
        print("eventDidReachThreshold")
        super.eventDidReachThreshold(event, activity: activity)
        
    }
}

Sorry for the long question but i've been stuck here for 2 days :/

In the DeviceActivityMonitorExtension, the print statements are being called but I don't know how to access the list of applicationtokens here which the user has selected.

0

There are 0 best solutions below