Search code examples
iosswiftnotificationscustomizationonesignal

Swift NotificationContentExtension not working


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 enter image description here


Solution

  • 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