How To Make an iPhone Transmit an iBeacon Emitter And Working With iBeacons

How To Make an iPhone Transmit an iBeacon Emitter

iOS 7.0 introduced not only the ability to detect iBeacons, but also the ability to create iBeacons – for iPhones and iPads to broadcast their own beacon signal that can then be detected by other devices. To make this work, you add these two imports:

import CoreBluetooth
import CoreLocation

Now make your view controller (or other class) conform to the CBPeripheralManagerDelegate protocol, like this:

class ViewController: UIViewController, CBPeripheralManagerDelegate {

To make your beacon work, you need to create three properties: the beacon itself, plus two Bluetooth properties that store configuration and management information. Add these three now:

var localBeacon: CLBeaconRegion!
var beaconPeripheralData: NSDictionary!
var peripheralManager: CBPeripheralManager!

Finally the code: here are three functions you can use to add local beacons to your app. The first one creates the beacon and starts broadcasting, the second one stops the beacon, and the third one acts as an intermediary between your app and the iOS Bluetooth stack:

func initLocalBeacon() {
    if localBeacon != nil {
        stopLocalBeacon()
    }

    let localBeaconUUID = "5A4BCFCE-174E-4BAC-A814-092E77F6B7E5"
    let localBeaconMajor: CLBeaconMajorValue = 123
    let localBeaconMinor: CLBeaconMinorValue = 456

    let uuid = UUID(uuidString: localBeaconUUID)!
    localBeacon = CLBeaconRegion(proximityUUID: uuid, major: localBeaconMajor, minor: localBeaconMinor, identifier: "Your private identifer here")

    beaconPeripheralData = localBeacon.peripheralData(withMeasuredPower: nil)
    peripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: nil)
}

func stopLocalBeacon() {
    peripheralManager.stopAdvertising()
    peripheralManager = nil
    beaconPeripheralData = nil
    localBeacon = nil
}

func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
    if peripheral.state == .poweredOn {
        peripheralManager.startAdvertising(beaconPeripheralData as! [String: AnyObject]!)
    } else if peripheral.state == .poweredOff {
        peripheralManager.stopAdvertising()
    }
}

func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?) {
 if error != nil {
 print("error" + (error?.localizedDescription)!)
 }
 }
 
 func peripheralManager(_ peripheral: CBPeripheralManager, willRestoreState dict: [String : Any]) {
 print("dict \(dict)")
 }

iBeacon TUTORIAL

There are many iBeacon devices available; a quick Google search should help reveal them to you. But when Apple introduced iBeacon, they also announced that any compatible iOS device could act as an iBeacon. The list currently includes the following devices:

  • iPhone 4s or later
  • 3rd generation iPad or later
  • iPad Mini or later
  • 5th generation iPod touch or later

An iBeacon is nothing more than a Bluetooth Low Energy device that advertises information in a specific structure. Those specifics are beyond the scope of this tutorial, but the important thing to understand is that iOS can monitor for iBeacons that emit three values known as: UUID, major and minor.

UUID is an acronym for universally unique identifier, which is a 128-bit value that’s usually shown as a hex string like this: B558CBDA-4472-4211-A350-FF1196FFE8C8. In the context of iBeacons, a UUID is generally used to represent your top-level identity.

Major and minor values provide a little more granularity on top of the UUID. These values are simply 16 bit unsigned integers that identify each individual iBeacon, even ones with the same UUID.

For instance, if you owned multiple department stores you might have all of your iBeacons emit the same UUID, but each store would have its own major value, and each department within that store would have its own minor value. Your app could then respond to an iBeacon located in the shoe department of your Miami, Florida store.

ForgetMeNot Starter Project

Download the starter project here — it contains a simple interface for adding and removing items from a table view. Each item in the table view represents a single iBeacon emitter, which in the real world translates to an item that you don’t want to leave behind.

Build and run the app; you’ll see an empty list, devoid of items. Press the + button at the top right to add a new item as shown in the screenshot below:

To add an item, you simply enter a name for the item and the values corresponding to its iBeacon. You can find your iBeacon’s UUID by reviewing your iBeacon’s documentation – try adding it now, or use some placeholder values, as shown below:

Press Save to return to the list of items; you’ll see your item with a location of Unknown, as shown below:

You can add more items if you wish, or swipe to delete existing ones. UserDefaults persists the items in the list so that they’re available when the user re-launches the app.

On the surface it appears there’s not much going on; most of the fun stuff is under the hood. The unique aspect in this app is the Item model class which represents the items in the list.

Open Item.swift and have a look at it in Xcode. The model class mirrors what the interface requests from the user, and it conforms to NSCoding so that it can be serialized and deserialized to disk for persistence.

Now take a look at AddItemViewController.swift. This is the controller for adding a new item. It’s a simple UIViewController, except that it does some validation on user input to ensure that the user enters valid names and UUIDs.

The Add button at the bottom left becomes tappable as soon as txtName and txtUUID are both valid.

Now that you’re acquainted with the starter project, you can move on to implementing the iBeacon bits into your project!

Core Location Permissions

Your device won’t listen for your iBeacon automatically — you have to tell it to do this first. The CLBeaconRegion class represents an iBeacon; the CL class prefix indicates that it is part of the Core Location framework.

It may seem strange for an iBeacon to be related to Core Location since it’s a Bluetooth device, but consider that iBeacons provide micro-location awareness while GPS provides macro-location awareness. You would leverage the Core Bluetooth framework for iBeacons when programming an iOS device to act as an iBeacon, but when monitoring for iBeacons you only need to work with Core Location.

Your first order of business is to adapt the Item model for CLBeaconRegion.

Open Item.swift and add the following import to the top of the file:

import CoreLocation

Next, change the majorValue and minorValue definitions as well as the initializer as follows:

let majorValue: CLBeaconMajorValue
let minorValue: CLBeaconMinorValue

init(name: String, icon: Int, uuid: UUID, majorValue: Int, minorValue: Int) {
  self.name = name
  self.icon = icon
  self.uuid = uuid
  self.majorValue = CLBeaconMajorValue(majorValue)
  self.minorValue = CLBeaconMinorValue(minorValue)
}

CLBeaconMajorValue and CLBeaconMinorValue are both a typealias for UInt16, and are used for representing major and minor values in the CoreLocation framework.

Although the underlying data type is the same, this improves readability of the model and adds type safety so you don’t mix up major and minor values.

Open ItemsViewController.swift, add the Core Location import to the top of the file:

import CoreLocation

Add the following property to the ItemsViewController class:

let locationManager = CLLocationManager()

You’ll use this CLLocationManager instance as your entry point into Core Location.

Next, replace viewDidLoad() with the following:

override func viewDidLoad() {
  super.viewDidLoad()

  locationManager.requestAlwaysAuthorization()

  loadItems()
}

The call to requestAlwaysAuthorization() will prompt the user for access to location services if they haven’t granted it already. Always and When in Use are variants on location permissions. When the user grants Always authorization to the app, the app can start any of the available location services while it is running in the foreground or background.

The point of this app is to monitor for iBeacon regions at all times, so you’ll need the Always location permissions scope for triggering region events while the app is both in the foreground and background.

iOS requires that you set up a string value in Info.plist that will be displayed to the user when access to their location is required by the app. If you don’t set this up, location services won’t work at all — you don’t even get a warning!

Open Info.plist and add a new entry by clicking on the + that appears when you select the Information Property List row.

Fortunately, the key you need to add is in the pre-defined list shown in the dropdown list of keys — just scroll down to the Privacy section. Select the key Privacy – Location Always Usage Description and make sure the Type is set to String. Then add the phrase you want to show to the user to tell them why you need location services on, for example: “ForgetMeNot needs to know where you are”.

Build and run your app; once running, you should be shown a message asking you to allow the app access to your location:

Select ‘Allow’, and the app will be able to track your iBeacons.

Listening for Your iBeacon

Now that your app has the location permissions it needs, it’s time to find those beacons! Add the following class extension to the bottom of ItemsViewController.swift :

// MARK: - CLLocationManagerDelegate

extension ItemsViewController: CLLocationManagerDelegate {
}

This will declare ItemsViewController as conforming to CLLocationManagerDelegate. You’ll add the delegate methods inside this extension to keep them nicely grouped together.

Next, add the following line inside of viewDidLoad():

locationManager.delegate = self

This sets the CLLocationManager delegate to self so you’ll receive delegate callbacks.

Now that your location manager is set up, you can instruct your app to begin monitoring for specific regions using CLBeaconRegion. When you register a region to be monitored, those regions persist between launches of your application. This will be important later when you respond to the boundary of a region being crossed while your application is not running.

Your iBeacon items in the list are represented by the the Item model via the items array property. CLLocationManager, however, expects you to provide a CLBeaconRegion instance in order to begin monitoring a region.

In Item.swift create the following helper method on Item:

func asBeaconRegion() -> CLBeaconRegion {
  return CLBeaconRegion(proximityUUID: uuid,
                                major: majorValue,
                                minor: minorValue,
                           identifier: name)
}

This returns a new CLBeaconRegion instance derived from the current Item.

You can see that the classes are similar in structure to each other, so creating an instance of CLBeaconRegion is very straightforward since it has direct analogs to the UUID, major value, and minor value.

Now you need a method to begin monitoring a given item. Open ItemsViewController.swift and add the following method to ItemsViewController:

func startMonitoringItem(_ item: Item) {
  let beaconRegion = item.asBeaconRegion()
  locationManager.startMonitoring(for: beaconRegion)
  locationManager.startRangingBeacons(in: beaconRegion)
}

This method takes an Item instance and creates a CLBeaconRegion using the method you defined earlier. It then tells the location manager to start monitoring the given region, and to start ranging iBeacons within that region.

Ranging is the process of discovering iBeacons within the given region and determining their distance. An iOS device receiving an iBeacon transmission can approximate the distance from the iBeacon. The distance (between transmitting iBeacon and receiving device) is categorized into 3 distinct ranges:

  • Immediate Within a few centimeters
  • Near Within a couple of meters
  • Far Greater than 10 meters away
Note: The real distances for Far, Near, and Immediate are not specifically documented, but this Stack Overflow Question gives a rough overview of the distances you can expect.

By default, monitoring notifies you when the region is entered or exited regardless of whether your app is running. Ranging, on the other hand, monitors the proximity of the region only while your app is running.

You’ll also need a way to stop monitoring an item’s region after it’s deleted. Add the following method to ItemsViewController:

func stopMonitoringItem(_ item: Item) {
  let beaconRegion = item.asBeaconRegion()
  locationManager.stopMonitoring(for: beaconRegion)
  locationManager.stopRangingBeacons(in: beaconRegion)
}

The above method reverses the effects of startMonitoringItem(_:) and instructs the CLLocationManagerto stop monitor and ranging activities.

Now that you have the start and stop methods, it’s time to put them to use! The natural place to start monitoring is when a user adds a new item to the list.

Have a look at addBeacon(_:) in ItemsViewController.swift. This protocol method is called when the user hits the Add button in AddItemViewController and creates a new Item to monitor. Find the call to persistItems() in that method and add the following line just before it:

startMonitoringItem(item)

That will activate monitoring when the user saves an item. Likewise, when the app launches, the app loads persisted items from UserDefaults, which means you have to start monitoring for them on startup too.

In ItemsViewController.swift, find loadItems() and add the following line inside the for loop at the end:

startMonitoringItem(item)

This will ensure each item is being monitored.

Now you need to take care of removing items from the list. Find tableView(_:commit:forRowAt:) and add the following line inside the if statement:

stopMonitoringItem(items[indexPath.row])

This table view delegate method is called when the user deletes the row. The existing code handles removing it from the model and the view, and the line of code you just added will also stop the monitoring of the item.

At this point you’ve made a lot of progress! Your application now starts and stops listening for specific iBeacons as appropriate.

You can build and run your app at this point; but even though your registered iBeacons might be within range your app has no idea how to react when it finds one…time to fix that!

Acting on Found iBeacons

Now that your location manager is listening for iBeacons, it’s time to react to them by implementing some of the CLLocationManagerDelegate methods.

First and foremost is to add some error handling, since you’re dealing with very specific hardware features of the device and you want to know if the monitoring or ranging fails for any reason.

Add the following two methods to the CLLocationManagerDelegate class extension you defined earlier at the bottom of ItemsViewController.swift:

func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
  print("Failed monitoring region: \(error.localizedDescription)")
}
  
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
  print("Location manager failed: \(error.localizedDescription)")
}

These methods will simply log any received errors as a result of monitoring iBeacons.

If everything goes smoothly in your app you should never see any output from these methods. However, it’s possible that the log messages could provide very valuable information if something isn’t working.

The next step is to display the perceived proximity of your registered iBeacons in real-time. Add the following stubbed-out method to the CLLocationManagerDelegate class extension:

func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) {
    
  // Find the same beacons in the table.
  var indexPaths = [IndexPath]()
  for beacon in beacons {
    for row in 0..<items.count {
        // TODO: Determine if item is equal to ranged beacon
    }
  }
    
  // Update beacon locations of visible rows.
  if let visibleRows = tableView.indexPathsForVisibleRows {
    let rowsToUpdate = visibleRows.filter { indexPaths.contains($0) }
    for row in rowsToUpdate {
      let cell = tableView.cellForRow(at: row) as! ItemCell
      cell.refreshLocation()
    }
  }
}

This delegate method is called when iBeacons come within range, move out of range, or when the range of an iBeacon changes.

The goal of your app is to use the array of ranged iBeacons supplied by the delegate methods to update the list of items and display their perceived proximity. You’ll start by iterating over the beacons array, and then iterating over items to see if there are matches between in-range iBeacons and the ones in your list. Then the bottom portion updates the location string for visible cells. You’ll come back to the TODO section in just a moment.

Open Item.swift and add the following property to the Item class:

var beacon: CLBeacon?

This property stores the last CLBeacon instance seen for this specific item, which is used to display the proximity information.

Now add the following equality operator at the bottom of the file, outside the class definition:

func ==(item: Item, beacon: CLBeacon) -> Bool {
  return ((beacon.proximityUUID.uuidString == item.uuid.uuidString)
        && (Int(beacon.major) == Int(item.majorValue))
        && (Int(beacon.minor) == Int(item.minorValue)))
}

This equality function compares a CLBeacon instance with an Item instance to see if they are equal — that is, if all of their identifiers match. In this case, a CLBeacon is equal to an Item if the UUID, major, and minor values are all equal.

Now you’ll need to complete the ranging delegate method with a call to the above helper method. Open ItemsViewController.swift and return to locationManager(_:didRangeBeacons:inRegion:). Replace the TODO comment in the innermost for loop with the following:

if items[row] == beacon {
  items[row].beacon = beacon
  indexPaths += [IndexPath(row: row, section: 0)]
}

Here, you set the cell’s beacon when you find a matching item and iBeacon. Checking that the item and beacon match is easy thanks to your equality operator!

Each CLBeacon instance has a proximity property which is an enum with values of far, near, immediate, and unknown.

Add the following method to Item:

func nameForProximity(_ proximity: CLProximity) -> String {
  switch proximity {
  case .unknown:
    return "Unknown"
  case .immediate:
    return "Immediate"
  case .near:
    return "Near"
  case .far:
    return "Far"
  }
}

This returns a human-readable proximity value from proximity which you’ll use next.

Still in Item, add the following method:

func locationString() -> String {
  guard let beacon = beacon else { return "Location: Unknown" }
  let proximity = nameForProximity(beacon.proximity)
  let accuracy = String(format: "%.2f", beacon.accuracy)
    
  var location = "Location: \(proximity)"
  if beacon.proximity != .unknown {
    location += " (approx. \(accuracy)m)"
  }
    
  return location
}

This generates a nice, neat string describing not only the proximity range of the beacon, but also the approximate distance.

Now it’s time to use that new method to display the perceived proximity of the ranged iBeacon.

Open ItemCell.swift and add the following to just below the lblName.text = item.name line of code:

lblLocation.text = item.locationString()

This displays the location for each cell’s beacon. And to ensure it shows updated info, add the following inside refreshLocation():

lblLocation.text = item?.locationString() ?? ""

refreshLocation() is called each time the locationManager ranges the beacon, which sets the cell’s lblLocation.text property with the perceived proximity value and approximate ‘accuracy’ taken from the CLBeacon.

This latter value may fluctuate due to RF interference even when your device and iBeacon are not moving, so don’t rely on it for a precise location for the beacon.

Now ensure your iBeacon is registered and move your device closer or away from your device. You’ll see the label update as you move around, as shown below:

You may find that the perceived proximity and accuracy is drastically affected by the physical location of your iBeacon; if it is placed inside of something like a box or a bag, the signal may be blocked as the iBeacon is a very low-power device and the signal may easily become attenuated.

Keep this in mind when designing your application — and when deciding the best placement for your iBeacon hardware.

Advertisements
How To Make an iPhone Transmit an iBeacon Emitter And Working With iBeacons

Saving and Loading Objects From Disk Memory

Saving and Loading List Items

Even though in the previous part we built the basic functionality of the demo app, we still must add two more important features so it works as expected. This means that we must make it capable of saving the shopping list permanently to the disk, and also load the list from the disk (if exists) when the application gets launched.

As the structure of our data is a mutable array, we can easily write and read to and from the disk, using the methods that the NSMutableArray class provides. Obviously, for doing both of the above, we are going to create two separate functions. So, let’s get started with the next one, which is responsible for saving the contents of the array to a file. The file is named shopping_list:

The first two lines consist of a standard piece of code that returns the path to the documents directory of the app. Once we know that, we form the path to the file, specifying the file name at the same time. Lastly, the key to the above function is the writeToFile(_:atomically:) method of the NSMutableArray class. This is the one that actually saves the array contents to the disk.

By having the above method implemented, we can go ahead and call it where it’s needed. If you think of what we did in the previous part, you can assume that we are going to call it in two places: When a new item is added to the list, and when an existing item is removed from the list.

At first, go to the textFieldShouldReturn(textField:) delegate method of the textfield. Just right before the return command, make a call to the above method:

Nice, each new item now will be saved to the disk too. Also, let’s pay a visit to the removeItemAtIndex(index:) function too, where we actually delete an item. As we just did, let’s make a call to the save function and let’s add it as the last command as well:

With all the above we can be sure that any changes that might take place in our data, they will become permanent too.

Now, we can proceed by implementing the exact opposite functionality, the data loading. At first, let’s see the definition of the respective function:

It’s important to highlight the fact that we must always check if a file exists before we load its contents. In iOS, we do that by using the NSFileManager class exactly as shown above. If the file doesn’t exists and the condition returns false, nothing will happen at all. On the other hand, if the file exists, we initialise the shoppingList array with the contents of the file, and then we reload the tableview so it displays the loaded data.

As a last step, we must call this function. As we want the data to be loaded right after the app has been launched, we’ll do that in the viewDidLoad method:

The two functionalities we added to the application in this part, will become really helpful later, when we’ll handle the notification actions without running the app on the foreground.

 

Saving and Loading Objects From Disk Memory

Creating Interactive Notifications in iOS 8

Creating Notification Actions

Several times up to this point I talked about notification actions, but always generally speaking. Now, it’s time to see what they really are.

An action is an object of the UIMutableUserNotificationAction class. This class is new in iOS 8, and provides various useful properties so an action can be properly configured. These are:

  • identifier: This is a string value, that uniquely identifies an action among all in an application. Obviously, you should never define two or more actions with the same identifier. Also, using this property we’ll be able to determine the chosen action by the user upon the notification appearance. We’ll see that later.
  • title: The title is the text that is shown on the action button to the user. This can be either a simple, or a localized string. Be cautious and always set proper titles to your actions, so the user can instantly understand by reading the 1-2 title words what is going to happen by selecting it.
  • destructive: This is a bool value. When it is set to true the respective button in the notification has red background colour. Note that this happens in the banner mode only. Usually, actions regarding deletion, removal and anything else critical are marked as destructive, so they increase the user’s attention.
  • authenticationRequired: This property is a bool value also. When it becomes true, the user must necessarily authenticate himself to the device before the action is performed. It’s extremely useful in cases where the action is critical enough, and any unauthorised access can damage the application’s data.
  • activationMode: This is an enum property, and defines whether the app should run in the foreground or in the background when the action is performed. The possible values specifying each mode are two: (a) UIUserNotificationActivationModeForeground, and (b) UIUserNotificationActivationModeBackground. In background, the app is given just a few seconds to perform the action.

While I was describing this demo application, I said that we’re going to create three distinct actions:

  1. An action to just make the notification go away, without anything else to be performed.
  2. An action to edit the list (actually to add a new item).
  3. An action to delete the entire list.

Let’s see how each one is written in code. For every action, we will set the values to all the aforementioned properties. The first one:

The identifier of this action is “justInform”. As you see, this action will be performed in the background, and as there’s nothing dangerous with it we set to false the destructive and the authenticationRequired properties.

The next one:

Apparently, this action requires the app to run in the foreground so we can edit the shopping list. Also, we don’t want nobody else to mess with our list, so we set the authenticationRequired to true.

And the third and last one:

With this action we’ll allow the user to delete the entire shopping list without launching the application in the foreground. However, this is a dangerous action for the data, therefore we tell the app that it’s destructive and that authentication is required to proceed.

By looking at the above actions setup, you understand that configuring them is an easy task.

Now that we have done all the above, let’s add them to the setupNotificationSettings function we started to implement in the previous part:

Action Categories

When the actions of a notification have been defined, they can then be grouped together in categories. Actually, categories are what is written in the settings, and not the actions themselves, so you should always create them, unless of course you present a notification without actions. Usually, a category matches to a notification, so supposing that all notifications in an application support actions, there should be as many categories as the notifications.

In this demo application, we are going to create just one notification, so we are going to have one category only. Programmatically speaking now, a category is an object of the UIMutableUserNotificationCategory class, which is also new in iOS 8. This class has just one property and one method. The property is a string identifier that uniquely identifies a category (similarly to actions), and the method is used to group the actions together.

Let’s see a few more things about that method, and let’s start by its signature (taken by the Apple documentation directly):

The first parameter regards the actions that should be grouped for the category. It’s an array containing all the actions, and the order of the of the action objects in the array specifies the order they will appear in the notification.

The second parameter is quite important. The context is an enum type, and describes the context of the alert that the notification will appear into. There are two possible values:

  1. UIUserNotificationActionContextDefault: It matches to a full alert that is appeared to the centre of the screen (when the device is unlocked).
  2. UIUserNotificationActionContextMinimal: It matches to a banner alert.

In the default context, the category can accept up to four actions, which will be displayed in the predefined order in the notification alert (at the screen centre). In the minimal context, up to two actions can be set to appear in the banner alert. Notice that in the second case, you must choose what the most important actions are among all so they appear in the banner notification. In our implementation we’ll specify the actions for both contexts.

As I said, the first parameter in the above method must be an array. For this reason, our initial step is to create two arrays with the actions for each context. Let’s go back to our function:

Next, let’s create a new category for which we will set an identifier, and then we will provide the above arrays for the two contexts:

And… that’s all it takes to create a category regarding the actions of a notification.

Registering Notification Settings

In the last three parts we configured all the new features of the local notification, and now we have only left to write everything to settings. For this purpose, we will use the UIUserNotificationSettings class (new in iOS 8), and through the following init method we’ll provide the types and the category of the notification:

The first parameter is the types we defined for the notification. The second parameter is a NSSet object, in which all the categories for all the existing notifications in an application must be set. In this example, we have just one category, however we’ll create a NSSet object no matter what.

Let’s continue the function implementation with that:

Now, we can create a new object of the UIUserNotificationSettings class and pass the required arguments:

Lastly, let’s write (register) the settings in the Settings app using the next line:

The first time the above code will work, it will create a new record for our application in the Settings app.

Now, before I present you the whole setupNotificationSettings() after all the previous additions, let me say something more. This function will be called in the viewDidLoad method, and that means that its content will be executed every time the app runs. However, as the notification settings are not going to change and therefore it’s pointless to set them again and again, it would be wise to contain all the above code in an if statement. In the condition of this statement, we will check if the notification types have been specified or not, where in the second case the if body will be executed. This is translated to code as shown next:

At first, we get the existing notification settings using the currentUserNotificationSettings() method of the UIApplication class. This method returns a settings object, through which we can check the current value of the types property. Remember that this property is an enum type. If its value equals to None, meaning that no notification types have been set, then we’ll allow it to register the notification settings by doing all the above in the previous parts, otherwise will do nothing.

With the above condition we avoid unneeded settings registration. Note though that if you want to modify any settings or add more actions, categories, etc for new notifications, you should comment out the if opening and closing lines and run the app once, so the new additions to be applied and so you can test it with the new settings. Then, remove the comments to revert your code to its previous state.

With that, all the work in the notification details is over. Right next you can see the setupNotificationSettings() function fully implemented and in one part:

Don’t forget to call this function in the viewDidLoad body:

Handling Notification Actions

There is one last aspect of the local notification that we haven’t worked with yet, and that is to handle the actions that get received by the application when the user taps on the respective buttons. As usual, there are certain delegate methods that should be implemented.

Before we see what actions we’ll handle and how, let me introduce you a couple of other delegate methods that can become handy when developing your own applications. Note that in this demo we won’t really use them; we’ll just print a message to the console. Also, make sure to open the AppDelegate.swift file now.

So, the first one regards the notification settings. This delegate method is called when the app is launched (either normally or due to a local notification’s action) and contains all the settings configured for the notifications of your app. Right next you can see its definition. What we only do, is to print the current notification types:

Regardless of the above simple implementation, you can use it to access all kind of settings supported by the UIUserNotificationSettings class. This method can become useful in cases you need to check the existing settings and act depending on the values that will come up. Don’t forget that users can change these settings through the Settings app, so do never be confident that the initial configuration made in code is always valid.

When scheduling a local notification, this will be fired no matter whether your application is currently running or not. Usually, developers imagine how the notifications are going to appear when the app is in the background or suspended, and all the implementation is focused on that. However, it’s your duty to handle a notification in case it will be fired when the app is running. Thankfully, iOS SKD makes things pretty straightforward, as there’s another delegate method that is called when the app is in the foreground:

There will be cases where you won’t need to handle the notification if your app is running. However, in the opposite scenario, the above method is the place to deal with it and perform the proper actions.

Now let’s focus on the delegate method that is called when an action button is tapped by the user. Based on the identifier values we set to the actions, we’ll determine which one has been performed, and then we will make the app execute the proper part of code. We configured three actions in total:

  1. One to make the notification simply go away (identifier: justInform).
  2. One to add a new item to the list (identifier: editList).
  3. One to delete entire the list (identifier: trashAction).

From all the above, we don’t need to do anything when the first action is performed. However, we’ll handle the other two. Depending on the identifier value, we will post a notification (NSNotification) for each one, and then in the ViewController class we’ll observe for those notifications, and of course, we’ll handle them.

Let’s get started with the new delegate method:

In both cases we post a notification (a NSNotification), specifying a different name each time. Notice that at the end we call the completion handler, and that’s mandatory to do, so the system knows we handled the received action. This is the most important delegate method when working with local notifications, as this is the place where you’ll route your code depending on the action the user performed.

Now, let’s go back to the ViewController.swift file. There, let’s make that class observe for the notifications we set before. In the viewDidLoad method add the next lines:

The modifyListNotification() and deleteListNotification() are both custom functions that we’ll implement right next.

Let’s begin with the first one. What we want is to make the textfield the first responder when the app is launched due to the edit list action, so we’re just talking about one line of code only:

With this, the keyboard will automatically be shown and the user will be able to type a new item immediately.

For the delete list action, we want to remove all existing objects from the array that contains them. So, we’ll do exactly that, and then we’ll save the (empty) array back to file. Lastly, we’ll reload the tableview data, so when the user launches the app will find no items at all:

With the above implemented, our demo application is finally ready!

Creating Interactive Notifications in iOS 8