11

I am working on Rich Notification for a Weather App which is sent via FCM.

First of all, I would like to inform you that my notifications are sent and received successfully both with APNS and FCM.

I am using Pusher and Postman in order to test everything.

CONTEXT

Here is the Payload I am sending through Postman

{
    "to": "/topics/03772",
    "content_available": true,
    "mutable_content": true,
    "priority": "high",

    "notification": {
        "title": "Daily forecast",
        "body": "Blue sky, no clouds",
        "sound": "default",
        "click_action": "weather"
    },
    "data": {
        "timestamp": "1506103200",
        "code": "03772",
        "city": "London",
        "attch": "7a",
        "t": "15˚",
    }
}

I like this Payload format, I know it's working, it feels clear to me and easy to understand.

EXPLANATIONS:

Users can subscribe to a topic by adding a city as a Favorite city. The topic is the city code.

The data part is the weather data sending from the server.

Here is my didReceive method :

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    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()
    }


    let attachment = try! UNNotificationAttachment(identifier: "image",
             url: Bundle.main.url(forResource: "12_c", withExtension: "png")!,
                                    options: nil)

    let category = UNNotificationCategory(identifier: "weather", actions: [], intentIdentifiers: [], options: [])

    UNUserNotificationCenter.current().setNotificationCategories([category])

    content.attachments = [attachment]

    contentHandler(content.copy() as! UNNotificationContent)
}

I am directly testing with an image name from Bundle, but it is not working.

QUESTION

How can I save to the disk the PNG Representation of an UIImage from the xcassets folder?

Indeed, the key/value "attch": "7a" from the Payload is the the code for the weather image to display in the notification as attachment. I have saved all the images in my xcassets folder (with same code name)

I need to:

  • Get the image name received in the Notification
  • Get the associated image in xcassets folder
  • Copy the PNG representation of the image to FileManager (disk)
  • Set it as Attachment for the Notification

EDIT: RESOLVED!

My question is resolved thanks to an answer from an other question which is not even marked as accepted answer, but it helped me, for this one.

Indeed, people might want to do the same as I did, or didn't even think about what can be achieve with Notifications.

In all the examples or guides that I read, they explain how to download an image and set it as attachment for you notification.

But it can also be done with local image from xcassets folder. People can now learn how it can be applied for this specific purpose.

The number of upvotes showed me that people were in the same situation as I did, and they probably learned from this question/answer.

Here is my final didReceive method :

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

        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        if let bestAttempContent = bestAttemptContent {
            // Modify the notification content here

            let userInfo : [AnyHashable: Any] = bestAttempContent.userInfo

            if let imgName = userInfo["attch"] {
                if let url = createLocalUrl(forImageNamed: imgName as! String) {
                    do {
                    let attachment = try UNNotificationAttachment(identifier: "weather", url: url, options: nil)
                        defer {
                            self.bestAttemptContent?.attachments = [attachment]
                        }
                    }
                    catch {
                        print("Could not set UNNotificationAttachment")
                    }
                }
            }

            contentHandler(bestAttempContent)
        }
    }

And the method I found on SO :

func createLocalUrl(forImageNamed name: String) -> URL? {

        let fileManager = FileManager.default
        let cacheDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)[0]
        let url = cacheDirectory.appendingPathComponent("\(name).png")
        let path = url.path

        guard fileManager.fileExists(atPath: path) else {
            guard
                let image = UIImage(named: name),
                let data = UIImagePNGRepresentation(image)
                else { return nil }

            fileManager.createFile(atPath: path, contents: data, attributes: nil)
            return url
        }

        return url
    }

I hope it helped you.

  • Didn't [this answer](https://stackoverflow.com/a/39748919/2857130) work for you? – staticVoidMan Apr 12 '18 at 07:01
  • Yes and I wrote an answer explaining that this answer was the one I was looking for but it get deleted and downvoted so... –  Apr 12 '18 at 08:00
  • yeah, you shouldn't be writing answers that link to other answers. anyways, this question will be closed as duplicate once the bounty period ends. – staticVoidMan Apr 12 '18 at 18:34
  • Possible duplicate of [Can I get a NSURL from an XCAssets bundle?](https://stackoverflow.com/questions/21769092/can-i-get-a-nsurl-from-an-xcassets-bundle) – staticVoidMan Apr 30 '18 at 09:06

1 Answers1

0

If I have this correctly, you would like to use the attch property's value as an image name to form a url from local storage (.xcassets).

The payload content is JSON, so should be able to access attch using dot syntax:

let fileName: String = request.content.data.attch

Then form a URL to the Resource and attach it. (I'm assuming its "7a.jpg")

if let url = Bundle.main.url(forResource: fileName, withExtension: "jpg") {
  //The Image exists
  let attachment = try! UNNotificationAttachment(identifier: "image",
         url: url, options: nil)
  content.attachments = [attachment]
} else {
  //Unable to get/find image, use a default one.

  let attachment = try! UNNotificationAttachment(identifier: "image",
         url: Bundle.main.url(forResource: "12_c", withExtension: "png")!, options: nil)
  content.attachments = [attachment]
}
RLoniello
  • 2,309
  • 2
  • 19
  • 26
  • Thank you for your answer, I am implementing it. The "image" identifier is purely declarative? –  Mar 28 '18 at 08:15
  • its a unique identifier of the particular attachment. Use it to identify the attachment later. – RLoniello Mar 28 '18 at 08:23