{"id":1689,"date":"2015-03-04T21:55:15","date_gmt":"2015-03-05T03:55:15","guid":{"rendered":"http:\/\/jamesonquave.com\/blog\/?p=1689"},"modified":"2018-09-03T06:24:49","modified_gmt":"2018-09-03T12:24:49","slug":"local-notifications-in-ios-8-with-swift-part-1","status":"publish","type":"post","link":"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/","title":{"rendered":"Local Notifications in iOS 10 with Swift (Part 1)"},"content":{"rendered":"<h1><span style=\"color: #0000ff;\">This post\u00a0has been updated for compatibility with XCode 8\u00a0and iOS 10<\/span><\/h1>\n<p>Local notifications are a simple way to display information to your user even when your app is in the background. They allow you to display an alert, play a sound or badge your app\u2019s icon. Local notifications can be triggered at a scheduled time or when your user enters or leaves a geographical area. In this tutorial, we\u2019ll create a simple to-do list application and delve into a number of UILocalNotification features and quirks.<\/p>\n<p>Note: There is an updated API that I have done an additional video tutorial for here:<br \/>\n<iframe loading=\"lazy\" src=\"https:\/\/www.youtube.com\/embed\/5TYKjt-2LMc\" width=\"560\" height=\"315\" frameborder=\"0\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<p>First, let&#8217;s create a new single view application in Xcode and call it LocalNotificationsTutorial. Remember to choose Swift as the language.<\/p>\n<p><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-10.15.42-PM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1705 size-full\" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-10.15.42-PM.png\" alt=\"Screen Shot 2015-01-30 at 10.15.42 PM\" width=\"730\" height=\"430\" srcset=\"https:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-10.15.42-PM.png 730w, https:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-10.15.42-PM-300x177.png 300w, https:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-10.15.42-PM-500x295.png 500w\" sizes=\"(max-width: 730px) 100vw, 730px\" \/><\/a><\/p>\n<p>Before we delve into writing any code, let&#8217;s get our view controllers and views set up. This is a necessary step, and I\u2019ll be covering some of the basics of using Interface Builder, but if want to skip it and <a href=\"#scheduling\">jump right into dealing with scheduling notifications<\/a> you can follow along by getting\u00a0the configured\u00a0application from\u00a0<a href=\"https:\/\/github.com\/jasonbnewell\/LocalNotificationTutorials\/tree\/part1_simplified\">here<\/a>.<\/p>\n<h1><b>Configuring the Views<\/b><\/h1>\n<p>Our finished application will have two views in a navigation controller: a root view that displays a chronologically ordered list of to-do items with deadlines, and a view for creating to-do list items.<\/p>\n<p><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/iOS-Simulator-Screen-Shot-Feb-4-2015-10.26.58-PM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1715 \" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/iOS-Simulator-Screen-Shot-Feb-1-2015-11.43.36-PM-576x1024.png\" alt=\"iOS Simulator Screen Shot Feb 1, 2015, 11.43.36 PM\" width=\"250\" height=\"437\" \/>\u00a0<img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1719 \" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/iOS-Simulator-Screen-Shot-Feb-4-2015-10.26.58-PM-576x1024.png\" alt=\"iOS Simulator Screen Shot Feb 4, 2015, 10.26.58 PM\" width=\"250\" height=\"439\" \/><\/a><\/p>\n<h1><b>Creating the View Controllers<\/b><\/h1>\n<p>Before opening Interface Builder, we should generate view controllers to back each of our views. Ctrl or right-click on the project group in the project navigator and select \u201cNew File\u201d.<\/p>\n<p><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-10.29.29-PM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1706 size-full\" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-10.29.29-PM.png\" alt=\"Screen Shot 2015-01-30 at 10.29.29 PM\" width=\"519\" height=\"193\" srcset=\"https:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-10.29.29-PM.png 519w, https:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-10.29.29-PM-300x112.png 300w, https:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-10.29.29-PM-500x186.png 500w\" sizes=\"(max-width: 519px) 100vw, 519px\" \/><\/a><\/p>\n<p>Select \u201cCocoa Touch Class\u201d from the &#8220;iOS -&gt; Source&#8221; menu and create a subclass of UITableViewController named \u201cTodoTableViewController\u201d. Don\u2019t create a XIB file, and, of course, the language should be Swift. This will be our root view controller and it will be in charge of displaying the to-do list.<\/p>\n<p>&#8211; Also read about: <a href=\"https:\/\/www.msidata.com\/field-service-scheduling-software\">Field Scheduling<\/a>.<\/p>\n<p>We need a separate view for creating our to-do items. Repeat the process, this time create a UIViewController subclass and name it \u201cTodoSchedulingViewController\u201d.<\/p>\n<h1><b>Setting up Navigation<\/b><\/h1>\n<p>Now that our view controllers are created, let&#8217;s hook them into our project\u2019s storyboard. Click \u201cMain.storyboard\u201d and delete the root view controller. Go ahead and delete \u201cViewController.swift\u201d as well. We won\u2019t be using it.<\/p>\n<p>Drag a new navigation controller from the object library onto the now blank storyboard. We\u2019ve deleted our root view, so drag a Storyboard Entry Point onto the navigation controller so our application will have a root view.<\/p>\n<figure id=\"attachment_1707\" aria-describedby=\"caption-attachment-1707\" style=\"width: 262px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-11.11.26-PM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-1707 size-full\" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-11.11.26-PM.png\" alt=\"Screen Shot 2015-01-30 at 11.11.26 PM\" width=\"262\" height=\"251\" \/><\/a><figcaption id=\"caption-attachment-1707\" class=\"wp-caption-text\">The object library<\/figcaption><\/figure>\n<p>Select the navigation controller\u2019s root view (a table view) and set its custom class to \u201cTodoTableViewController\u201d in the identity inspector.<\/p>\n<figure id=\"attachment_1708\" aria-describedby=\"caption-attachment-1708\" style=\"width: 261px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-11.22.17-PM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-1708 size-full\" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-11.22.17-PM.png\" alt=\"Screen Shot 2015-01-30 at 11.22.17 PM\" width=\"261\" height=\"109\" \/><\/a><figcaption id=\"caption-attachment-1708\" class=\"wp-caption-text\">The identity inspector<\/figcaption><\/figure>\n<p>Since we\u2019re going to display deadlines for each to-do item, we need to select the table view\u2019s first (and only) prototype cell, switch to the attributes inspector, and set the cell\u2019s style to \u201cSubtitle\u201d. \u00a0It needs a reuse identifier as well, so we can refer to it in our code. We\u2019ll use \u201ctodoCell\u201d.<\/p>\n<figure id=\"attachment_1709\" aria-describedby=\"caption-attachment-1709\" style=\"width: 260px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-11.39.00-PM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-1709 size-full\" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-11.39.00-PM.png\" alt=\"Screen Shot 2015-01-30 at 11.39.00 PM\" width=\"260\" height=\"129\" \/><\/a><figcaption id=\"caption-attachment-1709\" class=\"wp-caption-text\">The attributes inspector<\/figcaption><\/figure>\n<p>Keep the attributes inspector selected. Drag a navigation item onto the table view and give it the title \u201cTodo List\u201d, then drag a bar button item onto that and set the identifier to \u201cAdd\u201d.<\/p>\n<p>Now we\u2019ll set up the view on which users can schedule and title their to-do items. Drag a view controller into the storyboard. Its custom class should be set to \u201cTodoSchedulingViewController\u201d.<\/p>\n<p>Ctrl or right-click on the \u201cAdd\u201d button, drag from the \u201caction\u201d to the new view, and select \u201cshow\u201d. Now all our navigation is linked up.<\/p>\n<p><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-31-at-12.03.33-AM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-1711\" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-31-at-12.03.33-AM.png\" alt=\"Screen Shot 2015-01-31 at 12.03.33 AM\" width=\"286\" height=\"123\" \/><\/a><\/p>\n<p>We need to drag three controls onto this view: a text field (with \u201cTitle\u201d as the placeholder text), a date picker and a button (titled \u201cSave\u201d). Just center and widen all three, then \u201cadd missing constraints\u201d to all views in the view controller (Under \u201cResolve Auto Layout Issues\u201d, the triangle icon towards the bottom right of Xcode). Adding constraints ensures that the view is laid out predictably across various screen sizes (instead of having portions of controls cut off or misaligned).<\/p>\n<p><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-31-at-12.25.08-AM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1712 size-full\" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-31-at-12.25.08-AM.png\" alt=\"Screen Shot 2015-01-31 at 12.25.08 AM\" width=\"1294\" height=\"897\" srcset=\"https:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-31-at-12.25.08-AM.png 1294w, https:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-31-at-12.25.08-AM-300x208.png 300w, https:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-31-at-12.25.08-AM-1024x710.png 1024w, https:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-31-at-12.25.08-AM-433x300.png 433w\" sizes=\"(max-width: 1294px) 100vw, 1294px\" \/><\/a><\/p>\n<h1><b>Connecting Controls to Code<\/b><\/h1>\n<p>Now that our views and navigation are laid out, we have to link our text field and date picker controls to an IBOutlet in TodoSchedulingViewController.swift. This will allow us to access these controls (and their values) in our code. There are a few ways to do this, but the simplest is to enable the Assistant editor by clicking the interlocking circles in the top right of XCode, Ctrl or right-click the control, and drag the \u201cNew Referencing Outlet\u201d circle into the TodoSchedulingViewController class body.<\/p>\n<p><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-31-at-12.45.14-AM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone size-full wp-image-1713\" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-31-at-12.45.14-AM.png\" alt=\"Screen Shot 2015-01-31 at 12.45.14 AM\" width=\"747\" height=\"157\" srcset=\"https:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-31-at-12.45.14-AM.png 747w, https:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-31-at-12.45.14-AM-300x63.png 300w, https:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-31-at-12.45.14-AM-500x105.png 500w\" sizes=\"(max-width: 747px) 100vw, 747px\" \/><\/a><\/p>\n<p>Do this for both the text field and the date picker, naming them \u201ctitleField\u201d and \u201cdeadlinePicker\u201d respectively.<\/p>\n<pre class=\"\u201cbrush:js\u201d\">@IBOutlet weak var titleField: UITextField!\r\n@IBOutlet weak var deadlinePicker: UIDatePicker!\r\n<\/pre>\n<p>The final step is to connect the save button to an IBAction (an event handler function). Just Ctrl or right-click the button, and drag from the \u201cTouch Up Inside\u201d circle to the code window. Name the action \u201csavePressed\u201d and optionally set the sender type to UIButton (no other controls will be firing this action, so we can be more specific).<\/p>\n<pre class=\"\u201cbrush:js\u201d\">@IBAction func savePressed(_ sender: UIButton) {\r\n}\r\n<\/pre>\n<p>The views and navigation for this project are all set up. Go ahead and run the app in the simulator. Try it for a few different devices. You\u2019ll notice that, because of the constraints we added, your views stretch or squeeze to fit the various screen sizes.<\/p>\n<p>Now, let&#8217;s get out of Interface Builder and write some code.<br \/>\n<a name=\"scheduling\"><\/a><\/p>\n<h1><b>Registering Notification Settings<\/b><\/h1>\n<p>We need to register our intent to use\u00a0notifications with the application. Otherwise, the notifications we schedule simply won\u2019t fire. Switch over to your Application Delegate (AppDelegate.swift) and add the following line to application:didFinishLaunchingWithOptions:<\/p>\n<pre class=\"\u201cbrush:js\u201d\">application.registerUserNotificationSettings(UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil))\r\n<\/pre>\n<p>On the first launch of the app, users will now be prompted to allow your app to send\u00a0notifications. If the user grants permission, we will be able to schedule notifications that display a banner, play a sound, and update our app icon\u2019s badge number (which we\u2019ll cover in part 2).<\/p>\n<p><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/iOS-Simulator-Screen-Shot-Feb-3-2015-2.56.37-PM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1716 \" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/iOS-Simulator-Screen-Shot-Feb-3-2015-2.56.37-PM-576x1024.png\" alt=\"iOS Simulator Screen Shot Feb 3, 2015, 2.56.37 PM\" width=\"250\" height=\"441\" \/><\/a><\/p>\n<h1><b>Modeling the Application<\/b><\/h1>\n<p>For a simple app like this, it may be tempting to handle all of the logic in the view controllers that we just created, but we\u2019ll have an easier time understanding and maintaining our code if we keep the management of the to-do list and the presentation logic separate.<\/p>\n<p>I chose to model individual to-do items with a lightweight struct. Let&#8217;s create that now. Just click \u201cFile -&gt; New -&gt; File\u201d, choose \u201cSwift File\u201d and name it \u201cTodoItem\u201d. Each to-do list item has a title and a deadline, so we\u2019ll create properties for both.<\/p>\n<pre class=\"\u201cbrush:js\u201d\">struct TodoItem {\r\n  var title: String\r\n  var deadline: Date\r\n}\r\n<\/pre>\n<p>Ultimately, each to-do list item needs to be backed by an on disk representation, so that the list will persist if the application is terminated. Instances of UILocalNotification have a userInfo property &#8211; a dictionary that we can\u00a0use to store miscellaneous data like the title, but we can\u2019t rely on that alone in this case. Local notifications are automatically unscheduled after they are fired, which means that we wouldn\u2019t be able to retrieve past-due items. We\u2019ll have to use another method to persist our items, and we need a way to associate an item we\u2019ve retrieved from the disk with its corresponding local notification. For that, we\u2019ll use a universally unique identifier (UUID).<\/p>\n<pre class=\"\u201cbrush:js\u201d\">struct TodoItem {\r\n    var title: String\r\n    var deadline: Date\r\n    var UUID: String\r\n\r\n    init(deadline: Date, title: String, UUID: String) {\r\n        self.deadline = deadline\r\n        self.title = title\r\n        self.UUID = UUID\r\n    }\r\n}\r\n<\/pre>\n<p>Since we\u2019re going to display overdue items in red, lets also add a convenience method that returns whether or not an item is overdue.<\/p>\n<pre class=\"\u201cbrush:js\u201d\">var isOverdue: Bool {\r\n    \/\/ Optionally, you can omit \"ComparisonResult\" and it will be inferred.\r\n    return (Date().compare(self.deadline) == ComparisonResult.orderedDescending) \/\/ deadline is earlier than current date\r\n}\r\n<\/pre>\n<h1><\/h1>\n<h1><b>Saving To-Do Items (Scheduling Notifications)<\/b><\/h1>\n<p>We need a class to represent the list of items and handle persisting them. Create a new Swift file named \u201cTodoList\u201d.<\/p>\n<p>Our application is only concerned with maintaining a single to-do list, so it makes sense to make a single shared instance available throughout the app.<\/p>\n<pre class=\"\u201cbrush:js\u201d\">class TodoList {\r\n    class var sharedInstance : TodoList {\r\n        struct Static {\r\n            static let instance: TodoList = TodoList()\r\n        }\r\n        return Static.instance\r\n    }\r\n}\r\n<\/pre>\n<p>This method is the community-accepted way to implement the singleton pattern in Swift, which you can adapt to your own projects. If you\u2019re curious, you can read the details about what it\u2019s doing and why in <a href=\"http:\/\/stackoverflow.com\/questions\/24024549\/dispatch-once-singleton-model-in-swift\/24147830#24147830\">this Stack Overflow answer.<\/a><\/p>\n<p><a href=\"https:\/\/developer.apple.com\/library\/mac\/documentation\/Cocoa\/Reference\/Foundation\/Classes\/NSUserDefaults_Class\/index.html\">UserDefaults<\/a> provides a simple way to persist our to-do items to disk. The following snippet defines a method that adds a dictionary representation of a to-do item to standard user defaults (with UUID as the key), and then creates the associated local notification.<\/p>\n<pre class=\"\u201cbrush:js\u201d\">fileprivate let ITEMS_KEY = \"todoItems\"\r\nfunc addItem(_ item: TodoItem) {\r\n    \/\/ persist a representation of this todo item in UserDefaults\r\n    var todoDictionary = UserDefaults.standard.dictionary(forKey: ITEMS_KEY) ?? Dictionary() \/\/ if todoItems hasn't been set in user defaults, initialize todoDictionary to an empty dictionary using nil-coalescing operator (??)\r\n    todoDictionary[item.UUID] = [\"deadline\": item.deadline, \"title\": item.title, \"UUID\": item.UUID] \/\/ store NSData representation of todo item in dictionary with UUID as key\r\n    UserDefaults.standard.set(todoDictionary, forKey: ITEMS_KEY) \/\/ save\/overwrite todo item list\r\n \r\n    \/\/ create a corresponding local notification\r\n    let notification = UILocalNotification()\r\n    notification.alertBody = \"Todo Item \\\"\\(item.title)\\\" Is Overdue\" \/\/ text that will be displayed in the notification \r\n    notification.alertAction = \"open\" \/\/ text that is displayed after \"slide to...\" on the lock screen - defaults to \"slide to view\" \r\n    notification.fireDate = item.deadline as Date \/\/ todo item due date (when notification will be fired) notification.soundName = UILocalNotificationDefaultSoundName \/\/ play default sound \r\n    notification.userInfo = [\"title\": item.title, \"UUID\": item.UUID] \/\/ assign a unique identifier to the notification so that we can retrieve it later\r\n \r\n    UIApplication.shared.scheduleLocalNotification(notification)\r\n}\r\n<\/pre>\n<p>Notice that we\u2019re just playing the default sound when the notification fires. You can provide your own sound file, but audio files over 30 seconds in length are not supported. The default sound will play instead.<\/p>\n<p>We\u2019re almost to the point where users can create new list items. It\u2019s time to implement savePressed: in TodoSchedulingViewController.<\/p>\n<pre class=\"\u201cbrush:js\u201d\">@IBAction func savePressed(_ sender: UIButton) {\r\n    let todoItem = TodoItem(deadline: deadlinePicker.date, title: titleField.text!, UUID: UUID().uuidString)\r\n    TodoList.sharedInstance.addItem(todoItem) \/\/ schedule a local notification to persist this item\r\n    let _ = self.navigationController?.popToRootViewController(animated: true) \/\/ return to list view\r\n}\r\n<\/pre>\n<p>Note that, since this is a new to-do list entry, we\u2019re passing in a newly generated UUID.<\/p>\n<p>Try out the app now. Launch it in the simulator, create a new item due a minute in the future, and return to the home or lock screen (Shift-CMD-H or CMD-L) to view the notification. The notification won\u2019t necessarily fire right on the stroke of the minute (due to a hidden \u2018seconds\u2019 value on the time picker control), but you\u2019ll see it within the minute.<\/p>\n<p><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/iOS-Simulator-Screen-Shot-Feb-3-2015-4.29.05-PM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1717 \" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/iOS-Simulator-Screen-Shot-Feb-3-2015-4.29.05-PM-576x1024.png\" alt=\"iOS Simulator Screen Shot Feb 3, 2015, 4.29.05 PM\" width=\"250\" height=\"441\" \/><\/a><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/iOS-Simulator-Screen-Shot-Feb-3-2015-4.33.13-PM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1718 \" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/iOS-Simulator-Screen-Shot-Feb-3-2015-4.33.13-PM-576x1024.png\" alt=\"iOS Simulator Screen Shot Feb 3, 2015, 4.33.13 PM\" width=\"250\" height=\"441\" \/><\/a><\/p>\n<h1><b>The 64 Notification Limit<\/b><\/h1>\n<p>It\u2019s important to note that you\u2019re limited to scheduling 64 local notifications. If you schedule more, the system will keep the 64 soonest firing notifications and automatically discard the rest.<\/p>\n<p>We can avoid running into this issue by disallowing the creation of new items if 64 already exist.<\/p>\n<p>In TodoTableViewController:<\/p>\n<pre class=\"\u201cbrush:js\u201d\">func refreshList() {\r\n    todoItems = TodoList.sharedInstance.allItems()\r\n    if (todoItems.count &gt;= 64) {\r\n        self.navigationItem.rightBarButtonItem!.enabled = false \/\/ disable 'add' button\r\n    }\r\n    tableView.reloadData()\r\n}\r\n<\/pre>\n<h1><b>Retrieving To-Do Items<\/b><\/h1>\n<p>The fact that to-do items are persisted as an array of dictionaries is an implementation detail that outside classes shouldn\u2019t have to worry about. Our TodoList class needs a public facing function that the list view controller can query to retrieve a list of to-do items.<\/p>\n<pre class=\"\u201cbrush:js\u201d\">func allItems() -&gt; [TodoItem] {\r\n    let todoDictionary = UserDefaults.standard.dictionary(forKey: ITEMS_KEY) ?? [:]\r\n    let items = Array(todoDictionary.values)\r\n    return items.map({\r\n        let item = $0 as! [String:AnyObject]\r\n        return TodoItem(deadline: item[\"deadline\"] as! Date, title: item[\"title\"] as! String, UUID: item[\"UUID\"] as! String!)\r\n    }).sorted(by: {(left: TodoItem, right:TodoItem) -&gt; Bool in\r\n        (left.deadline.compare(right.deadline) == .orderedAscending)\r\n    })\r\n}\r\n<\/pre>\n<p>This function retrieves the array of item representation from disk, converts it to an array of TodoItem instances using an unnamed closure we pass to map, and sorts that array chronologically. Describing the map and sort functions in detail is beyond the scope of this tutorial, but you can find more information in the Swift language guide\u2019s section on <a href=\"https:\/\/developer.apple.com\/library\/mac\/documentation\/Swift\/Conceptual\/Swift_Programming_Language\/Closures.html#\/\/apple_ref\/doc\/uid\/TP40014097-CH11-ID94\">closures<\/a>.<\/p>\n<p>Now we can hook up TodoTableViewController to display the list.<\/p>\n<pre class=\"\u201cbrush:js\u201d\">class TodoTableViewController: UITableViewController {\r\n    var todoItems: [TodoItem] = []\r\n    override func viewDidLoad() {\r\n        super.viewDidLoad()\r\n    }\r\n\r\n    override func viewWillAppear(_ animated: Bool) {\r\n        super.viewWillAppear(animated)\r\n        refreshList()\r\n    }\r\n\r\n    func refreshList() {\r\n        todoItems = TodoList.sharedInstance.allItems()\r\n\r\n        if (todoItems.count &gt;= 64) {\r\n            self.navigationItem.rightBarButtonItem!.enabled = false \/\/ disable 'add' button\r\n        }\r\n        tableView.reloadData()\r\n    }\r\n\r\n    override func numberOfSections(in tableView: UITableView) -&gt; Int {\r\n        return 1\r\n    }\r\n\r\n    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {\r\n        return todoItems.count\r\n    }\r\n\r\n    override func tableView(_ tableView: UITableView, cellForRowAtIndexPath indexPath: IndexPath) -&gt; UITableViewCell {\r\n        let cell = tableView.dequeueReusableCell(withIdentifier: \"todoCell\", for: indexPath) \/\/ retrieve the prototype cell (subtitle style)\r\n        let todoItem = todoItems[(indexPath as NSIndexPath).row] as TodoItem\r\n        cell.textLabel?.text = todoItem.title as String!\r\n        if (todoItem.isOverdue) { \/\/ the current time is later than the to-do item's deadline\r\n            cell.detailTextLabel?.textColor = UIColor.red\r\n        } else {\r\n            cell.detailTextLabel?.textColor = UIColor.black \/\/ we need to reset this because a cell with red subtitle may be returned by dequeueReusableCellWithIdentifier:indexPath:\r\n        }\r\n\r\n        let dateFormatter = DateFormatter()\r\n        dateFormatter.dateFormat = \"'Due' MMM dd 'at' h:mm a\" \/\/ example: \"Due Jan 01 at 12:00 PM\"\r\n        cell.detailTextLabel?.text = dateFormatter.string(from: todoItem.deadline as Date)\r\n        return cell\r\n    }\r\n}\r\n<\/pre>\n<p>Our to-do list now shows each item in chronological order, with the date label in red if the item is overdue.<\/p>\n<p><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/iOS-Simulator-Screen-Shot-Feb-4-2015-10.26.58-PM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1719 \" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/iOS-Simulator-Screen-Shot-Feb-4-2015-10.26.58-PM-576x1024.png\" alt=\"iOS Simulator Screen Shot Feb 4, 2015, 10.26.58 PM\" width=\"250\" height=\"441\" \/><\/a><\/p>\n<p>There are two issues we need to deal with here. Users currently don\u2019t receive any visual feedback that a notification has fired (and a to-do item is overdue) when the app is running in the foreground. Also, when the app resumes, the list won\u2019t automatically be refreshed, meaning that missed deadlines may not appear in red. Lets solve both issues now.<\/p>\n<p>In TodoTableViewController:<\/p>\n<pre class=\"\u201cbrush:js\u201d\">override func viewDidLoad() {\r\n    super.viewDidLoad()\r\n    NotificationCenter.default.addObserver(self, selector: #selector(TodoTableViewController.refreshList), name: NSNotification.Name(rawValue: \"TodoListShouldRefresh\"), object: nil)\r\n}\r\n<\/pre>\n<p>In AppDelegate:<\/p>\n<pre class=\"\u201cbrush:js\u201d\">func application(_ application: UIApplication, didReceive notification: UILocalNotification) {\r\n    NotificationCenter.default.post(name: Notification.Name(rawValue: \"TodoListShouldRefresh\"), object: self)\r\n}\r\n<\/pre>\n<pre class=\"\u201cbrush:js\u201d\">func applicationDidBecomeActive(_ application: UIApplication) {\u00a0     \r\n    NotificationCenter.default.post(name: Notification.Name(rawValue: \"TodoListShouldRefresh\"), object: self)\r\n}\r\n<\/pre>\n<p>Please note that, despite the presence of the word \u201cnotification\u201d, NotificationCenter is unrelated to UILocalNotification. NotificationCenter\u2019s purpose is to provide a simple way to implement the <a href=\"http:\/\/en.wikipedia.org\/wiki\/Observer_pattern\">observer pattern<\/a> in your apps.<\/p>\n<p>Here we register TodoTableViewController as an observer to the \u201cTodoListShouldRefresh\u201d notification. Whenever a notification with that name is posted, the reloadData method will be called.<\/p>\n<p>I&#8217;ve omitted this step, but\u00a0it is generally better to define notification names as static constants to avoid repeating yourself.<\/p>\n<h1><b>Completing To-Do Items (Canceling Notifications)<\/b><\/h1>\n<p>Our to-do list application isn\u2019t very useful without a way to clear out completed items, and the simplest way to do that is to delete them. We need to add some functionality to TodoList.<\/p>\n<pre class=\"\u201cbrush:js\u201d\">func removeItem(_ item: TodoItem) {\r\n    let scheduledNotifications: [UILocalNotification]? = UIApplication.shared.scheduledLocalNotifications\r\n    guard scheduledNotifications != nil else {return} \/\/ Nothing to remove, so return\r\n \r\n    for notification in scheduledNotifications! { \/\/ loop through notifications...    \r\n        if (notification.userInfo![\"UUID\"] as! String == item.UUID) { \/\/ ...and cancel the notification that corresponds to this TodoItem instance (matched\r\n            UIApplication.shared.cancelLocalNotification(notification) \/\/ there should be a maximum of one match on UUID\r\n            break\r\n        }\r\n    }\r\n\r\n    if var todoItems = UserDefaults.standard.dictionaryForKey(ITEMS_KEY) {\r\n        todoItems.removeValue(forKey: item.UUID)\r\n        UserDefaults.standard.set(todoItems, forKey: ITEMS_KEY) \/\/ save\/overwrite todo item list\r\n    }\r\n}\r\n<\/pre>\n<p>Note that passing an existing notification to scheduleLocalNotification: will cause a duplicate to be created. If you want to give users the ability to edit existing local notifications, you\u2019ll need to retrieve the old one and cancel it before scheduling the new one.<\/p>\n<p>Now we just need to allow users to remove items by swiping the item\u2019s cell and pressing \u201cComplete\u201d.<\/p>\n<p>In TodoTableViewController:<\/p>\n<pre class=\"\u201cbrush:js\u201d\">override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -&gt; Bool {\r\n    return true \/\/ all cells are editable\r\n}\r\noverride func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {\r\n    if editingStyle == .delete { \/\/ the only editing style we'll support\r\n        \/\/ delete the row from the data source\r\n        let item = todoItems.remove(at: (indexPath as NSIndexPath).row) \/\/ remove TodoItem from notifications array, assign removed item to 'item'\r\n        tableView.deleteRows(at: [indexPath], with: .fade) \r\n        TodoList.sharedInstance.removeItem(item) \/\/ delete backing property list entry and unschedule local notification (if it still exists) \r\n        self.navigationItem.rightBarButtonItem!.isEnabled = true \/\/ we definitely have under 64 notifications scheduled now, make sure 'add' button is enabled\r\n    }\r\n}\r\n<\/pre>\n<h1><a href=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/iOS-Simulator-Screen-Shot-Feb-4-2015-10.26.58-PM.png\"><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-1719 \" src=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/iOS-Simulator-Screen-Shot-Feb-4-2015-10.26.58-PM-576x1024.png\" alt=\"iOS Simulator Screen Shot Feb 4, 2015, 10.26.58 PM\" width=\"250\" height=\"441\" \/><\/a><\/h1>\n<h1><b>Conclusion<\/b><\/h1>\n<p>We now have a working to-do list application that lets our users schedule and cancel local notifications with sound and custom alert messages. The source code can be downloaded <a href=\"https:\/\/github.com\/jasonbnewell\/LocalNotificationTutorials\/tree\/part1_simplified\">here<\/a>.<\/p>\n<p>In part 2 of this series, which builds on this project, we\u2019ll add support for an application icon badge and learn about notification actions, a new feature that allows us to trigger code from a notification without ever opening the app.<\/p>\n<p><a href=\"http:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-2\/\">Go to part 2 now \u00bb<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post\u00a0has been updated for compatibility with XCode 8\u00a0and iOS 10 Local notifications are a simple way to display information to your user even when your app is in the background. They allow you to display an alert, play a sound or badge your app\u2019s icon. Local notifications can be triggered at a scheduled time&#8230;<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_links_to":"","_links_to_target":""},"categories":[50,26,21,25,10,5,32],"tags":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v19.13 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Local Notifications in iOS 10 with Swift (Part 1) - Jameson Quave<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Local Notifications in iOS 10 with Swift (Part 1) - Jameson Quave\" \/>\n<meta property=\"og:description\" content=\"This post\u00a0has been updated for compatibility with XCode 8\u00a0and iOS 10 Local notifications are a simple way to display information to your user even when your app is in the background. They allow you to display an alert, play a sound or badge your app\u2019s icon. Local notifications can be triggered at a scheduled time...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/\" \/>\n<meta property=\"og:site_name\" content=\"Jameson Quave\" \/>\n<meta property=\"article:published_time\" content=\"2015-03-05T03:55:15+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-09-03T12:24:49+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-10.15.42-PM.png\" \/>\n<meta name=\"author\" content=\"Jason Newell\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jason Newell\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/\",\"url\":\"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/\",\"name\":\"Local Notifications in iOS 10 with Swift (Part 1) - Jameson Quave\",\"isPartOf\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/#website\"},\"datePublished\":\"2015-03-05T03:55:15+00:00\",\"dateModified\":\"2018-09-03T12:24:49+00:00\",\"author\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/f9280beb862ff805cb1e4545311f8c4b\"},\"breadcrumb\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/jamesonquave.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Local Notifications in iOS 10 with Swift (Part 1)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/#website\",\"url\":\"https:\/\/jamesonquave.com\/blog\/\",\"name\":\"Jameson Quave\",\"description\":\"Using computer technology to educate, and improve lives.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/jamesonquave.com\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/f9280beb862ff805cb1e4545311f8c4b\",\"name\":\"Jason Newell\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/575497a137605d6bb40c2a2e0f77d0b9?s=96&d=retro&r=pg\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/575497a137605d6bb40c2a2e0f77d0b9?s=96&d=retro&r=pg\",\"caption\":\"Jason Newell\"},\"description\":\"Jason Newell is a full-stack web and application developer specializing in server-side Ruby, Swift, and most recently TVML. Most recently, Jason has developed the DAD App platform for Apple TV.\",\"url\":\"https:\/\/jamesonquave.com\/blog\/author\/newell-jasonb\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Local Notifications in iOS 10 with Swift (Part 1) - Jameson Quave","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/","og_locale":"en_US","og_type":"article","og_title":"Local Notifications in iOS 10 with Swift (Part 1) - Jameson Quave","og_description":"This post\u00a0has been updated for compatibility with XCode 8\u00a0and iOS 10 Local notifications are a simple way to display information to your user even when your app is in the background. They allow you to display an alert, play a sound or badge your app\u2019s icon. Local notifications can be triggered at a scheduled time...","og_url":"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/","og_site_name":"Jameson Quave","article_published_time":"2015-03-05T03:55:15+00:00","article_modified_time":"2018-09-03T12:24:49+00:00","og_image":[{"url":"http:\/\/jamesonquave.com\/blog\/wp-content\/uploads\/Screen-Shot-2015-01-30-at-10.15.42-PM.png"}],"author":"Jason Newell","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Jason Newell","Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/","url":"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/","name":"Local Notifications in iOS 10 with Swift (Part 1) - Jameson Quave","isPartOf":{"@id":"https:\/\/jamesonquave.com\/blog\/#website"},"datePublished":"2015-03-05T03:55:15+00:00","dateModified":"2018-09-03T12:24:49+00:00","author":{"@id":"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/f9280beb862ff805cb1e4545311f8c4b"},"breadcrumb":{"@id":"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/jamesonquave.com\/blog\/local-notifications-in-ios-8-with-swift-part-1\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/jamesonquave.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Local Notifications in iOS 10 with Swift (Part 1)"}]},{"@type":"WebSite","@id":"https:\/\/jamesonquave.com\/blog\/#website","url":"https:\/\/jamesonquave.com\/blog\/","name":"Jameson Quave","description":"Using computer technology to educate, and improve lives.","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/jamesonquave.com\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/f9280beb862ff805cb1e4545311f8c4b","name":"Jason Newell","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/575497a137605d6bb40c2a2e0f77d0b9?s=96&d=retro&r=pg","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/575497a137605d6bb40c2a2e0f77d0b9?s=96&d=retro&r=pg","caption":"Jason Newell"},"description":"Jason Newell is a full-stack web and application developer specializing in server-side Ruby, Swift, and most recently TVML. Most recently, Jason has developed the DAD App platform for Apple TV.","url":"https:\/\/jamesonquave.com\/blog\/author\/newell-jasonb\/"}]}},"_links":{"self":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/1689"}],"collection":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/comments?post=1689"}],"version-history":[{"count":29,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/1689\/revisions"}],"predecessor-version":[{"id":2413,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/1689\/revisions\/2413"}],"wp:attachment":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/media?parent=1689"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/categories?post=1689"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/tags?post=1689"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}