Push Notifications Tutorial for iOS: Rich Push Notifications

Learn how to modify and enhance push notifications before they are presented to the user, how to create custom UI around your push content, and more! By Mark Struzinski.

4.6 (14) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Creating a Custom UI

You can take rich notifications a step further by adding a custom UI on the top of your push content. This interface will replace the standard push notification UI by way of an app extension.

Content Extension interaction

This interface is a view controller that conforms to UNNotificationContentExtension. By implementing didReceive(_:), you can intercept the notification and set up your custom interface.

Adding the Target

Like before, click on FileNewTarget…, and filter for the Notification Content Extension:

Adding a notification content extension

Name the content extension WendercastNotificationContent and ensure the fields are proper (using your own team and organization name):

Configuring a notification content extension

This time, click Activate on the schema activation confirmation screen.

Configuring Info.plist

Next, you need to configure the Info.plist of the new target to display your content extension.

  1. In the WendercastNotificationContent group, open Info.plist.
  2. Expand NSExtension dictionary.
  3. Expand the NSExtensionAttribute dictionary.
  4. Update the value in UNNotificationExtensionCategory to new_podcast_available.
  5. Click the + to add a new key-value pair to the NSExtensionAttribute dictionary.
  6. Add the key UNNotificationExtensionDefaultContentHidden as a Boolean, and set the value to YES.
  7. Add one more Boolean key-value pair. Set the key to UNNotificationExtensionUserInteractionEnabled and the value to YES.

Your final Info.plist should look like this (the order of the keys doesn’t matter):

Final info plist setup

Here’s what these parameters are for:

UNNotificationExtensionCategory

  • The value of this entry must match a value in the incoming push content. iOS needs this to determine which UI to use for displaying the notification.
  • You need it because you may want to provide custom UIs for different categories of push notifications.
  • If this value is missing, iOS will not invoke your extension.

UNNotificationExtensionInitialContentSizeRatio

  • This value is a number between 0 and 1. It represents the aspect ratio of your custom interface.
  • The default of 1 tells iOS that your initial interface height is the same as its width.
  • For example, if you set this value to 0.5, then this would tell iOS that the height of your interface is half the size as its width.
  • This is an estimate and allows iOS to set the initial size of your interface, preventing unnecessary resizing.

UNNotificationExtensionDefaultContentHidden

  • When set to YES, the standard title, subtitle and body of the push content are not visible.
  • When set to NO, the standard push content displays beneath the custom UI.

UNNotificationExtensionUserInteractionEnabled

  • When set to YES, it enables user interaction with UIKit elements.

Adding the App Group

Add the same app group you created for the main app target:

  1. In the File navigator, click the project node.
  2. Select the WendercastNotificationContent target.
  3. Select the Signing & Capabilities tab.
  4. Click + Capability.
  5. Select App Groups.
  6. Select the same app group ID you created at the beginning of this tutorial.

Building the Custom UI

You probably want to focus on building the content extension logic, not waste time on tedious Interface Builder shenanigans. To give you the assist, the download content for this tutorial already contains a ready-to-be-used storyboard. Feel free to use it to replace the storyboard automatically created by Xcode.

Here’s how to do that:

  1. In Xcode, delete MainInterface.storyboard from the WendercastNotificationContent group. Choose Move to Trash when prompted.
  2. In the download materials, drag the MainInterface.storyboard file from the ContentStoryboard folder into Xcode in the WendercastNotificationContent folder.
  3. Check the Copy items if needed box and select the WendercastNotificationContent target in the Add to targets list.
  4. Click Finish.
  5. Open the storyboard in Xcode.

You’ll see this storyboard provides a good starting point for the notification UI. It provides UI elements for a title, podcast image, favorites button and play button. As a bonus, the auto layout constraints are already set up.

You can now focus on building the view controller.

Setting up NotificationViewController

NotificationViewController is responsible for presenting the custom notification view for your users. You’ll make the modifications necessary to present your awesome new push notification view :].

Adding Shared Files

Open the following files and add WendercastNotificationContent to their target membership in the File inspector:

  • CoreDataManager.swift
  • PodcastItem.swift
  • Podcast.swift
  • DiskCacheManager.swift
  • Wendercast.xcdatamodel

You do this by checking the WendercastNotificationContent box.

Setting up target membership

This will make the data model and networking classes available to the content extension.

Note: Xcode may be confused at this point, showing a number of errors. You can clear them by simply pressing Command-B to build the target.

Customizing the UI

Look under the class declaration in NotificationViewController. Remove this code automatically generated by Xcode:

  • label
  • viewDidLoad()
  • didReceive(_:)‘s body

Then add the following outlets right under the class declaration:

@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var favoriteButton: UIButton!
@IBOutlet weak var podcastTitleLabel: UILabel!
@IBOutlet weak var podcastBodyLabel: UILabel!

And then add a property to hold the current podcast:

var podcast: Podcast?

Lastly, add the following convenience method to load a podcast from the shared data store:

private func loadPodcast(from notification: UNNotification) {
  // 1
  let link = notification.request.content.userInfo["podcast-link"] as? String

  // 2
  guard let podcastLink = link else {
    return
  }

  // 3
  let podcast = CoreDataManager.shared.fetchPodcast(
    byLinkIdentifier: podcastLink)

  // 4
  self.podcast = podcast
}

Here’s what you’re doing above:

  1. Try to get the link to the podcast from the userInfo object attached to the notification. The podcast link is the podcast’s unique identifier in the Core Data store.
  2. If the link does not exist, return early.
  3. Use the link to fetch a Podcast model object from the Core Data store.
  4. Set podcast with a response.

Customizing the Notification

Replace the body of didReceive(_:) with the following:

// 1
loadPodcast(from: notification)

// 2
let content = notification.request.content
podcastTitleLabel.text = content.subtitle
podcastBodyLabel.text = content.body

// 3
guard 
  let attachment = content.attachments.first,
  attachment.url.startAccessingSecurityScopedResource() 
  else {
    return
}

// 4
let fileURLString = attachment.url

guard 
  let imageData = try? Data(contentsOf: fileURLString),
  let image = UIImage(data: imageData) 
  else {
    attachment.url.stopAccessingSecurityScopedResource()
    return
}

// 5
imageView.image = image
attachment.url.stopAccessingSecurityScopedResource()

Once a push notification comes in, here’s what you are doing above:

  1. Call the convenience method to load the podcast from the Core Data store. This sets podcast for use later.
  2. Set the title and body labels to the values received from the push notification.
  3. Attempt to access the media attached to the service extension. If not, return early. The call to startAccessingSecurityScopedResource() allows you to access the attachment.
  4. Get the URL for the attachment. Attempt to retrieve it from disk and convert the data to an image. If it fails, return early.
  5. If the image retrieval is successful, set the podcast image and stop accessing the resource.