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>
ios version for all targets is 17.0
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
