-
Notifications
You must be signed in to change notification settings - Fork 10
Lesson 3.6: Local Notifications
- How to use local notifications to inform or remind users of new or updated information
- How to schedule and configure local notifications
- How to create actionable notifications with custom actions
- How to handle received notifications in the background and foreground
In this lab, you'll independently schedule and display local notifications with custom actions. You'll do this by extending the BillManager project starter.
Currently, BillManager gives users the ability to track the bills they owe and mark them as paid or unpaid. Your task is to add local notifications to remind users of payments due.
Before you begin, take a look at the app as written. You'll see that it has two view controllers: a master view of a list of bills and a detail view for an individual bill. You'll also notice a model object for Bill
, along with an extension for Bill
with computed properties for hasReminder
, isPaid
, and formattedDueDate
.
-
Think about the different methods you might need to add to the extension on
Bill
to create reminders, remove reminders, and request authorization to display notifications. Add method signatures toBill
for these methods. -
The exact structure of your methods is up to you. However, at a minimum, you should have method signatures for the following:
- A method that removes reminders. This method doesn't need any parameters.
- A mutating method that schedules reminders. It should take a parameter of type
Date
, representing the date on which the reminder is set, and an escaping completion closure that passes an updatedBill
instance. - A private method that checks whether the app has authorization to display notifications. It should take an escaping completion closure that takes a Boolean parameter and has no return.
-
Implement your permission-checking method. If the app hasn't yet requested authorization, request authorization in this method. Call
completion
at the end of all possible code paths, passing in the appropriate Boolean value to indicate whether the app has permission to schedule user notifications. -
Add a static constant to
Bill
namednotificationCategoryID
and set it to a string you'll use for the notification category identifier. -
Set up notification actions in the
AppDelegate
methodapplication(_:didFinishLaunchingWithOptions:)
. One action reminds the user again in an hour, and the other allows the user to mark the bill as paid. The action to mark a bill as paid requires the.authenticationRequired
option when initialized. This option requires that a user's device be unlocked for the action to be completed. This ensures that the bill can't be marked as paid by someone other than the device owner. -
In the same spot, create a category with the two actions and register it with the user notification center.
-
Set the user notification center's delegate.
In the extension for Bill
, implement the method for removing reminders. This method safely unwraps notificationID
and removes any pending notifications with that identifier from the user notification center. Set notificationID
and remindDate
to nil
.
Because each bill can have its own reminder, you need to track which notifications have been created and to which bill they belong. Add a property named notificationID
of type String?
to Bill
.
- Implement your method on
Bill
for scheduling reminders. Because an escaping closure can't be used in a mutating method if it refers toself
, you'll need to make a copy ofself
(e.g.,var updatedBill = self
), modify it as necessary, and always return the copy in the completion closure.
The first thing this method should do is remove any previous notifications scheduled for that bill by calling your method to remove reminders. This ensures that users don't get reminders that they thought had been changed.
Call your method that checks for authorization. In the body of the completion closure, check whether permission to create notifications has been granted. Handle lack of permissions by calling the completion closure immediately, passing the copied instance.
Make the title “Bill Reminder.” The body should be a string that displays the amount due, the payee, and the bill's due date. The category identifier is the ID you used when you registered the category.
Here's an example body string: $12.00 due to Alexis Key on 4/10/21
- After you've created the content, do the following:
- Create a notification trigger with the date passed into the method as a parameter.
- Create a new notification identifier
(UUID().uuidString)
and assign it tonotificationID
. - Create a new notification request with the content, trigger, and identifier.
- Add the notification request to the user notification center. If authorization has been given, modify the copy of the bill by updating its notification ID and reminder date. Finish by calling the completion closure, passing the copied instance.
Remember, you should make all calls to the completion handler on the main thread.
For simplicity, call the set reminder function only when the user taps the Done button. In the detail view controller, follow these steps to call the set reminder function you just wrote:
-
Find the
prepare(for:sender:)
function. Within that function, find theif-else
statement that checks the conditionremindSwitch.isOn
. Currently, both cases set theremindDate
property of the newBill
instance—to the selected date if the switch is on or tonil
if the switch has been turned off. Modify these cases to also call your methods for setting a reminder and removing a reminder, respectively. The method to set a reminder takes a completion closure. For now, pass an empty closure. -
If the method to schedule a reminder fails because of lack of authorization, the
notificationID
property of the bill passed to the closure will benil
. If so, present an alert to users telling them that reminders are unavailable unless they grant notification permissions in iOS Settings. Create a new method namedpresentNeedAuthorizationAlert()
to present this alert. -
Refactor the method so that the database is updated with the new bill inside the completion closure of the call to schedule a reminder, as well as the case where the reminder switch is set to off.
- In
AppDelegate
, implement theuserNotificationCenter(_:didReceive:withCompletionHandler:)
delegate method. Get the notification identifier from the response object:
let notificationID = response.notification.request.identifier
-
Using this notification identifier, get the bill associated with that ID. You might find it helpful to write a
getBill(forNotificationID:)
method inDatabase
that returns a bill for a given notification ID. -
Determine which action the user selected using a
switch
orif-else
statement. -
If the user has chosen to be reminded later, schedule a new reminder for an hour from now by calling the bill's method for scheduling reminders. Remember to save the updated bill in the completion closure.
-
If the user has chosen to mark the bill as paid, set the paid date to now and save the new data.
-
Remember to call the completion handler of
userNotificationCenter(_:didReceive:withCompletionHandler:)
. -
Implement the
userNotificationCenter(_:willPresent:withCompletionHandler:)
to show the notification even when the app is in the foreground.
You should now be able to schedule a reminder notification for each of your bills. Try it by creating a new bill, setting the reminder, and waiting until the reminder goes off.
Nice work! You've taken a big step toward integrating your code with the operating system and taking advantage of the iOS features available to you.
Course curriculum resources from:
-
Develop in Swift Data Collections
- Apple Inc. - Education, 2020. Apple Books.