jperl
jperl

Reputation: 1106

Scheduled NSNotification (Instead of UILocalNotification)... Swift solutions?

My app (prefix "AAS") is basically a game where users lose points every day they don't play. I use UILocalNotifications to alert the user that they've lost points, and invite them back to play. One of my view controllers displays when the points have changed, and it's pretty simple to send out an NSNotification when a UILocalNotification is fired while the app is open).

func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
    if notification.userInfo != nil {
        if let notificationName = notification.userInfo![AASNotification.ActionKey] as? String {
            NSNotificationCenter.defaultCenter().postNotificationName(notificationName, object: nil, userInfo: nil)
        }
    }
}

When the app is reopened after being inactive, one of the classes calculates how many points are lost. Great. Bulletproof, except when the user disallows my app to use NotificationCenter, the app will not be updated if it's open when the notification is supposed to fire. For this case, I wrote my own implementation of a timed notification queue that would mimic UILocalNotification to a certain extent while my app is open. But I thought, someone must have had this problem before, and maybe there is a cocoapod for it.

So my question to the community is, does someone know of a library that dispatches timed NSNotifications? Or a different approach to this problem? Here's my solution, which is barebones and works for the purpose I need:

https://github.com/JamesPerlman/JPScheduledNotificationCenter

I'd love to use one that was coded by a professional and is well tested and feature rich. (I was made aware that this request is off topic for SO.)

Edits: I want to be able to queue up any amount of NSNotifications to be fired at arbitrary dates. Obviously the NSNotifications can only be received by my app while it is open, that's fine. I do not know the expense of using one NSTimer for each NSNotification (could be hundreds of NSTimers all on the run loop), so my solution only uses one NSTimer at a time. I want the ability to schedule and cancel NSNotifications just like you can do with UINotifications.

Upvotes: 0

Views: 577

Answers (1)

ConfusedByCode
ConfusedByCode

Reputation: 1730

You could try NSTimer (NSTimer class reference). In your AppDelegate you can create a method similar to your didReceiveLocalNotification method to execute when the timer is triggered. Also, create an NSUserDefault to store the next time you need to trigger the timer. Finally, at the point where you want to begin the countdown, get the time interval from the current time until the time you want to trigger the event, and set the timer.

So in your AppDelegate, register the default and implement the notifyPlayer:

class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
    {
         let userDefaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
         userDefaults.registerDefaults(["alertTime": NSDate()])  //initial value

         return true
    }

    func notifyPlayer() {
        // Calculate points and notify relevant viewcontroller to alert player.
        let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
        let lastNotificationTime = defaults.objectForKey("alertTime") as! NSDate
        let nextNotificationTime = lastNotificationTime.dateByAddingTimeInterval(86400)
        defaults.setObject(nextNotificationTime, forKey: "alertTime")
    }
}

Now set the timer wherever it makes sense, probably in your app's initial view controller.

class InitialVewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
        let savedTime = defaults.objectForKey("alertTime") as! NSDate

        let countDownTime = savedTime.timeIntervalSinceNow
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate

        NSTimer.scheduledTimerWithTimeInterval(countDownTime,
            target: appDelegate,
            selector: #selector(AppDelegate.notifyPlayer()),
            userInfo: nil,
            repeats: false)
    }
}

It's not perfect, as I haven't tested it, but I think the concept will work for you.

Edit: Just to clarify, this would solve your problem of alerting the user while he is using the app, but won't do anything when the app is not in use. I don't know of any way to send users notification center notifications when permission hasn't been granted.

Upvotes: 2

Related Questions