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

Manage Keyboard And TextField iOS

SOURCE

// Created by Kapil Ahuja on 6/16/17.
// Copyright © 2017 Kapil Ahuja. All rights reserved.
//

import UIKit

/* Class Contain all Keyboard management related methods */

final class KeyboardManager: NSObject {

//MARK: – Instance
static let shared = KeyboardManager()
lazy var textFieldDetails : (textField : UITextField, maxY : CGFloat, isAnimated : Bool, viewY : CGFloat)! = (UITextField(), 0, false, 0)

/* Register For KeyBoard & TextField Notification */
func RegisterForNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(textFieldViewDidBeginEditing), name: Notification.Name.UITextFieldTextDidBeginEditing, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: Notification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: Notification.Name.UIKeyboardWillHide, object: nil)
}

/* UnRegister For KeyBoard & TextField Notification */
func UnRegisterForNotifications() {
NotificationCenter.default.removeObserver(self, name: Notification.Name.UITextFieldTextDidBeginEditing, object: nil)
NotificationCenter.default.removeObserver(self, name: Notification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: Notification.Name.UIKeyboardWillHide, object: nil)
}

/* UITextFieldTextDidBeginEditing Notification */
func textFieldViewDidBeginEditing(_ notification:Notification) {

// Getting object & Saving in Tuple
if let textField = notification.object as? UITextField {
let tectFieldPoints = textField.convert((textField.frame.origin), to: UIScreen.main.coordinateSpace)
textFieldDetails = (textField, tectFieldPoints.y + textField.frame.size.height, false, 0)
}

}

/* UIKeyboardWillShow Notification */
func keyboardWillShow(_ notification:Notification) {

if let info = notification.userInfo {

// Getting UIKeyboardSize.
if let kbFrame = (info[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {

let screenSize = UIScreen.main.bounds

//Calculating actual keyboard displayed size, keyboard frame may be different when hardware keyboard is attached

//Check if Text Field is Active
guard textFieldDetails != nil else {
return
}

//Get Size and Check for Animation
let intersectRect = kbFrame.intersection(screenSize)
if intersectRect.origin.y < textFieldDetails.maxY {

if let controller = textFieldDetails.textField.parentViewController {

//Set Values of Animation
self.textFieldDetails.viewY = controller.view.frame.origin.y
self.textFieldDetails.isAnimated = true

//Animate Window
UIView.animate(withDuration: 0.5, animations: {
controller.view.frame = CGRect(x: controller.view.frame.origin.x, y: intersectRect.origin.y – self.textFieldDetails.maxY, width: controller.view.frame.size.width, height: controller.view.frame.size.height)

}, completion: nil)
}
}

}
}
}

/* UIKeyboardWillHide Notification */
func keyboardWillHide(_ notification:Notification) {

//Check if animated text field
if textFieldDetails.isAnimated {

if let controller = textFieldDetails.textField.parentViewController {

//Animate Window to reset
UIView.animate(withDuration: 0.5, animations: {
controller.view.frame = CGRect(x: controller.view.frame.origin.x, y: self.textFieldDetails.viewY, width: controller.view.frame.size.width, height: controller.view.frame.size.height)
}, completion: nil)
}
}
}
}

//MARK: – UIView Extension For Parent View Controller
extension UIView {

//Fetch Parent View Controller From UIView
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}

IMAGES

USE

/* Register For Notification */
KeyboardManager.shared.RegisterForNotifications()

Manage Keyboard And TextField iOS