Tag Archives: Notifications

Adding Actions to iOS and WatchOS Local Notifications

Even if you never want to make an app for the Apple Watch, there’s one place you might want to think about supporting: Notifications. In a previous lesson, we explored how to make local notifications for iOS devices. That lesson ended with a free bonus: If a phone is sleeping and you have an Apple Watch, the notification will go to the watch.

Starting in iOS8, notifications get one big change: they can have actions of their own. On phone, tablet or watch this gives you more flexibility in how to respond to a notification. Suppose you had an app that after an appointment comes up, nags you about it every ten seconds until you stop the nagging. You could of course go into the app and shut it off, but it might be easier to have a stop nagging button on the notification. In this lesson, we’ll make that stop nagging button first for iOS devices, then for the Apple Watch. Along the way we’ll discover why watch notifications are the one place iOS developers should seriously think about for their phone apps.

Making a Demo Program – A Review of Notifications

In a previous lesson we went into detail about local notifications. We’ll be using a lot of what was learned there. In this tutorial, we’ll build a simple app to make a series of notifications similar to the HIIT app at the end of that lesson. We’ll use this as a review and quick introduction to local notifications if you are not familiar with them.

Since we will eventually get to some watch related notifications, make a new project using the iOS with Watch OS App template.

2016-04-26_15-32-23

Name the project PhoneWatchNotificationDemo and make sure watch notifications is checked on and everything else is off.

2016-04-26_15-32-22

Once you save the project, add a new file by pressing Command-N called NaggyNotification subclassing NSObject. Add these properties to the class

class NaggyNotification: NSObject {
    private let basicNags = ["Nag", "Nag nag","Naggity nag"]
    private let emojiNags = ["😀","😉","🙄","😒","😠","😡"]
    private var nags = ["Nag", "Nag nag","Naggity nag"]
    private var counter = 0
    private var timer = NSTimer()
    private var backgroundTask = 0
    private var localNotification = UILocalNotification()
    var nagging:Bool {return timer.valid } //read only
}

If you don’t know how to type emojis, press Control-Command-Spacebar to get the symbols palette.

We are going to do something insane a little later with this class. To protect against some of the problems with that, all but one of our properties is private and inaccessible outside the class. There is one read-only property nagging which uses a computed read-only value.

Most of these properties we’ll get to. We’ll start with the most important. The localNotification will contain our notification. Add the following method to present it immediately:

    func presentNotificationNow(){
        UIApplication.sharedApplication().presentLocalNotificationNow(localNotification)
    }

There are two ways to present notifications. We can present immediately as we do with the presentLocalNotificationNow method above or at some scheduled time in the future with the scheduleLocalNotification method. We could schedule that time using the fireDate property of UILocalNotification, but that presents a problem. We can only schedule 64 notifications, which in a nagging app might be too little space. We will take a different approach: Use a NSTimer loop in the background. Add the following method to start a timer:

func startTimer(timeInterval:NSTimeInterval){
        if !timer.valid{ //prevent more than one timer on the thread
            counter = 0
            backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler(nil)
            timer = NSTimer.scheduledTimerWithTimeInterval(
                timeInterval,
                target: self,
                selector: #selector(timerDidFire),
                userInfo: nil,
                repeats: true)
        }
        
    }

We do three things in this code. We reset a counter for counting how many nags the user has been subjected to, then set this task into the background. While on a simulator you can leave things in the background without this, your app will suspend operation without beginBackgroundTaskWithExpirationHandler on a live device. Finally we start a timing loop, getting the timing interval from the parameter timeInterval. The timing loop ends on a selector timerDidFire. Let’s add the code for timerDidfire. Add this to the class:

    func timerDidFire(timer:NSTimer){
            counter += 1
            let currentNag = counter % nags.count //cycle through messages
            let nagString = String(format:"# %i %@",counter,nags[currentNag])
            localNotification.alertBody = nagString
            localNotification.alertTitle = "Naggy Notification"
            localNotification.category = "Nags"
            localNotification.soundName = UILocalNotificationDefaultSoundName
            localNotification.userInfo = ["NagNum":counter,"NagString":nags[currentNag]]
            presentNotificationNow()
    }

If you are not familiar with timing loops you might want to refer to this lesson. Basically, the timer object is set to run the timerDidFire function every timeInterval seconds. Once in the timerDidFire function, we increment counter and compose a string we’ll post to the notification. The property alertBody of UILocalNotification sets the text for the notification. We also set the sound to the default sound, which will also set off vibrations and haptics. For later use, we all send a dictionary containing our count and our current nag as userInfo we can use later in this lesson. Finally we present the notification.

We’ll also need a way to shut down the timer. Add this method to your code:

func stopTimer(){
        timer.invalidate()
        UIApplication.sharedApplication().endBackgroundTask(backgroundTask)
}

We shut down the timer and remove the task from the background.

Now for the small, but insane bit of code. Add this above the class declaration:

let naggingNotification = NaggyNotification()

class NaggyNotification: NSObject {
...

A variable declared outside a class is global. Global values or instances can be used anywhere in the target code. There are other, better ways of handling the NaggyNotification class, but I could write full tutorials on them. As we’ll see, this instance will show up in several classes. This is a simple way to get it into all of them. As I always do when I use a global, I warn people not to use globals except in special circumstances. They are notoriously hard to track down in code. It’s why I made all the properties private, only methods are accessible to other classes, which are easier to deal with.

In order to use a notification, you must tell the application you will be using the notification in the AppDelegate.swift file. Go to the AppDelegate class Change the application:didFinishLaunchingWithOptions:launchOptions: method to this:

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        application.registerUserNotificationSettings(
            UIUserNotificationSettings(
                forTypes:[.Alert, .Sound],
                categories: nil
            )
        )
        return true
    }

We register the notification settings for the two types of notifications: Alerts(or banners) and sounds(or haptics and vibrations).

Our app has all the internal parts set, we need a user interface. We’ll make a very simple one of a single button. Go to the ViewController.swift file. Change ViewController to this:

class ViewController: UIViewController {  
    @IBOutlet weak var nagButton: UIButton!
    @IBAction func nagButton(sender: UIButton) {
    }
 }

We’ll have one button that we’ll assign a outlet and action to. Change the nagButton action to this:

@IBAction func nagButton(sender: UIButton) {
    if naggingNotification.nagging{
       naggingNotification.stopTimer()
    }else{
       naggingNotification.startTimer(10.0)
    }
      stateDidChange()
}

This code uses the naggingNotification read-only property nagging to determine if the timer is on. We toggle the timer depending on the result, using the naggingNotification methods stopTimer and startTimer. We’ve set up this demo with a 10 second timer.

Our last statement calls a method we have yet to define stateDidChange(). We’ll update the button title with this. Add this code to ViewController:

func stateDidChange(){
    if !naggingNotification.nagging{
       nagButton.setTitle"Nag", forState: .Normal)
    }else{
       nagButton.setTitle("Stop Nagging", forState: .Normal)
    }
}

We have our code. Go to the storyboard. Drag one button to the center of the storyboard. Title it Nag, and set the font to 46 point Bradley Hand.

2016-05-01_07-49-18

In the lower right corner of the storyboard, you will find the auto layout controls. With the Nag button selected, click the align alignment icon icon to get the align menu. Towards the bottom of this menu you will find the center Horizontally in Container and Vertically in container settings. Click both on with values of zero. Set the Update frames to Items of New constraints.

2016-04-26_15-32-25

Open the assistant editor. Drag from both circles in the code to the button to connect the action and the outlet to the button.  Close the assistant editor.

Before we test this, launch both the iPhone and watch simulators. If you have never launched these before running an app, check the directions here Especially with the watch, it helps to have them running before you run an app on them.

Build and run using a iPhone 6s plus simulator. You get the notification message the first time you run this:

2016-04-29_10-09-50

Press OK to allow notifications.You get a simple screen of a single button:

2016-05-01_07-54-37

Tap the Nag button.

2016-05-01_07-54-44

The button toggles but doesn’t do more than that. we need to be outside the app to see a notification.

Press Command-Shift-H or on the hardware simulator menu, Hardware>Home. Wait a few seconds and you will see this:

2016-04-29_10-09-53

Swipe down from the status bar once the banner disappears. Select Notifications and you will see the notifications appear.

2016-04-29_10-09-54

Tap one of the notifications and you are back in the app. Tap Stop Nagging and the notifications turn off.

Adding Categories and Actions to your Notifications

Introduced in iOS8, Notifications  now have their own actions, which appear as buttons on a notification. Often you’ll find these in the AppDelegate.  I’ll however keep everything on one class for the notification. Actions are of class UIUserNotificationAction and it’s subclass UIMutableUserNotificationAction. Since you can’t change anything in a UIUserNotificationAction, we define actions in UIMutableUserNotificationActions. We’ll define two actions, one to stop the timer and one to change the nag message to emoji. In the NaggyNotification class add the following method:

func makeActions()-> [UIMutableUserNotificationAction]{
    let stopAction = UIMutableUserNotificationAction()
    stopAction.title = "Stop"
    stopAction.identifier = "StopAction"
    stopAction.destructive = true
    stopAction.authenticationRequired = true
    stopAction.activationMode = .Foreground
     
    let moreAction = UIMutableUserNotificationAction()
    moreAction.title = "Emoji"
    moreAction.identifier = "EmojiAction"
    moreAction.destructive = false
    moreAction.authenticationRequired = false
    moreAction.activationMode = .Background
        
    return [stopAction,moreAction]
    }

The title parameter is the title that will appears on buttons of the notification. The identifier is the string used to internally identify the action. You’ll notice that there is no code here to execute the action. We’ll use that identifier to code the action in AppDelegate. We can add a red warning to a button if it is a destructive button, one that deletes something. I used it here as the Stop button, but left destructive as false for the Emoji button. The activationMode has two choices: .Foreground or .Background and indicates if the app will execute the action in the foreground or the background. To maintain security of the user’s phone we can also require an authentication before we execute the action. For actions in the foreground, this is mandatory and activationMode is always true. The statement in our code is redundant.

We return an array of the actions. We will use these actions in a category. Categories contain all information about a specific type of notification. Our category, which we’ll call Nags contains the actions that will show in the nag notification. Add the following code to the NaggyNotification class.

 //MARK: - Categories and Actions
    func makeCategory()-> UIMutableUserNotificationCategory {
        let category = UIMutableUserNotificationCategory()
        category.identifier = "Nags"
        category.setActions(makeActions(), forContext: .Default)
        return category
    }

We make set two properties on a UIMutableUserNotificationCategory. We set an identifier Nags and using the setActions:forContext: method add the actions to the category.

We’ve set up our category and actions. Now to use them. We’ll do that in AppDelegate. Find the method we changed earlier. Change the highlighted lines to add the category to our current code.

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) ->; Bool {
        // Override point for customization after application launch.
        let category = naggingNotification.makeCategory()
        application.registerUserNotificationSettings(
            UIUserNotificationSettings(
                forTypes:[.Alert, .Sound, .Badge],
                categories: [category]
            )
        )
        return true
    }

If we ran our code now, we would have our actions listed in the notification, but nothing would happen. We need to add a callback method to AppDelegate containing the code to execute the actions. Add this to AppDelegate

    func application(application: UIApplication, handleActionWithIdentifier identifier: String?, forLocalNotification notification: UILocalNotification, completionHandler: () -> Void) {
        
        if notification.category == "Nags"{
            if identifier == "StopAction"{
                naggingNotification.stopTimer()
            }
            if identifier == "EmojiAction"{
                naggingNotification.toggleAlertMessages()
            }
        }
        completionHandler()
    }
  

The application:handleActionWithIdentifier:forlocalNotification method has a actionIdentifier and a UILocalNotification. for parameters.   The code checks the notification’s title. If it is our notification, we check the action identifier and perform the action we need.

We need one more method back in NaggyNotification to toggle the alert messages. Add this to the code.

func toggleAlertMessages(){
        if nags == basicNags {
            nags = emojiNags
        } else {
            nags = basicNags
        }
    }    

Fixing a Bug Before It Happens

This is all the code we need to run our application. However, there’s one bug that we’ll fix first. The problem is the Stop action when we are in the background. It will stop the the timer and shut down the nags. The button on our view controller does not know this. With the code as it is written the button still says Stop Nagging when we enter the foreground. We need to update the button any time we do enter the foreground. Find the applicationDidBecomeActive in AppDelegate. This method runs when we need to do that refresh of our user interface. Change it to this

    func applicationDidBecomeActive(application: UIApplication) {
        let rvc = window?.rootViewController as! ViewController
        rvc.stateDidChange() //Update the button
    }

Line 2 of the code gets the view controller. We have the view controller as our root view controller, so it’s easy to access. Once we have a reference to the root controller, we can call its method. statDidChange which checks the status of the timer and changes the title to the correct one.

Build and run.

2016-05-01_07-54-37

Press the nag button. Press Command-Shift-H. Eventually the notification appears:

2016-05-01_07-09-56

Drag the small oval at the bottom of the notification down.

2016-05-01_07-37-48

The actions appear.

2016-05-01_07-11-10

Tap the emoji button.  The next notification you get is an emoji.

2016-05-01_07-11-41

Drag down from the status bar and get the notifications. Swipe right to left on one of the notifications.

2016-05-01_07-15-52

Tap the Stop button. The app stops and opens up to the Nag screen again.

2016-05-01_07-54-37

The Apple Watch for All Developers

While that’s what you need to know for iOS, it’s not the whole story. Unless notifications are turned off by the user of an Apple watch, the watch will receive the notification if the phone is asleep.  Sadly, the watch simulator does not always reflect what happens on the watch.  You can try using the simulator if you  don’t have a watch handy, I’ll be using both since they have slightly different behaviors.

If your app is still running, get the watch and phone  simulator visible

2016-05-01_16-39-08

Start nagging with the phone, then press Command-L to Lock the phone. The simulator goes black. Wait a few seconds,  And you will see the Short-Look Notification appear:

Photo May 01, 9 26 23 AM

A second later, the  Long-Look notification appears.

2016-05-01_16-39-07

You’ll notice the buttons for the actions don’t quite fit on the watch. scroll up and you’ll find one more button.

2016-05-01_16-45-22

Test the emoji button.  Tap it, and the next notification changes to this:

2016-05-01_16-51-29

Press the stop button and you get a blank screen with the time. Actually, you are running a blank watch app.

2016-05-01_16-53-02

 You get slightly different  results with a real phone. Running the app on my phone, I start nagging by pressing the Nag button. I then lock my phone  by pressing the switch. SInce I turned on sounds and used the system sound in my actions, I’ll get a Haptic vibration on my wrist,  and then the notification.

Photo May 01, 9 25 40 AM

The notification includes the Emoji button and a dismiss button, which is cut off.  You can scroll up to get the rest of the dismiss button. On the simulator, we had the stop button, but on the watch it’s missing.

The difference has to do with activationMode. If you have an action with an activationMode of .Foreground the watch does not display it automatically. The foreground mode on the watch starts the watch app associated with this notification. In the simulator it lets us run that blank app, but on a physical watch it knows there is no app and doesn’t show the button.  On the other hand, an activationMode of .Background on the watch sends to the background of the phone. That’s why with no coding, the Emoji button works perfectly. Go to the NaggyNotification class. In makeActions, change our code to put the Stop button in the background

stopAction.activationMode = .Background

Now run the app again and when we lock the phone we get both buttons on the watch:

Photo May 01, 10 05 16 AM

With more buttons, the dismiss button is missing. Drag up and you will find it:

Photo May 01, 10 10 27 AM

Tap the Emoji Button. The next notification changes to emoji

Photo May 01, 10 05 31 AM

Tap the Stop button. On both the live watch and the simulator, the timer stops.  All of our actions work on the watch without a single new line of code.

Notifications will happen on an Apple Watch even if you don’t program for it. For Apple watch developer, this provides one way to launch your watch app. For iOS developers using notifications and actions, it means you have one more thing to think about when writing an app.  Think out what you want to show and not to show  for a watch user. If you are not writing a watch app, you can exclude actions from the watch but not the phone by making the  activationMode go to the foreground.

The whole code

NaggyNotification.swift

//
//  NaggyNotification.swift
//  PhoneWatchNotificationDemo
//
//  Created by Steven Lipton on 5/1/16.
//  Copyright © 2016 MakeAppPie.Com. All rights reserved.
//

import UIKit

//Global variable -- not always a good idea,
// but makes running this code much simpler
//
let naggingNotification = NaggyNotification()

class NaggyNotification: NSObject {
    
    //All properties are private or read only 
    //Good insurance for using the global instance
    //nothing gets messed up. 
    
    private let basicNags = ["Nag", "Nag nag","Naggity nag"]
    private let emojiNags = ["😀","😉","🙄","😒","😠","😡"]
    private var nags = ["Nag", "Nag nag","Naggity nag"]
    private var counter = 0
    private var timer = NSTimer()
    private var backgroundTask = 0
    private var localNotification = UILocalNotification()
    var nagging:Bool {return timer.valid } //read only
    
    func presentNotificationNow(){
        UIApplication.sharedApplication().presentLocalNotificationNow(localNotification)
    }
    func toggleAlertMessages(){
        if nags == basicNags {
            nags = emojiNags
        } else {
            nags = basicNags
        }
    }
    
    func startTimer(timeInterval:NSTimeInterval){
        if !timer.valid{ //prevent more than one timer on the thread
            counter = 0
            backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler(nil)
            timer = NSTimer.scheduledTimerWithTimeInterval(
                timeInterval,
                target: self,
                selector: #selector(timerDidFire),
                userInfo: nil,
                repeats: true)
        }
        
    }
    
    func timerDidFire(timer:NSTimer){
        counter += 1
        let currentNag = counter % nags.count //cycle through messages
        let nagString = String(format:"# %i %@",counter,nags[currentNag])
        localNotification.alertBody = nagString
        localNotification.alertTitle = "Naggy Notification"
        localNotification.category = "Nags"
        //localNotification.alertTitle = localNotification.category
        localNotification.soundName = UILocalNotificationDefaultSoundName
        localNotification.userInfo = ["NagNum":counter,"NagString":nags[currentNag]]
        presentNotificationNow()
        
    }
    
    func stopTimer(){
        timer.invalidate()
        UIApplication.sharedApplication().endBackgroundTask(backgroundTask)
    }
    
    //MARK: - Actions and Categories
    func makeActions()-> [UIMutableUserNotificationAction]{
        let stopAction = UIMutableUserNotificationAction()
        stopAction.title = "Stop"
        stopAction.identifier = "StopAction"
        stopAction.destructive = true
        stopAction.authenticationRequired = true
        stopAction.activationMode = .Background
        
        let moreAction = UIMutableUserNotificationAction()
        moreAction.title = "Emoji"
        moreAction.identifier = "EmojiAction"
        moreAction.destructive = false
        moreAction.authenticationRequired = false
        moreAction.activationMode = .Background
        
        return [stopAction,moreAction]
    }
    
    func makeCategory()-> UIMutableUserNotificationCategory {
        let category = UIMutableUserNotificationCategory()
        category.identifier = "Nags"
        category.setActions(makeActions(), forContext: .Default)
        return category
    }
}

AppDelegate.swift

//
//  AppDelegate.swift
//  PhoneWatchNotificationDemo
//
//  Created by Steven Lipton on 5/1/16.
//  Copyright © 2016 MakeAppPie.Com. All rights reserved.
//

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        let category = naggingNotification.makeCategory()
        application.registerUserNotificationSettings(
            UIUserNotificationSettings(
                forTypes:[.Alert, .Sound],
                categories: [category]
            )
        )
        return true
    }
    
    func application(application: UIApplication, handleActionWithIdentifier identifier: String?, forLocalNotification notification: UILocalNotification, completionHandler: () -> Void) {
        
        if notification.category == "Nags"{
            if identifier == "StopAction"{
                naggingNotification.stopTimer()
            }
            if identifier == "EmojiAction"{
                naggingNotification.toggleAlertMessages()
            }
        }
        completionHandler()
    }
    

      func applicationDidBecomeActive(application: UIApplication) {
        let rvc = window?.rootViewController as! ViewController
        rvc.stateDidChange() //Update the button
    }

   
}



ViewController.swift

//
//  ViewController.swift
//  PhoneWatchNotificationDemo
//
//  Created by Steven Lipton on 5/1/16.
//  Copyright © 2016 MakeAppPie.Com. All rights reserved.
//

import UIKit


class ViewController: UIViewController {
    @IBOutlet weak var nagButton: UIButton!
    @IBAction func nagButton(sender: UIButton) {
        if naggingNotification.nagging{
            naggingNotification.stopTimer()
        }else{
            naggingNotification.startTimer(10.0)
        }
        stateDidChange()
    }
    func stateDidChange(){
        if !naggingNotification.nagging{
            nagButton.setTitle("Nag", forState: .Normal)
        }else{
            nagButton.setTitle("Stop Nagging", forState: .Normal)
        }
    }
}

Adding iOS Local Notifications in Swift

The word notification gets a bit abused in the world of mobile development, especially in the world of Apple development. Notifications could mean internal notifications, where classes use observers to watch unrelated code. When a notification appears, the observer code executes special code for that event.

Another type of notification notifies a user that something has happened, even if they are not in the app the sent the notification – or the device  is sleeping. That kind of user notification has two varieties: push notifications and local notifications. To  the user, these two are the same. For the developer, the push notification uses an external server to push data from the outside world. The Messages app, Facebook and Twitter all use push messages. Due to using an external server, things get complicated with push notifications. There is a registration process with Apple, extra libraries to use them, and you have to write or import code to handle the server data.

The much simpler local notification needs none of that. With a few lines of code, you can fire off a notification. Often used on scheduling and timer applications, this can adapt for many other uses where external data isn’t necessary. In this lesson, we’ll look at the local notification. We’ll make a HIIT timer app to show aspects of the local notification.

Set Up a New Project.

Start a new single view project called SwiftLocalNotificationDemo with Swift as the language and a Universal device.  Save the file, and then go to the story board. We’ll eventually use a navigation controller for this app. Select the existing view controller and from the menu select Editor>Embed in>Navigation Controller.  You’ll have a navigation controller and a view controller on the storyboard.

2016-04-20_06-19-15

On the storyboard add three buttons,  three labels  and a segmented control.  Arrange them roughly like this.

2016-04-20_06-28-04

Set the titles and text like this for the controls.

2016-04-20_06-36-53

I added color an fancied it up. You don’t have to do this step, but it makes it a bit nicer to look at. I used a palette of #220066 for the dark  and #FFDDFF for the light. I also changed up and down for symbols.  Use the keyboard shortcut  Control-Command-Space to get the symbol popup.

2016-04-20_07-02-41

Using Stack Views

We’ll use some embedded stack views to align everything quickly.  I’m not going to explain stack views here, but for those who have never used them, I’ll go step by step. If you are interested you can read this about them or go buy my book on auto layout. Select the Seconds and 00 labels  On the bottom right of the story board, You’ll find the auto layout controls.

stackButton

Click the stack view iconstack view button. It immediately makes a stack view of the two labels.  Select the two arrows and the seconds stack view.

2016-04-20_07-09-35

Click the stack view button stack view buttonagain. The three combine into one stack view.

2016-04-20_07-12-50

Select everything on the storyboard. Hit the stack view button stack view buttonagain. Now everything is one stack view.

2016-04-20_07-14-34

Find the pin buttonpinMenuButton in the auto layout controls. In the popup that appears, pin all four sides 10 points in the popup. Also set the Update Frames with Items of New Constraints.

2016-04-20_07-18-14

Add the 4 constraints. Depending on where your controls were, th storyboard now looks something like this.

2016-04-20_07-18-47

You’ll notice in the attributes inspector the stack view attributes. Change them to An axis of Vertical, Alignment of Fill and Distribution of Fill Equally:

2016-04-20_07-23-31

Open the document outline. Open up all the arrows so you can see the hierarchy of the stack views. It’s a lot easier to click on the document outline than the storyboard to select stack views.  Select the second stack view down.

2016-04-20_07-27-18

In the attributes inspector, change the stack view to this:

2016-04-20_07-29-32

Select the last stack view.

2016-04-20_07-34-39

Change the attributes to this:

2016-04-20_07-23-31

You now have a storyboard  looking like this:

2016-04-20_07-36-23

Select the Seconds and 00 labels. Center align them.

2016-04-20_07-40-52

We now have a fully aligned layout.

2016-04-20_07-41-18

Setting Up the Code

Go to the ViewController.swift code.  Add the following outlets and actions.

//MARK: - Outlets
@IBOutlet weak var statusLabel: UILabel!
@IBOutlet weak var workoutType: UISegmentedControl!
@IBOutlet weak var secondsIndicator: UILabel!
//MARK: - Actions
@IBAction func upArrow(sender: UIButton) {
}
@IBAction func downArrow(sender: UIButton) {
}
@IBAction func setInterval(sender: UIButton) {
}

Go back to the storyboard and open the assistant editor. Drag from the circle next to statusLabel to The Quick HIIT Timer Label. Drag from the circle to the left of secondsIndicator to the 00 label. In the same way Connect workoutType to the segmented control, upArrow to the up arrow(Up) button, downArrow to the down arrow(Down) button, and setInterval to the Set Interval button. Close the assistant editor and go to the Viewcontroller.swift code. Above the outlets, add a property:

 //MARK: Properties
    var seconds:NSTimeInterval = 0.0

We’ll use the up and down buttons to change a counter seconds. Change the actions upArrow and downArrow like this:

@IBAction func upArrow(sender: UIButton) {
     seconds += 1.0
     secondsIndicator.text = displaySeconds()      
}
@IBAction func downArrow(sender: UIButton) {
     seconds -= 1
     if seconds < 0{
          seconds = 0.0
     }
     secondsIndicator.text = displaySeconds()
}

In these actions, our last statement sends the current seconds to the label. It converts the time interval to a string in a function displaySeconds. Add displaySeconds to your code:

//MARK: Instance Methods
func displaySeconds() -> String{
    let mySeconds = String(format:"%02i",Int(seconds))
    return mySeconds
}

We now have a working counter in our app. Add the following code to setInterval.

@IBAction func setInterval(sender: UIButton) {
    //make a status string
    let index = workoutType.selectedSegmentIndex
    let workout = workoutType.titleForSegmentAtIndex(index)!
    let status = displaySeconds() + " seconds " + workout
    statusLabel.text = status + " set"
}

Nothing visible happens when we schedule a notification. We now have some feedback of what notification we just set.

Setting a Notification

We’re ready to set a local notification. Add some more code to the setInterval action

@IBAction func setInterval(sender: UIButton) {
    //make a status string
    let index = workoutType.selectedSegmentIndex
    let workout = workoutType.titleForSegmentAtIndex(index)!
    let status = displaySeconds() + " seconds " + workout
    statusLabel.text = status + " set"
        
    //make the local notification
    let localNotification = UILocalNotification()
    localNotification.fireDate = NSDate(timeIntervalSinceNow:seconds)
    localNotification.alertBody = status + " complete"
    localNotification.timeZone = NSTimeZone.defaultTimeZone()
    //set the notification
    UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
}

Line 9 of this code makes an instance localNotification of the UILocalNotification class. Local notifications can be immediate or time based. We set the time we will launch the notification, its fireDate, with an NSDate object. If this was a scheduling app I’d set the scheduled time for the notification to fire. In line 10 I want a time seconds in the future from now. The alertBody property of line 11 sets the message that will appear on the notification. We show we completed that interval. While not mandatory, since we are using a time, we set the time zone the notification will use for its clock, usually the default time zone for the system.

Our last line of code in line 14 is the first of two tricky things about notifications. The notification isn’t running in our class. Instead, it’s part of a bigger structure within the device’s OS. If the app isn’t running in the foreground, we get our notification. The object UIApplication.sharedApplication() is the application object we need to schedule this with. We schedule that notification with scheduleLocalNotification.

The second tricky part is we tell the UIApplication object we are using notifications and how we will be using them. We do that in its delegate, the often ignored AppDelegate. Go to the AppDelegate.swift file in Xcode. Toward the top, you’ll find the application:didFinishLaunchingWithOptions:LaunchOptions: method. Add the highlighted code:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Override point for customization after application launch.
    application.registerUserNotificationSettings(
        UIUserNotificationSettings(
            forTypes:[.Alert, .Sound, .Badge],
            categories: nil
        )
    )
    return true
}

We register our notification with a UIUserNotificationSettings object. We have two parameters to set in the UIUserNotificationSettings object: the type of notification and the actions we might take. These actions are known as categories. We’ll discuss categories in our next lesson, so we set categories to nil and focus on types. There are four types available to us:

  • .Alert sends an alert or banner
  • .Sound plays a sound
  • .Badge shows the small red dot with a counter on the app icon
  • .None which does not present a notification.

We set up all three notifications. We are ready to build and run. As soon as the launch screen disappears from the app we see the alert:

2016-04-21_06-53-35

Notifications are one feature that the user chooses. Tap OK. Tap the up arrow until the counter reads 15 seconds.

2016-04-21_06-58-37

Tap the Set Interval button. The status tells us we added an interval.

2016-04-21_07-27-47

Press Command-Shift-H to go to the home screen. In a few seconds, the interval will fire and you’ll see the notification.

2016-04-21_07-03-21

Swipe down to get the notification center, and you’ll see your notification there.

2016-04-21_07-03-42

User Settings and Notifications

Go to the Settings app in the simulator. Scroll down towards the bottom and you’ll find our app.

2016-04-21_07-10-45

Select the SwiftNotificationDemo entry in Settings and we find one entry  for Notifications.

2016-04-21_07-16-44

Click that and we get the standard notifications settings page. Currently it is at its default settings.

2016-04-21_07-16-45

Toward the bottom you’ll  find the Alert Style When Unlocked setting. Change it from Banners to Alerts.

2016-04-21_07-11-34

Now go back to the app. Let’s do this and demonstrate one more feature of notifications. Swipe down from the top of the screen to get the notification center.

2016-04-21_07-03-42

Tap the notification. You are back in the app. Notifications when tapped launch the app or bring the app to the foreground.

2016-04-21_07-27-47

Tap the Set Interval again, then press Command-Shift-H to go back to the home screen. Wait, and you get an alert style notification.

2016-04-21_07-32-02

Unlike the banner type, which will dismiss itself, the alert waits for a response.  You can close the alert or open the app. Close the notification.

Are Notifications Allowed?

There is one user setting that all developers must be aware of. In the simulator go back to our apps notification settings. At the top you will find the Allow Notifications button.

2016-04-21_07-35-34

Switch it off. All the other settings disappear.

2016-04-21_07-35-51

Shut down the app in Xcode.  Build and run.  Set a 10 second Notification.  The system will let you do this but you will be waiting a long time for that notification –  it doesn’t exist.  The system shuts down our request for a notification.

In an app where notifications have major functionality, such as this timer app, we may want to tell the user that this is a bad idea.

We’d first have to know ourselves that notifications are off. We told the application which ones to turn on in the AppDelegate. There is a property that lets us look at those settings, currentUserNotificationSettings. It has a property type, which has a value of UIUserNotification type. This type uses a bitmask. If you’ve never used a bitmask before, it’s a way of compressing several Bool values into a single number. The three types we have each have one digit of a binary number. We thus can make an unsigned integer value from that value according to this table

Value .Badge (4) .Sound(2) .Alert (1)
0
(.None)
0 0 0
1 0 0 1
2 0 1 0
3 0 1 1
4 1 0 0
5 1 0 1
6 1 1 0
7 1 1 1

Notice the zero row. Remember we had four, not three options for UIUserNotification. The fourth option is .None. That is when all three of the others are off. When a user has notifications off in the user settings, the system always preempts our currentUserNotificationSettings.type setting it to .None. For an easy comparison, it often best to change this to a rawValue which gives us direct access to that integer value. Add this line under the one you just added:

let noNotifications = UIUserNotificationType.None.rawValue

This is just assigning a constant to the value 0, but for good documentation we write the longer way.
All this gets us to a simple if. If the user notifications is off, the type will be zero, otherwise it is on. Change the setInterval action to this:

@IBAction func setInterval(sender: UIButton) {
     //make a status string
     let index = workoutType.selectedSegmentIndex
     let workout = workoutType.titleForSegmentAtIndex(index)!
     let status = displaySeconds() + " seconds " + workout
     statusLabel.text = status + " set"
        
     let notificationTypes = UIApplication.sharedApplication().currentUserNotificationSettings()?.types
     let noNotifications = UIUserNotificationType.None.rawValue
     if notificationTypes!.rawValue == noNotifications {
         statusLabel.text = "Turn on notifications in settings"
     } else{
        //make the local notification
        let localNotification = UILocalNotification()
        localNotification.fireDate = NSDate(timeIntervalSinceNow:seconds)
        localNotification.alertBody = status + " complete"
        localNotification.timeZone = NSTimeZone.defaultTimeZone()
        
        //set the notification
        UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
     }

We check if notifications are turned off. If off we send a message to the status label to tell the user the app doesn’t work. In a more sophisticated app I’d use an alert, explaining that the app doesn’t work without notifications, and include an action in the alert to take the user to the settings app.
Go back to the simulator, and make sure notifications are turned off.

2016-04-21_07-35-51

Build and run.  Make a notification, and you get a message

2016-04-22_06-14-04

While it give us the right message, It runs off the screen. Exit the app, and go to the storyboard. Select the status label.  In the attributes, change Lines to 0, Line Breaks to Word Wrap.

2016-04-22_06-14-06

This sets the number of lines in the label to automatic.

Change the message to be a bit more verbose:

statusLabel.text = "Please turn on notifications in Settings"

Build and run. When we try to set a notification, we get this:

2016-04-22_06-34-15

Enable notifications in the settings app. Go back to the app and it works.

2016-04-22_06-36-00

Listing Notifications

We may want to see all notifications currently waiting to fire.  There is an array scheduledLocalNotifications that lets us see this. We’ll set up a quick table view to display these. If you are not familiar with table views you might want to read this. Shut down the app in Xcode, and go to the storyboard.  Add a UIBarButtonItem to the right side of the navigation bar. Title it  List. Drag out a table view controller to the storyboard. Control drag from the List bar button to the view controller to get a segue. Make it a Show segue.

2016-04-22_06-48-20

In the document outline, Select the Table View Cell

2016-04-22_06-31-42

In the attributes inspector set the Identifier to cell and the Style to Basic.

2016-04-22_06-31-55

We need to make the controller. I usually avoid the table view template. Press Command-N to make a new file.  Make a new Cocoa Touch Class file subclassing UIViewController.  Name the file NotificationsTableViewController. When the file appears, change the view controller to this:

class NotificationsTableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }
}

Go to the storyboard and select the table view controller. In the identity inspector, Change the class to NotificationsTableViewController.

Go to the NotificationsTableViewController class. Add two of the three data sources and a constant to the code like this:

class NotificationsTableViewController: UITableViewController { 
    var  notifications = UIApplication.sharedApplication().scheduledLocalNotifications
    
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return notifications!.count
    }
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }
}

Our constant notifications contains an array of the scheduled notifications. We’ll use one section in the table, and our number of rows will be the number of scheduled notifications. Now add the last method we need to the class:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
        let row = indexPath.row
        let fireDate = (notifications![row].fireDate)!
        let dateString = NSDateFormatter.localizedStringFromDate(fireDate, dateStyle: .ShortStyle, timeStyle: .LongStyle)
        cell.textLabel?.text = "Notification at " + dateString
        if row % 2 == 0 {cell.backgroundColor = UIColor.lightGrayColor()}
        return cell
    }

This sets up each cell of the table. Since notifications is an array of UILocalNotificaton,s we go down the array getting the fireDate from the notification. We format the fireDate as a string and return it as a cell. I added a line to alternate the colors of the rows to make it slightly more readable.

Build and run. Add several notifications of 30 seconds or more. Press the List button, and you will see your notifications.

2016-04-22_07-32-29

Deleting Notifications

You may need to delete notifications. We’ll delete any selected notification. Add the following code to the NotificationsTableViewController class.

   override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        let row = indexPath.row
        if  let notification = notifications?[row]{
            UIApplication.sharedApplication().cancelLocalNotification(notification)
            notifications = UIApplication.sharedApplication().scheduledLocalNotifications
            tableView.reloadData()
        }

To delete a notification, you use the UIApplication method cancelLocalNotification. We get that by row, cancel it, then refresh our notifications list and table. While I’m usually a bit careless about optionals to make the code easier to understand here’s one exception we need to be careful. Our table is not refreshing itself when a notification occurs. Between the time we look at the table and select a notification to delete, it may have already fired and dismissed itself. We check to make sure it is still there. Build and run. Add a few notifications and try deleting them.

The Local Notification Limit

There is one limitation to local notifications: you can only have 64 waiting to fire at one time. The most recent 64 will remain in the scheduledLocalNotifications, the rest are cancelled.

We’ve made an app that’s supposed to be a HIIT app. If you are not familiar with HIIT, it stands for High Intensity Interval Training. Usually for cardio training, you do a very intense exercise for a short amount of time then rest for a short amount of time to recover, then repeat several times. However, there’s a problem. If I do run/walk/run intervals for HIIT I might have over a hundred intervals. Other apps have nagging modes which remind you every minute or every hour to do something. Both of these would kill the 64 notification limit quickly.

A Solution: presentLocalNotificationsNow

We’ve used scheduleLocalNotification to schedule our notifications. If we don’t schedule them we don’t run up against the 64 notification limit. Instead we use presentLocalNotificationNow and let our code do the timing. We’ll use an NStimer to do this. If you are not familiar with NSTimer and the timing loop we will use, you might refer to my post on them

Go to the ViewController class. Add the following properties:

var timer = NSTimer()
let timeInterval:NSTimeInterval = 10.0
var workout = false
var workoutIntervalCount = 5

The variable timer will be our timer with a firing time of every 15 seconds, which I set in a constant timeInterval. The code will alternate between working out and resting for a set number of intervals found in workoutIntervalCount. To set up the timer we’ll use this function:

//MARK: -  NSTimer based notifications
func startTimer(){
    if !timer.valid{ //prevent more than one timer on the thread
        timer = NSTimer.scheduledTimerWithTimeInterval(
            timeInterval,
            target: self,
            selector: #selector(timerDidEnd),
            userInfo: nil,
            repeats: true)
        }
    }

The timer will repeat every 15 seconds, executing the selector timerDidEnd. We now have to add that function:

func timerDidEnd(timer:NSTimer){
    if workoutIntervalCount == 0 { //finished intervals
        timer.invalidate()
        statusLabel.text = "Workout complete"
    } else {
        workout = !workout
        if !workout {
            statusLabel.text = String(format:"Interval %i Rest",workoutIntervalCount)
            workoutIntervalCount -= 1
        }else{
            statusLabel.text = String(format:"Interval %i Work Out",workoutIntervalCount)    
        }
            
     }
}

This figures out which kind of notification we need to give, alternating between a workout and a rest. At each workout, we decrease workoutIntervalCount  by  one until we are at zero, where we shut off the timer. Under this code in the function timerDidEnd place the following to make our notification:

 //make the local notification
 let localNotification = UILocalNotification()
 localNotification.alertBody = statusLabel.text!
 localNotification.timeZone = NSTimeZone.defaultTimeZone()
 localNotification.applicationIconBadgeNumber = workoutIntervalCount
 //set the notification
 UIApplication.sharedApplication().presentLocalNotificationNow(localNotification)
}

Line 7 in this code uses presentLocalNotificationNow to immediately send the notification. Since this notification sends immediately, it ignores fireDate so we didn’t assign it this time. We did include line 5 however. Notifications are also badges, those little numbers on the corners of some icons, such as the one for mail. Using the property applicationIconBadgeNumber, We set it to the current workout interval count.
Instead of making a new button for this , we’ll run the code when seconds = 0 . Change setInterval to this:

    @IBAction func setInterval(sender: UIButton) {
        //make a status string
        if seconds > 0 {
            let index = workoutType.selectedSegmentIndex
            let workout = workoutType.titleForSegmentAtIndex(index)!
            let status = displaySeconds() + " seconds " + workout
            statusLabel.text = status + " set"
        
            let notificationTypes =  UIApplication.sharedApplication().currentUserNotificationSettings()?.types
            let noNotifications = UIUserNotificationType.None.rawValue
            if notificationTypes!.rawValue == noNotifications {
                statusLabel.text = "Please turn on notifications in Settings."
            } else{
                //make the local notification
                let localNotification = UILocalNotification()
                localNotification.fireDate = NSDate(timeIntervalSinceNow:seconds)
                localNotification.alertBody = status + " complete"
                localNotification.timeZone = NSTimeZone.defaultTimeZone()
                //set the notification
                UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
            
            }
        }else{
            workoutIntervalCount = 5
            startTimer()
        }

    }

Now when we have a value of 0 for seconds our automatic interval counter will send notifications every 15 seconds. Build and run using the simulator. Tap the set interval button. Press Command-Shift-H and watch the results:

2016-04-23_09-41-352016-04-23_09-41-44

2016-04-23_09-43-15

You’ll also notice the badge on the icon.

2016-04-23_09-41-50

Running on an iPhone: Using the Background

Up to this point we have been running this application on a simulator. You may get different results on an iPhone for the NSTimer based notifications. You may get nothing. Real devices suspend background operations unless you state otherwise. Fortunately, we can do that with a few more lines of code.
Add the highlighted line of code.

//NSTimer executed functions
    func startTimer(){
        if !timer.valid{ //prevent more than one timer on the thread
            backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler(nil)
            timer = NSTimer.scheduledTimerWithTimeInterval(
                timeInterval,
                target: self,
                selector: #selector(timerDidEnd),
                userInfo: nil,
                repeats: true)
            
        }
        
        
    }

The beginBackgroundTaskWithExpirationHandler method registers our code as a background task. iOS gives this background task an amount of time to finish. If it expires, the code in the closure runs. You can then gracefully shout down things. We’ll keep our app simple and leave the closure nil, which will end the app. This is only for simplicity. It is much better to write a handler.

The beginBackgroundTaskWithExpirationHandler method returns a unique ID as an Int. We’ll store this ID as a property of our classbackgroundTask. At the top of our class, add this

 var backgroundTask = 0

When our timer loop completes, we dismiss our app from the background. Add the highlighted line to timerDidEnd

func timerDidEnd(timer:NSTimer){
        //decrement the counter
        if workoutIntervalCount == 0 { //finshed intervals
            timer.invalidate()
            statusLabel.text = "Workout complete"
        UIApplication.sharedApplication().endBackgroundTask(backgroundTask)

This deregisters the background task using the ID of the task.
With this code, you can build and run a a iPhone and get the same results as the simulator.

Local Notifications and Apple WatchOS

If you have an Apple Watch and run this App on a phone, try running the app on your phone for the five intervals. Once started, put your phone to sleep by tapping the sleep switch. If your phone is asleep, your watch will display the notifications that were going to your phone.

Photo Apr 23, 10 10 52 AM

Notifications to on the Apple Watch are completely free and automatic. No extra programming is required.

Photo Apr 24, 7 27 25 AM

However, you may have noticed that notification in some apps give you haptics and sounds. Some notifications have extra buttons. These are controlled by more actions we can add to both iOS and WatchOS. In the next lesson,  we’ll look at how to make more buttons on your notification first in iOS and then in Watch OS.

The Whole Code

ViewController.swift

//
//  ViewController.swift
//  SwiftNotificationDemo
//
//  Created by Steven Lipton on 4/20/16.
//  Copyright © 2016 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UIViewController {
    //MARK: Properties
    var seconds:NSTimeInterval = 0.0
    var backgroundTask = 0
    //MARK: - Outlets
    @IBOutlet weak var statusLabel: UILabel!
    @IBOutlet weak var workoutType: UISegmentedControl!
    @IBOutlet weak var secondsIndicator: UILabel!
    
    
    //MARK: - Actions
    @IBAction func upArrow(sender: UIButton) {
        seconds += 1.0
        secondsIndicator.text = displaySeconds()
        
    }
    @IBAction func downArrow(sender: UIButton) {
        seconds -= 1
        if seconds < 0{ seconds = 0.0 } secondsIndicator.text = displaySeconds() } @IBAction func setInterval(sender: UIButton) { //make a status string if seconds > 0 {
            let index = workoutType.selectedSegmentIndex
            let workout = workoutType.titleForSegmentAtIndex(index)!
            let status = displaySeconds() + " seconds " + workout
            statusLabel.text = status + " set"
        
            let notificationTypes =  UIApplication.sharedApplication().currentUserNotificationSettings()?.types
            let noNotifications = UIUserNotificationType.None.rawValue
            if notificationTypes!.rawValue == noNotifications {
                statusLabel.text = "Please turn on notifications in Settings."
            } else{
                //make the local notification
                let localNotification = UILocalNotification()
                localNotification.fireDate = NSDate(timeIntervalSinceNow:seconds)
                localNotification.alertBody = status + " complete"
                localNotification.timeZone = NSTimeZone.defaultTimeZone()
                //set the notification
                UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
            
            }
        }else{
            workoutIntervalCount = 5
            startTimer()
        }

    }
    //MARK: - Instance Methods
    func displaySeconds() -> String{
        let mySeconds = String(format:"%02i",Int(seconds))
        return mySeconds
    }
    
    
    //MARK: -  NSTimer based notifications
    var timer = NSTimer()
    let timeInterval:NSTimeInterval = 5.0
    var workout = false
    var workoutIntervalCount = 5

    
    //NSTimer executed functions
    func startTimer(){
        if !timer.valid{ //prevent more than one timer on the thread
            backgroundTask = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler(nil)
            timer = NSTimer.scheduledTimerWithTimeInterval(
                timeInterval,
                target: self,
                selector: #selector(timerDidEnd),
                userInfo: nil,
                repeats: true)
            
        }
        
        
    }
    
    func timerDidEnd(timer:NSTimer){
        //decrement the counter
        if workoutIntervalCount == 0 { //finshed intervals
            timer.invalidate()
            statusLabel.text = "Workout complete"
        UIApplication.sharedApplication().endBackgroundTask(backgroundTask)
        } else {
            workout = !workout
            if !workout {
                statusLabel.text = String(format:"Interval %i Rest",workoutIntervalCount)
                workoutIntervalCount -= 1
            }else{
                statusLabel.text = String(format:"Interval %i Work Out",workoutIntervalCount)
                
            }
            
        }
        //make the local notification
        let localNotification = UILocalNotification()
        localNotification.alertBody = statusLabel.text!
        localNotification.timeZone = NSTimeZone.defaultTimeZone()
        localNotification.applicationIconBadgeNumber = workoutIntervalCount
        
        //set the notification
        UIApplication.sharedApplication().presentLocalNotificationNow(localNotification)
}
    //MARK: - Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
       
        // Do any additional setup after loading the view, typically from a nib.
    }

}


AppDelegate.swift

//
//  AppDelegate.swift
//  SwiftNotificationDemo
//
//  Created by Steven Lipton on 4/20/16.
//  Copyright © 2016 MakeAppPie.Com. All rights reserved.
//

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

//methods not overridden not included. 
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        application.registerUserNotificationSettings(
            UIUserNotificationSettings(
                forTypes:[.Alert, .Sound, .Badge],
                categories: nil
            )
        )
        return true
    }
}

NotificationsTableViewController.swift

//
//  NotificationsTableViewController.swift
//  SwiftNotificationDemo
//
//  Created by Steven Lipton on 4/22/16.
//  Copyright © 2016 MakeAppPie.Com. All rights reserved.
//

import UIKit

class NotificationsTableViewController: UITableViewController {
    
    var  notifications = UIApplication.sharedApplication().scheduledLocalNotifications
    
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return notifications!.count
    }
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
        let row = indexPath.row
        let fireDate = (notifications![row].fireDate)!
        let dateString = NSDateFormatter.localizedStringFromDate(fireDate, dateStyle: .ShortStyle, timeStyle: .LongStyle)
        cell.textLabel?.text = "Notification at " + dateString
        if row % 2 == 0 {cell.backgroundColor = UIColor.lightGrayColor()}
        return cell
    }
    
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        let row = indexPath.row
        if  let notification = notifications?[row]{
            UIApplication.sharedApplication().cancelLocalNotification(notification)
            notifications = UIApplication.sharedApplication().scheduledLocalNotifications
            tableView.reloadData()
        }
        
    }
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    

}