David Johansen
David Johansen

Reputation: 1

After stopping tasks in Xcode, my app wont reshield an application in the background anymore

So I have been working on this system of selecting apps to shield them. then you can unshield those applications and they should automatically reshield. however after I stop tasks in Xcode they only reshield if I put my own app in the foreground. I have a notification which generates at the same time as the app should be reshielded and interestingly it still generates after I stop tasks in Xcode but it doesnt reshield. this is my relevant code:

@main
struct YourApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL { url in
                    if url.absoluteString == "yourapp://navigateToSwiftUIView" {
                        // Navigate to SwiftUIView if this URL scheme is opened
                        NotificationCenter.default.post(name: .navigateToSwiftUIView, object: nil)
                    }
                }
        }
    }
}

class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        UNUserNotificationCenter.current().delegate = self
        
        // Register the background refresh task for reshielding
        BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.yourapp.refreshShield", using: nil) { task in
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
        }
        
        return true
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        if let appData = response.notification.request.content.userInfo["appToken"] as? Data,
           let appToken = try? JSONDecoder().decode(ApplicationToken.self, from: appData) {
            NotificationCenter.default.post(name: .navigateToSwiftUIView, object: nil, userInfo: ["appToken": appToken])
        }
        completionHandler()
    }

    // MARK: - Background Task Handling

    func handleAppRefresh(task: BGAppRefreshTask) {
        // Reschedule the background refresh
        scheduleAppRefresh()
        
        // Perform reshielding check
        TimerManager.shared.timers.keys.forEach { appToken in
            TimerManager.shared.reapplyShieldIfNeeded(for: appToken)
        }
        
        task.setTaskCompleted(success: true)
    }
    
    func scheduleAppRefresh() {
        let request = BGAppRefreshTaskRequest(identifier: "com.yourapp.refreshShield")
        request.earliestBeginDate = Date(timeIntervalSinceNow: 10) // Adjust timing as needed
        do {
            try BGTaskScheduler.shared.submit(request)
        } catch {
            print("Failed to submit BGAppRefreshTask: \(error)")
        }
    }
}
class TimerManager: ObservableObject {
     var timers = [ApplicationToken: Timer]()
    private let store = ManagedSettingsStore()
    private var unshieldedApps = Set<ApplicationToken>()
    
    // Dictionary to store unshield logs with timestamps for calculating daily, weekly, monthly counts
    private var unshieldLogs = [String: [Date]]()
    private var hourlyUnshieldLogs = [String: [Int]]()
    
    static let shared = TimerManager()
    
    private init() {
        loadUnshieldLogs()
    }
    
    func isAppUnshielded(_ appToken: ApplicationToken) -> Bool {
        return unshieldedApps.contains(appToken)
    }

    func pauseShield(for appToken: ApplicationToken) {
            unshieldedApps.insert(appToken)
            
            // Immediate unshield
            store.shield.applications?.remove(appToken)
            logUnshieldEvent(for: appToken)
            logHourlyUnshieldEvent(for: appToken)
            
            // Schedule a 10-second Timer
            let timer = Timer.scheduledTimer(withTimeInterval: 10, repeats: false) { [weak self] _ in
                self?.reapplyShieldIfNeeded(for: appToken)
                self?.timers[appToken] = nil
            }
            timers[appToken] = timer
            
            // Schedule a background notification as backup
            scheduleReapplyShieldNotification(for: appToken)
        }
    
    private func scheduleReapplyShieldNotification(for appToken: ApplicationToken) {
            let content = UNMutableNotificationContent()
            content.title = "Reapplying Shield"
            content.body = "Shield will be reapplied for the selected app."
            
            // Pass appToken data for decoding in NotificationServiceExtension
            if let appData = try? JSONEncoder().encode(appToken) {
                content.userInfo = ["appToken": appData]
            }
            
            let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
            let request = UNNotificationRequest(identifier: "ReapplyShield_\(appToken)", content: content, trigger: trigger)
            
            UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
        }
    
    func reapplyShield(for appToken: ApplicationToken) {
            store.shield.applications?.insert(appToken)
            unshieldedApps.remove(appToken)
        }
    func reapplyShieldIfNeeded(for appToken: ApplicationToken) {
            // Check if the app should still be reshielded
            if unshieldedApps.contains(appToken) {
                reapplyShield(for: appToken)
            }
        }
}
class NotificationServiceExtension: UNNotificationServiceExtension {
    private let store = ManagedSettingsStore()

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        if let appData = request.content.userInfo["appToken"] as? Data,
           let appToken = try? JSONDecoder().decode(ApplicationToken.self, from: appData) {
            
            TimerManager.shared.reapplyShield(for: appToken)
        }
        contentHandler(request.content)
    }
}

Upvotes: 0

Views: 32

Answers (0)

Related Questions