-->

What's the most efficient way to refresh a tab

2020-05-06 10:21发布

问题:

Currently what I have is this:

AppDelegate.applicationDidBecomeActive():

func applicationDidBecomeActive(_ application: UIApplication) {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    guard let vc = self.window?.rootViewController?.children.first as! AlarmTableViewController? else {
        fatalError("Could not downcast rootViewController to type AlarmTableViewController, exiting")
    }
    vc.deleteOldAlarms(completionHandler: { () -> Void in
        vc.tableView.reloadData()
    })
}

deleteOldAlarms():

func deleteOldAlarms(completionHandler: @escaping () -> Void) {

    os_log("deleteOldAlarms() called", log: OSLog.default, type: .default)
    let notificationCenter = UNUserNotificationCenter.current()
    var activeNotificationUuids = [String]()
    var alarmsToDelete = [AlarmMO]()
    guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
        return
    }
    let managedContext = appDelegate.persistentContainer.viewContext

    notificationCenter.getPendingNotificationRequests(completionHandler: { (requests) in
        for request in requests {
            activeNotificationUuids.append(request.identifier)
        }
        for alarm in self.alarms {
            guard let alarmUuids = alarm.value(forKey: "notificationUuids") as! [String]? else {
                os_log("Found nil when attempting to unwrap notificationUuids in deleteOldAlarms() in AlarmTableViewController.swift, cancelling",
                       log: OSLog.default, type: .default)
                return
            }
            let activeNotificationUuidsSet: Set<String> = Set(activeNotificationUuids)
            let alarmUuidsSet: Set<String> = Set(alarmUuids)
            let union = activeNotificationUuidsSet.intersection(alarmUuidsSet)
            if union.isEmpty {
                alarmsToDelete.append(alarm)
            }
        }
        os_log("Deleting %d alarms", log: OSLog.default, type: .debug, alarmsToDelete.count)
        for alarmMOToDelete in alarmsToDelete {
            self.removeNotifications(notificationUuids: alarmMOToDelete.notificationUuids as [String])
            managedContext.delete(alarmMOToDelete)
            self.alarms.removeAll { (alarmMO) -> Bool in
                return alarmMOToDelete == alarmMO
            }
        }
        completionHandler()
    })

}

but it feels disgusting. Plus, I'm calling tableView.reloadData() on a background thread now (the thread executing the completion handler). What's the best way to refresh the UI once the user opens the app back up? What I'm aiming for is for these old alarms to be deleted and for the view to be reloaded. An alarm is considered old if it doesn't have any notifications pending in the notification center (meaning the notification has already been executed).

回答1:

Don't put any code in the app delegate. Have the view controller register to receive notifications when the app enters the foreground.

Add this in viewDidLoad:

NotificationCenter.default.addObserver(self, selector: #selector(enteringForeground), name: UIApplication.willEnterForegroundNotification, object: nil)

Then add:

@objc func enteringForeground() {
    deleteOldAlarms {
        DispatchQueue.main.async {
            tableView.reloadData()
        }
    }
}

As of iOS 13, you should register for UIScene.willEnterForegroundNotification. If your app needs to work under iOS 13 as well as iOS 12 then you need to register for both notifications but you can use the same selector.



回答2:

You can use NSNotification

NotificationCenter.default.addObserver(self, selector: #selector(didBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)

In didBecomeActive call tableView.reloadData(), that should be all. You should remember to unregister the observer in deinit.

NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil)