I am using OneSignal for the notifications in my messaging app. I have a standard OneSignal’s NotificationServiceExtension. And I also need to add a NotificationContentExtension to customize my notifications’ UI. What exactly I need to customize: I need to replace the app icon on the left of the notification with the avatar of the person who sent me a message. The only way I currently see to do that is by using NotificationContentExtension, but please do let me know if there is a simpler way. I don’t need to customzie anything else – just this picture, but Apple doesn’t give me a simple way to do it.
What’s wrong: When the noti comes – I see the standard push UI in notification centre. When I long press and hold, I see my custom UI (btw not quickly, only after a while). What I need – is to see only my custom UI from the beginning. I think it’s unlikely that OneSignal has anything to do with this, but in any case I cannot try without it.
I created a basic version of my app – only containing the noti setup, here is all of its code without the api keys
import SwiftUI
import OneSignalFramework
@main
struct qqqqApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
if #available(iOS 10.0, *) {
let options: UNAuthorizationOptions = [.alert]
UNUserNotificationCenter.current().requestAuthorization(options: options) { (authorized, error) in
if authorized {
let categoryIdentifier = "OSNotificationCarousel"
let carouselNext = UNNotificationAction(identifier: "OSNotificationCarousel.next", title: "👉", options: [])
let carouselPrevious = UNNotificationAction(identifier: "OSNotificationCarousel.previous", title: "👈", options: [])
let carouselCategory = UNNotificationCategory(identifier: categoryIdentifier, actions: [carouselNext, carouselPrevious], intentIdentifiers: [], options: [])
UNUserNotificationCenter.current().setNotificationCategories([carouselCategory])
}
}
}
// Remove this method to stop OneSignal Debugging
OneSignal.Debug.setLogLevel(.LL_VERBOSE)
// OneSignal initialization
OneSignal.initialize("API_KEY", withLaunchOptions: launchOptions)
// requestPermission will show the native iOS notification permission prompt.
// We recommend removing the following code and instead using an In-App Message to prompt for notification permission
OneSignal.Notifications.requestPermission({ accepted in
print("User accepted notifications: (accepted)")
}, fallbackToSettings: true)
return true
}
}
import UserNotifications
import OneSignalExtension
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var receivedRequest: UNNotificationRequest!
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.receivedRequest = request
self.contentHandler = contentHandler
self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
/* DEBUGGING: Uncomment the 2 lines below to check this extension is executing
Note, this extension only runs when mutable-content is set
Setting an attachment or action buttons automatically adds this */
// print("Running NotificationServiceExtension")
// bestAttemptContent.body = "[Modified] " + bestAttemptContent.body
OneSignalExtension.didReceiveNotificationExtensionRequest(self.receivedRequest, with: bestAttemptContent, withContentHandler: self.contentHandler)
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
OneSignalExtension.serviceExtensionTimeWillExpireRequest(self.receivedRequest, with: self.bestAttemptContent)
contentHandler(bestAttemptContent)
}
}
}
import UIKit
import UserNotifications
import UserNotificationsUI
class NotificationViewController: UIViewController, UNNotificationContentExtension {
@IBOutlet var titleLabel: UILabel?
@IBOutlet var subtitle: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
// Do any required interface initialization here.
}
func didReceive(_ notification: UNNotification) {
self.titleLabel?.text = notification.request.content.title
self.subtitle?.text = notification.request.content.body
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>UNNotificationExtensionDefaultContentHidden</key>
<true/>
<key>UNNotificationExtensionCategory</key>
<string>OSNotificationCarousel</string>
<key>UNNotificationExtensionInitialContentSizeRatio</key>
<real>0.3</real>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.content-extension</string>
</dict>
</dict>
</plist>
2
Answers
Toru is right, I need to use communication notification. The Apple doc doesn't show any pictures, and doesn't say explicitly it will change the push's avatar. It also uses Siri intents which seem like something I don't need at all to change the picture. But it is what I was looking for.
Here is the result I wanted:
Here is the setup:
Communication Notifications
to your main target in theCapabilities
sectionAdd Notification Service Extension to your app - it serves to modify your push's payload before passing it on to the iOS system to be shows in notification centre. The avatar will be set here, and can be extracted from the "aps" field of your push.
Add the following to service extension's plist, inside
NSExtension
dictionary:You can change the notification icon by using Intents. It is called communication notification. You don’t need to use NotificationContentExtension.
There’s an official document you can refer to implement.
https://developer.apple.com/documentation/usernotifications/implementing-communication-notifications
You can also refer to the below link; there is a swift implementation with OneSignal in the question.
iOS Communication Notification icon