// MailChip Integration // Lottie Animations Integration
SkySpirit Labs

Believe in the power of innovation to shape the future with the technology.

Complete Guide of Rich Push Notifications With Video, Image, GIF and Many More

Local Notifications, Push Notifications. Location Triggers. Multimedia Messages Enriched with Image, Video, Gif or Full Custom UI. iOS15 Changes

Local, push notifications, multimedia messages, and iOS 15 changes
Rich Media with Video-mp4

Introduction

Almost every app needs some way to communicate with the users. One of the main goals for the marketing team is to reach more users with relevant information, engage with them, and help to understand the value of the application. One of their favourite choices is push notifications.

When I started to work with notifications after a couple of tutorials I thought I knew everything. And with every feature request, I have found something new. This article will cover a really wide range of knowledge based on my experience.

Before we go into the details, here is a textual-based game I worked on a long time ago that uses notifications heavily. It is supported on iPhone, but more fun can be had on Apple WatchLifeline: Whiteout

This article will cover several topics like:

  • Local notifications
  • Push notifications
  • Silent push messages
  • How to add action buttons
  • How to configure it in Xcode
  • How to register
  • Message types and states
  • iOS 15 changes
  • APNs
  • How to setup certificate
  • Rich media (multimedia messages with image, GIF, or video),
    Notification Service Extension
  • Rich media (messages with custom created content),
    Notification Content Extension
  • Example how to present MapKit
  • How to handle the badge count
  • How to test on simulator and on device
  • Useful tools
  • Good third-party push notification services
Notification Types — by Apple
Notification Types — by Apple

Subscribe for Notifications

Regardless of whether the local or push notification type is picked, the first step is to ask the user to allow them to receive the notifications. To register, to be able to receive notifications. Lots of time it is called from application (_:didFinishLaunchingWithOptions:), but a much more neat option is to call it from your custom screen to explain to the user why we will use the local or/and push notifications and its benefits.

OS will present Alert to ask user.
The OS will present an alert to ask the user

When we call requestAuthorization(options:) the OS will present the Alert view that can be seen on the previous image. With red is marked the application name. The green underlying is the requested options.

The image below shows one of the examples of how the custom screen explains the permissions. Must mention that when the OS presents the alert, the application will go to the background, and after the user answers the OS permission question, then it will come back to the application. It means it will trigger the AppDelegate / SceneDelegate lifecycle methods.

Example of Custom View what present OS Alert what ask for Permission
Example of Custom View that presents an OS alert asking for permission

The first step is to import UserNotifications.
Next is to create configuration that we would like to use, e.g., let options: UNAuthorizationOptions = [.alert, .sound, .badge].

Request permission for notifications

This alertsound and badge will cover most application needs but there are more parameters like for carPlaycriticalAlert or maybe provisional etc.

  • alert — To be able to get the notification pop up, alert
  • sound — To enable sound, the default is UNNotificationSound.default
  • badge — Enable the use of “red dot” with number on application icon
  • carPlay — To be able to display notification in CarPlay (Link to really nice tutorial of the use CarPlay from a friend)
  • criticalAlert— Critical alert goes over “Do Not Disturb.” Must be allowed by Apple’s special Entitlement
  • provisionalWith this option, you don’t need to ask the user for permission. You can send notifications right away. But just to the Notification Center

If you want to check what options are allowed in the application for the user you can do that using the following code:

How to check from app the Permissions

Provisional Messages

As mentioned, these are silent messages that make no attention and the user can receive them from the app installation. No need to ask for permission. You still need to call the method requestAuthorization(options:). But it will not prompt any questions for the user.

let options: UNAuthorizationOptions = [.provisional]
Provisional Push Message
Provisional Push Message

In the previous image, you can see how the user sees the message. The user can still configure it from Settings as the user wants.

Local Notifications

This kind of notification, as its name already tells us, is created locally, offline. Local notifications can be scheduled by time interval as a reminder, which will trigger them by exact time of a day as event or by geographical location, geofence.

Examples:

  • If you want to notify the user to stop playing World of Warcraft and to check on the meat that’s been cooking for 20 minutes or it will burn (Time Interval).
  • That today on Feb. 1st is the birthday of your best friend (Calendar).
  • That you are passing by the favourite store and need to by milk (Location)
  • The file’s download has completed in the background (Time Interval/Instant)
Simple Local Notification
Simple local notification

To create local notifications you need to ask the user for permission like in the previous explanation.

When you create a local notification, the OS takes over the responsibility. When your app is killed or in the background, the OS will make sure that it will be presented to the user when needed.

Local notifications are the way to draw users’ attention and increase engagement with the app.

Considering the technical part, every local notification has three parts:

Content

These are the visual and configurational properties of the notification.

  • title — Simple String, title of Message
  • subtitle — Simple String, subtitle of Message. This is usually optionally used by the marketing team
  • body — Simple String, subtitle of Message. Content of the Message
  • sound — UNNotificationSound.default is the default. You can use a custom one
  • badge — The number on the “Red Dot.” You can use this to set the exact number. But you can have your own system that will add or subtract to the count based on your needs. You can combine it with your inbox messages, some app-specific thread messaging system. Whatever you support.
  • user info — You can pass some data as a key:value pair.
  • attachments — You can use multimedia files that are on your phone. Basically, you can have some video, image, or gif in the bundle of the application and use it as a resource. I did not use this, as Rich Media was a much better option as resources are downloaded. Later this will be explained.
  • categoryIdentifier — This is an interesting option. You can use this to add Action Buttons to a notification or to distinguish between rich media (Notification Service Extension or your Notification Content Extension actions).
  • interruptionLevel — iOS 15 brought this. This tells the system how important the message is and when to send it.
    active — The system presents the notification immediately, lights up the screen, and can play a sound.
    critical — The system presents the notification immediately, lights up the screen, and bypasses the mute switch to play a sound.
    passive — The system adds the notification to the notification list without lighting up the screen or playing a sound.
    timeSensitive — The system presents the notification immediately, lights up the screen, and can play a sound, but won’t break through system notification controls.
  • threadIdentifier — You can assign each local or push notification an identifier to group them visually on Notification Center. For example, all info messages to be grouped.
interruptionLevel on iOS 15

Trigger

This is the type of trigger that will wake up the OS and send the notification.

  • time — timeInterval, duration what will pass till notification is fired
  • calendar — exact date-time when need to fire the notification
  • location — When you enter or exit a specific region, the notification will be triggered.

Request

Request is used to do nothing more than group all the data from above. It has the contenttrigger and the identifier.

It is worth mentioning that an identifier is important in case you want to cancel the notification. We already said that you create the notification in the app. But the OS takes over and adds to the list. If you have no identifier then you can’t ask the OS to cancel it.

UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: "my_notification_id")

The code example of notification types is below:

Time Interval local notification

Calendar local notification, exact date will be the Trigger

For Location type of a local notification, I will show an example but will not go into detail. It is a story for itself, and I will create a dedicated article about this. On a high level you need to use the CoreLocation module to be able to use Geofencing.

Here’s the code:

Location local notification with Geofence

Enter or Exit Geofence from previous example
Enter or exit Geofence from previous example

In the Content section, you could see threadIdentifier. You can use this to group notifications. For example, by unique user or deal promotions. Prior to iOS 15, there was a property summaryArgument which was an addition beside the grouping but now it is deprecated.

How Push Notification grouping looks like
How push notification grouping looks like

Listen to the Events

At this point we have imported UserNotifications registered with requestAuthorization(options:) but we still can’t listen to events.

The next step is to conform to protocol UNUserNotificationCenterDelegateYou can do it in multiple ways. I did this for simplicity in AppDelegate, but you can create a separate Service/Manager or do it in ViewController, all this is situational and there is no final way.

If you want to receive notifications in the foreground, then in the method userNotificationCenter (_:willPresent:withCompletionHandler:) you can configure it, look at completionHandler([.alert, . badge, . sound]).

Now you can listen for local / push notifications with userNotificationCenter (_:didReceive:withCompletionHandler:) and userInfo data. If it has custom Action buttons I’ll explain these in the next section.

If you want to create multiple local notifications, care to not have the same identifier as the new message will override the old one with the same identifier. Here’s the code:

Configure notification to be able to listen to some Events

Action

We can define Actions / Buttons for notifications. To do this, we need to define actions with UNNotificationAction. And then to add them to the category with UNNotificationCategory. The code is shown below:

How to Create Actions on notifications

If you scroll back when we have created the notifications and add the next line:

content.categoryIdentifier = "myActionCategoryIdentifier1"

then it will trigger the category1 type of notification. You can see that you can create lots of category types with different numbers of buttons and functionalities.

When you receive the next notification and pull the notification down or long-press it, you will see the Action buttons. The visual appearance of the buttons can be different between iOS versions.

The Action Buttons and Badge
The Action Buttons and Badge

To be able to detect what button is tapped by the user, modify the userNotificationCenter (_:didReceive:withCompletionHandler:) method with the following lines of code:

Listen for notification Button Tap

For example, you can snooze a repeating alarm and it will ring/send a local notification again in five minutes. But, you can also delete the notification, which will cancel this notification with an identifier.

APNs (Apple Push Notification service)

At this point, we are almost ready for push notifications. But before we jump into that, we need to understand how to register for push notifications. APNs is the Apple Service that lets us request the Push Token.

High Level Overview how Push Notification is used
High-Level Overview of how the push notification is used

Notifications Permission State

There are a couple of states of push notification permission. You can use this to decide should you present them the notification permission alert or notify your backend side that the user rejected or disabled the Notifications.

  • notDetermined — User is not asked for permissions
  • denied — User is presented the alert but denied
  • authorized — User is presented the alert and user allowed
  • provisional — User is not asked directly, but in code is set for this Permission type
  • ephemeral — The app is authorized to schedule or receive notifications for a limited amount of time. For example, AppClip can do this with some configuration in Info.plist. The details are in the next link.
ephemeral AppClip Notification is enabled
ephemeral AppClip notification is enabled

Code example that can be asked for the system.

Push Notifications

All the UI that we have seen till now with local notifications can be applied also with push notifications. The only difference is that push notifications are triggered remotely and must go over APNs.

Considering the configuration, first we need to enable push notifications for the project in Xcode.

Setup push notifications in project

The register part is almost the same as with local notification. Actually, we will use the same method as above requestAuthorization(options:). The difference is that now we also want to call

UIApplication.shared.registerForRemoteNotifications()

We can see it is just a couple lines of code difference

This will trigger the application (_:didRegisterForRemoteNotificationsWithDeviceToken:) and the token will be received. Or in case of a fail application (_:didFailToRegisterForRemoteNotificationsWithError:).

Token looks something like this:
9340516ea8a5ae6b149fa90c07efc1f738b2b0d38463cd326858921fc0d93a91

As APNs are the central point from where we get the Remote Notification Token, sometimes firewalls don’t want to let through. Especially in the case of the wireless. At least this is my experience and assumption. Resetting wifi, router, or switching to a cellular connection can help.
Also, keep in mind this is an async process. It could take a while to receive the token — second, even minutes sometimes.

The token is received in data form and can be converted into human-readable code

We should send this token to our server or to a third-party service that we or the marketing team will use to create the push messages.

There are a bunch of these kinds of third-party services like LeanPlumCleverTapOneSignalFirebase Cloud MessagingUrbanAirship, etc. It can make the integration much easier.

Testing Push Notifications

If you are using a simulator, don’t worry. There is a way to do it.

Drag and drop .apns file to the Simulator
Drag and drop .apns file to the Simulator

First, you need a file with the extension .apns. Something like payload.apns. It has a JSON-like structure.

Payload.apns file

You need to change the Simulator Target Bundle value in the apns file to your app Bundle Identifier. The next image shows how to find it.

How to find the Bundle Identifier
How to find the Bundle Identifier

The apns file’s most used properties are:

  • alert– titlesubtitlebody which is self-explanatory
  • badge– Number of Red dots.
  • sound– The “default” string to play the default sound or “my_sound.mp3” from the bundle
  • category– This is the string that will determine the Action Buttons categoryIdentifier from the local notification part
  • thread-id– This is the id that is used to group the messages
  • content-available– This is the background notification flag. “To perform a silent background update, specify the value 1 and don’t include the alertbadge, or sound keys in your payload.” — Apple
  • mutable-content– If this is 1, then it means the service app extension is used. Later, I will tell more about this.
  • interruption-level– This came with the introduction of iOS 15. The string values “passive”, “active”, “time-sensitive”, or “critical”.

A detailed list can be found at this link.

Example of some properties

One more way is to test push notifications from Terminal.

$ xcrun simctl push device_identifier com.example.yourApp payload.apns

You need to replace the device_identifierYou can see how to find this value from the next image.

Device Identifier
Device Identifier

If in the payload, you have the “Simulator Target Bundle”, then no need to type in a command. If you have configured the command the right way, then something like Notification sent to ‘com.example.myApp will pop up on the terminal.

Silent Push Notification

Silent push notifications are messages without alert, UI and sound, and can be received in the background. To be able to use silent push notifications it is not enough to configure the payload with "content-available": 1. We also need to configure the application. We need to enable Background Modes for Remote notifications.

Add Background Modes
Add Background Modes
Background modes: Remote Notification
Background modes: Remote Notification

Silent push notifications can’t contain any alerttitlebody, or sound etc.

Silent push notification payload

Silent push notifications from a practical point of view are used to initiate some background fetch API calls, to start some updates, to be able to perform a task in the background, etc. It wakes up the application from the background.

Badge Count

You can set the badge with a number on the apps icon to let the user know their attention is needed. That the app has something new.

Badge Count
Badge Count

If you have no custom mechanism, then the easiest way is to reset it. To do that in AppDelegate event applicationDidBecomeActive(_:) (or sceneDidBecomeActive(_:) in case of SceneDelegate), call the next line of code:

UIApplication.shared.applicationIconBadgeNumber = 0

Rich Media (Multimedia Notification)

This is the most fun part. We can use an image, video, gif, or create a Custom UIView.

Push notifications have a maximum payload size of 4KB. With Extensions, we have a chance to intercept any incoming push notification and modify the title, decrypting any encrypted data or even downloading media attachments.

If you are not familiar with the Extensions, they are like mini applications that are in some way connected with your main app, bundled inside. They are the “gatekeepers” of your app. In the push notification case, they have a chance to do some action before your app is presented with the push notification.

How Service / Content Extension Intercept the Message by Microsoft
How Service / Content Extension Intercept the Message by Microsoft

Example of Payload Content

As you see “mutable-content”: 1 indicates that this is a rich media type push notification.

Notification Service Extension

At the start of the article, you could see a Rich Media push notification with the video (mp4) extension. Below you can see one with a photo:

Rich Media Push Notification with Photo
Rich Media push notification with Photo

In our case, the Service Extension has a chance to download some picture, video, or gif, depending on what URL is in the payload. For example, OS gives the Service Extension some time to modify the title, body, or maybe to download an image. If you could not modify the content, it will fall back to regular push notifications with original content from the payload.

To add a Notification Service Extension to the project, go to menu – > File -> New -> Target. Pick Notification Service Extension, and click the Next button.

Application Extensions
Application Extensions
Creating the Service Extension
Creating the Service Extension: Organization Identifier vs Bundle Identifier

Look into Organization Identifier and Bundle Identifier. The Extension identifier is based on your main app target bundle identifier, the difference is in the postfix. This way iOS knows in what application the Extension is embedded.

You have Folder and Target created
You have Folder and Target created

With the method, didReceive(_ request:), we can catch and modify the content. serviceExtensionTimeWillExpire() is a fallback option in case we could not modify the content. For example, if the download of the image failed or took too much time.

Basic Notification Service Code

Complete code can be found on the next link.

Example of how to add multimedia resources to the payload

Rich media push notification with gif and Action buttons

You can see that Rich Media also can have Action buttons. You need to register them in Service Extension. But the tap handling needs to be done in the application. In our case, in AppDelegate.

Rich media Action buttons

Notification Content Extension

Content Extension is all about creating a Custom View. Notification Content Extension uses a NotificationViewController, which is a subclass of UIViewController. It also creates in the folder storyboard and Info.plist.
The setup is similar to Service Extension. Just follow the previous steps.

Content Extension by Microsoft
Content Extension by Microsoft

Below you can see a really nice Custom View—a carousel with multiple images, titles, subtitles, and buttons. Every image could have a different text. Every image could have a different Deep-link.

Content Extension with Carousel by OneSignal

To be able to use the Content Extension in Info.plist, find UNNotificationExtensionCategory and its value that needs to be added in the push notification payload under the “category” key.

This kind of notification can even have integrated maps or any framework.
Of course, keep it lightweight.

Content Extension with Map Kit

Now the possibilities are almost unlimited.

Certificates

I will try to explain this simply and focus on Push certificates.
Go to https://developer.apple.com/account/ Certificates, Identifiers & Profiles.

  • Click on the “+” sign, Apple push notification service SSL.
  • Select the App ID (assume you have created it)
  • Download the aps_xyz.cer
  • Double click it, go to Keychain Access.app (cmd + space opens the Spotlight and,type Keyc…)
  • You should see Apple Sandbox Push Services: com.example.demo (your bundle id)
  • Right-click
  • Export “Apple Sand…”
  • Save the .p12 file.

The backend will ask for this .p12 file mostly, to be able to send push notifications over APNs. But a better option is to create a key /.p8 file.

Key File

Tip

Push certificates expire in one year, and you have to renew them in the Apple developer center and re-upload the new certificate to your push provider/backend every year. If you have more apps, this process needs to be done every year for every app. The p8 / key file works for all your apps in both development and production environments, and it doesn’t expire.

Tools

Here are tools to be able to test easily on the device:

https://github.com/onmyway133/PushNotifications 👈 My recommendation

PushNotification Tool

https://github.com/noodlewerk/NWPusher

Conclusion

Now you know lots of things to do with User Notifications as Apple names them. As I said, I needed years and years to gather all the info and learn it. Please feel free to tell me something that I may be interested in and don’t know.

If you got to this point, thanks for reading. You deserve a coffee ☕️. 🙂 If you like the content please 👏, share, subscribe, buy a coffee it means to me. If you have some suggestions or questions please feel free to comment.

Next Post

Previous Post

Leave a Reply

© 2025 SkySpirit Labs

Theme by Anders Norén