Search code examples
iosswiftoptimizationnsdateuilocalnotification

Scheduling UILocalNotification causing lagging and delay of user interface


I have a function scheduleFutureLocalNotifications() in Swift code that creates 64 UILocalNotification that fire in the future.

Originally the function was called at viewDidLoad(), but this caused delays when starting the app.

Next, the function was called during the active application, but this caused unpredictable pauses or lagging of the user interface.

Finally, the function was moved to trigger when the app transitions to the background after receiving a UIApplicationDidEnterBackground notification, but this causes iOS to briefly lag as the local notifications are prepared in the background. This appears more evident on older devices.

Question:

1 - How can I reduce lag and improve user interface responsiveness creating local notifications?

2 - What better techniques can be employed to schedule the 64 notifications?

3 - What other better times could the function scheduleFutureLocalNotifications() be called?

Code:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        scheduleFutureLocalNotifications()
    }

    func scheduleFutureLocalNotifications() {

        // Remove all previous local notifications
        let application = UIApplication.sharedApplication()
        application.cancelAllLocalNotifications()

        // Set new local notifications to fire over the coming 64 days
        for nextLocalNotification in 1...64 {

            // Add calendar day
            let addDayComponent = NSDateComponents()
            addDayComponent.day = nextLocalNotification
            let calendar = NSCalendar.currentCalendar()
            let nextDate = calendar.dateByAddingComponents(addDayComponent, toDate: NSDate(), options: [])

            // Set day components for next fire date
            let nextLocalNotificationDate = NSCalendar.currentCalendar()
            let components = nextLocalNotificationDate.components([.Year, .Month, .Day], fromDate: nextDate!)
            let year = components.year
            let month = components.month
            let day = components.day

            // Set notification fire date
            let componentsFireDate = NSDateComponents()
            componentsFireDate.year = year
            componentsFireDate.month = month
            componentsFireDate.day = day
            componentsFireDate.hour = 0
            componentsFireDate.minute = 0
            componentsFireDate.second = 5
            let fireDateLocalNotification = calendar.dateFromComponents(componentsFireDate)!

            // Schedule local notification
            let localNotification = UILocalNotification()
            localNotification.fireDate = fireDateLocalNotification
            localNotification.alertBody = ""
            localNotification.alertAction = ""
            localNotification.timeZone = NSTimeZone.defaultTimeZone()
            localNotification.repeatInterval = NSCalendarUnit(rawValue: 0)
            localNotification.applicationIconBadgeNumber = nextLocalNotification

            application.scheduleLocalNotification(localNotification)
        }
    }
}

Solution

  • You can dispatch the function, asynchronously, onto another queue. This will ensure that the main queue isn't blocked performing the scheduling and will prevent the UI from becoming unresponsive:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIO‌​RITY_BACKGROUND, 0)) {
            scheduleFutureLocalNotifications()
        } 
    }