Improving the iOS Push Notification localization @ Halodoc using UNNotificationServiceExtension approach

iOS Development Dec 8, 2023

At Halodoc, we prioritize providing the best user experience possible, which is why we offer complete app localization. This goes beyond simply adding translation support. We ensure that our app interface is adapted to accommodate language and regional specificities, including support for different time zones, regional numeric formats, dates, measurement units, and more.

Our commitment to delivering the best user experience is evident throughout the internationalization process, from the design stage to the final product. We strive to create a globalized user experience that meets the needs and expectations of our users.

Overview

In this blog, we will discuss, how we localised iOS Push Notifications based on our app language. We will discuss our older localization Approach and the problems with that approach. Then we will discuss how we solved those problems using UNNotificationServiceExtension Approach

Older Approach: Localizable.strings File Approach

Remote Push Notifications were driven from the Backend via APNs (Apple Push Notification Service). We relied on keys such as  loc-key, title-loc-key in push notifications payload for the same and the localised values for these keys were bundled within the App resource.

Problems faced with this approach

  1. App's language-specific push notifications localisation: In our app, users can choose their preferred languages. They can later switch to other language as per their requirements. So the problem we faced was, the push notification was localised using the device’s language settings, however we wanted notification to be localised using the App’s language settings.
  2. Backward Compatibility: Since the values for the Push Notification keys( log-key , title-log-key) were part of the App’s bundle, localised content for new push notification was missing for older Apps. To partially solve this we could have used title and body key of the push notification payload but it can either be EN (English) or ID (Indonesian) in our use case. We had to make a app release after making the changes

New Approach with Notification Extension

We started localizing the notification content with the help of Notification Service Extension and App Groups. Let’s discuss about these in detail:

  1. Notification Service Extension — Notification Service Extension is a process that allows us to modify the content of a remote notification before it's delivered to the user
  2. App Groups — By using App groups, we can know in NotificationServiceExtension  about the language user have selected in App. It provides a way to share data between apps and our Notification Service Extension. In our case, we can save current app language in UserDefaults group and use that group to access app language between app and service extension.

Notification Service Extension

How this extension service was triggered by OS

When device received remote notification, it checked for mutable-content key in the payload . If the mutable-content value is 1  then the system passed the notification to our notification service app extension before delivery. So to invoke extension service, we made sure

  1. To set mutable-content  value to 1 in push notifications payload.
  2. To include all the supported language version of title, body in the push notifications payload from backend.

What changed in iOS app?

Step 1.

Created a Notification Service Extension in our app:

File -> New -> Target -> Notification Service Extension and filled in the details

Step 2.

Gave a name to service extension and activated it. Once activated, we saw that extension was added to project targets. We were also able see the service folder inside our project.

Step 3.

There was two files inside the service folder. NotificationService.swift which extended UNNotificationServiceExtension and info.plist. When a notification received in app, the system loaded the extension and didReceive method got called.

Step 4.

The notification service app extension had two methods

Step 5.

Created a class with the name UNNotificationContentMutator and implemented mutate(content:) methods

Step 6.

In didReceive(_:withContentHandler:) method, we mutated the UNNotificationContent instance using UNNotificationContentMutator with the "localization_content" dictionary from the push notification payload.

UNNotificationServiceExtension Sample Code
didReceive(_:withContentHandler:) method has only about 30 seconds to modify the payload and call the provided completion handler. If our code takes longer than that, the system calls the serviceExtensionTimeWillExpire() method, at which point we must return whatever you can to the system immediately. If we fail to call the completion handler from either method, It will display the original content and the extension process will crash

Payload sample for localization_content

Sample Payload

App Groups

We needed App groups to get shared UserDefaults. We used shared UserDefaults with app groups to save and fetch current language of the app used. Steps to create app group

How we created a App Group

  1. Selected App Target -> Signing & Capabilities
  2. Clicked ‘+’ to Add Capability. From the Capabilities listing, selected App Groups.
  3. Now our app had the capability to add app groups. Clicked ‘+’ to create one. Gave a name and clicked ‘OK’.
  4. Xcode created provisioning profile with this entitlement for app. Once done, we could see .entitlements file inside our app folder and we were able to see created app group.

Now we had an app group container which helped us share data between our app and service extension.

How we fetched data using App Group

We got access to the language settings via shared preferences UserDefault(suiteName: "AppGroupSuiteName")

The Advantages of Service Extension:

Rich Content Handling
UNNotificationServiceExtension allows for the modification of the content of remote notifications before they are delivered to the user. This is crucial as our app needs to handle rich content, such as downloading and attaching media files or localising the notification payload.

Asynchronous Processing
The extension runs independently of our app and allows for asynchronous processing of notification content.

Custom Logic
As our app requires custom logic for handling specific types of notifications and we need to perform actions like authentication or fetching additional data before displaying the notification, UNNotificationServiceExtension provides a way to implement such custom logic.

Compatibility and Integration
In some cases, the use of UNNotificationServiceExtension might be a part of broader compatibility and integration strategies. For example, our app interacts with other services or third-party systems that require custom processing of notifications, using the service extension is a practical choice.

Limitations with Notification Extension Service

  1. If we support many languages then localisation content for all those languages must be present in the payload which will increase the size. APNs refuses a notification if the total size of its payload exceeds 4KB
  2. Though without new app release, we can trigger new push notifications or Update content of old push notifications from backend with UNNotificationServiceExtension support, to handle any new push notifications tap actions we must add those routing logic and make a new app release.

Conclusion

The UNNotificationServiceExtension is a crucial component in iOS development for customising and processing remote notifications before they are displayed to the user. By extending the capabilities of the User Notifications framework, we can modify the content of notifications, download media attachments, or even perform additional processing. This extension plays a vital role in enhancing the user experience by allowing developers to tailor notifications to suit their app’s specific needs.

References

Notification Payload Structure

Modify Notification Content

Notification Extension Service

Join Us

Scalability, reliability and maintainability are the three pillars that govern what we build at Halodoc Tech. We are actively looking for engineers at all levels and if solving hard problems with challenging requirements is your forte, please reach out to us with your resume at careers.india@halodoc.com.

About Halodoc

Halodoc is the number 1 all around Healthcare application in Indonesia. Our mission is to simplify and bring quality healthcare across Indonesia, from Sabang to Merauke. We connect 20,000+ doctors with patients in need through our Tele-consultation service. We partner with 3500+ pharmacies in 100+ cities to bring medicine to your doorstep. We've also partnered with Indonesia's largest lab provider to provide lab home services, and to top it off we have recently launched a premium appointment service that partners with 500+ hospitals that allow patients to book a doctor appointment inside our application. We are extremely fortunate to be trusted by our investors, such as the Bill & Melinda Gates Foundation, Singtel, UOB Ventures, Allianz, GoJek, Astra, Temasek, and many more. We recently closed our Series D round and in total have raised around USD$100+ million for our mission. Our team works tirelessly to make sure that we create the best healthcare solution personalised for all of our patient's needs, and are continuously on a path to simplify healthcare for Indonesia.

Sakshi Bala

Swift enthusiasts with 7+ years of experience in iOS.