Vincenzo
Vincenzo

Reputation: 6358

Incoming rich push notifications won't show the image. Swift 4

I'm trying to implement rich push notifications but I don't get the image at passed url to get displayed. When I open the notification I can only see title, subtitle and body. I'm following the tutorial at https://www.pluralsight.com/guides/creating-ios-rich-push-notifications as I found it suggested in other posts but I'm stuck at showing the image. I also read in one post that I shouldn't run the app but the NotificationExtension but when I do the app crashes when receiving the notification with

Message from debugger: Terminated due to signal 9 Program ended with exit code: 0

message.

This is the print of userInfo when running the app instead :

[AnyHashable("attachment-url"): https://firebasestorage.googleapis.com/v0/b/fix-it-b4b00.appspot.com/o/Prodotti%2FPrato.jpeg?alt=media&token=5d0fde09-2b86-45b0-a383-a11e7e8e241c, AnyHashable("gcm.message_id"): 1560360808567562, AnyHashable("productId"): 9290CEBE-393C-4285-BE7B-B9E2968A1AA0, AnyHashable("aps"): { alert = { body = "Nuova promozione per articolo: Prato"; subtitle = "Negozio: vincenzo calia"; title = "Promozione negozio"; }; "content-available" = 1; "mutable-content" = 1; sound = true; }, AnyHashable("price"): 10.00, AnyHashable("gcm.notification.priority"): high, AnyHashable("google.c.a.e"): 1]

I checked and the url is correct. Could the problem be that the url is not in string format? I put a print to check it but I don't even get the print at beginning of 'didReceive' method. Can you see where is possibly going wrong? As always Many thanks for your interest and time.

These are the function in NotificationService.swift :

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {

        print("NotificationService: dide receive called")
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        func failEarly() {
            contentHandler(request.content)
        }

        guard let content = (request.content.mutableCopy() as? UNMutableNotificationContent) else {
            return failEarly()
        }

        guard let apnsData = content.userInfo["data"] as? [String: Any] else {
            return failEarly()
        }

        guard let attachmentURL = apnsData["attachment-url"] as? String else {
            print("url is not in string form")
            return failEarly()
        }

        guard let imageData = NSData(contentsOf:NSURL(string: attachmentURL)! as URL) else { return failEarly() }
        guard let attachment = UNNotificationAttachment.create(imageFileIdentifier: "image.gif", data: imageData, options: nil) else { return failEarly() }

        content.attachments = [attachment]
        contentHandler(content.copy() as! UNNotificationContent)
    }

    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 {
            contentHandler(bestAttemptContent)
        }
    }


extension UNNotificationAttachment {
    static func create(imageFileIdentifier: String, data: NSData, options: [NSObject : AnyObject]?) -> UNNotificationAttachment? {

        let fileManager = FileManager.default
        let tmpSubFolderName = ProcessInfo.processInfo.globallyUniqueString
        let fileURLPath      = NSURL(fileURLWithPath: NSTemporaryDirectory())
        let tmpSubFolderURL  = fileURLPath.appendingPathComponent(tmpSubFolderName, isDirectory: true)

        do {
            try fileManager.createDirectory(at: tmpSubFolderURL!, withIntermediateDirectories: true, attributes: nil)
            let fileURL = tmpSubFolderURL?.appendingPathComponent(imageFileIdentifier)
            try data.write(to: fileURL!, options: [])
            let imageAttachment = try UNNotificationAttachment.init(identifier: imageFileIdentifier, url: fileURL!, options: options)
            return imageAttachment
        } catch let error {
            print("error \(error)")
        }

        return nil
    }
}

And this is function that sends the push notification:

static func sendTopicPushNotification(to topic: String, title: String, subtitle: String, body: String, dataUrl: String, productId: String, price: String) {
        let serverKey = firebaseServerKey // AAAA8c3j2...
        //            let topic = "/topics/<your topic here>"  // replace it with partnerToken if you want to send a topic
        let url = NSURL(string: "https://fcm.googleapis.com/fcm/send")

        let postParams: [String : Any] = [
            "to": topic,
//            "priority": "high",
//            "content_available": true,
//            "mutable_content": true,
            "notification": [
                //                    "badge" : 1, sendig the badge number, will cause aglitch
                "body": body,
                "title": title,
                "subtitle": subtitle,
                "text": "some text",
                "sound" : true, // or specify audio name to play
                "priority": "high",
                "content_available": true,
                "mutable_content": true
//                 "category" : "pushNotificationCategory" // "topicPushNotification"
//                    "click_action" : "🚀", // action when user click notification (categoryIdentifier)
            ],
            "data" : [
                "attachment-url": dataUrl,
                "productId": productId,
                "price": price
            ]
        ]


        let request = NSMutableURLRequest(url: url! as URL)
       request.httpMethod = "POST"
        request.setValue("key=\(serverKey)", forHTTPHeaderField: "Authorization")
        request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")

        do {
            //                request.httpBody = try JSONSerialization.data(withJSONObject: postParams, options: JSONSerialization.WritingOptions())
            request.httpBody = try JSONSerialization.data(withJSONObject: postParams, options: [.prettyPrinted]) // working
            print("sendTopicPushNotification : My paramaters: \(postParams)")
        } catch {
            print("sendTopicPushNotification : Caught an error: \(error)")
        }

        let task = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in
            if let realResponse = response as? HTTPURLResponse {
                if realResponse.statusCode != 200 {
                    print("sendTopicPushNotification : Not a 200 response : \(realResponse)")
                }
                print("sendTopicPushNotification : response : \(realResponse)")
            }

            if let postString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue) as String? {
                print("sendTopicPushNotification : POST: \(postString)")
            }
        }

        task.resume()
    }

Upvotes: 4

Views: 3037

Answers (1)

Vincenzo
Vincenzo

Reputation: 6358

I'm finally able to show the picture with the notification. It might had to do with the fact that the tutorial I'm following is for swift 5, and I'm on swift 4. I changed the didReceivecode and omitted the extension UNNotificationAttachmentcode and it's now displaying it correctly. Next step is to add actions based on the category received in the payload, but I still have to figure out if that is possible without implementing also a Notification Content Extension, which for what I understood should be only to have a custom view to display the notification. Any hint will be very appreciated. I hope this will help others as tutorials I found on the subject are a bit confusing and misleading at times. Also many thanks for up voting to the question.

NotificationServiceExtension didReceiveis now:

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {

        print("NotificationService: dide receive called")
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        if let bestAttemptContent = bestAttemptContent {
            if let urlString = bestAttemptContent.userInfo["attachment-url"] as? String,
                let data = NSData(contentsOf: URL(string:urlString)!) as Data? {
                let path = NSTemporaryDirectory() + "attachment"
                _ = FileManager.default.createFile(atPath: path, contents: data, attributes: nil)

                do {
                    let file = URL(fileURLWithPath: path)
                    let attachment = try UNNotificationAttachment(identifier: "attachment", url: file,options:[UNNotificationAttachmentOptionsTypeHintKey : "public.jpeg"])
                    bestAttemptContent.attachments = [attachment]

                } catch {
                    print(error)

                }
            }

            contentHandler(bestAttemptContent)
        }

Upvotes: 1

Related Questions