14

I've been working on rich notification experience which has been introduced in iOS10 and stuck with passing images as attachments to UNNotificationContentExtension.

Here's my ContentExtension:

class NotificationViewController: UIViewController, UNNotificationContentExtension {

    @IBOutlet weak var attachmentImage: UIImageView!

    func didReceive(_ notification: UNNotification) {

        if let attachment = notification.request.content.attachments.first {
            if attachment.url.startAccessingSecurityScopedResource() {
                attachmentImage.image = UIImage(contentsOfFile: attachment.url.path)
                attachment.url.stopAccessingSecurityScopedResource()
            }
        }
    }
}

As a tutorial, I've been following Advanced Notifications video from WWDC. I've checked - UIImage I'm assigning to UIImageView:

  • is not nil
  • has proper CGSize (191x191)
  • attachment.url.path equals /var/mobile/Library/SpringBoard/PushStore/Attachments/<bundle of app>/<...>.png

Here's how I send local notification from the app:

    let content = UNMutableNotificationContent()
    content.title = "Sample title"
    content.body = "Sample body"
    content.categoryIdentifier = "myNotificationCategory"

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

    content.attachments = [ attachement ]

    let request = UNNotificationRequest(identifier:requestIdentifier, content: content, trigger: nil)
    UNUserNotificationCenter.current().delegate = self
    UNUserNotificationCenter.current().add(request){(error) in
        if (error != nil){
        }
    }

"cat.png" is just a dummy resource I've added to proj. enter image description here

As you can see, notification shows the pic, so I assume, that I'm sending it correctly, but in the expanded state(in NotificationViewController) I've never succeed at showing the same image.

What am I doing wrong? Thanks!

Konstantin Loginov
  • 15,802
  • 5
  • 58
  • 95
  • 1
    Hi I am still facing the same issue that not able to display image in expanded state. Can you plz help me. My code is in objective c. – Ekta Padaliya Sep 05 '16 at 11:29

2 Answers2

26

When you create an UIImage with contentsOfFile, the UIImage object reads the image header only, which indicates basic info, such as image size, etc.

So, try move stopAccessingSecurityScopedResource to [NotificationViewController dealloc].

Or using following:

  • objective-c code:

    NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
    UIImage *image = [UIImage imageWithData:imageData];
    
  • swift code:

    let imageData = NSData(contentsOf: attachment.url)
    let image = UIImage(data: imageData! as Data)
    

There is no document saying that contentsOfFile only reads the image header. But when I run the following code:

NSString *docFolderPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *pngPath = [docFolderPath stringByAppendingPathComponent:@"test.png"];    
UIImage *image = [UIImage imageWithContentsOfFile:pngPath];
[[NSFileManager defaultManager] removeItemAtPath:pngPath error:nil];
imageView.image = image;

An error occurs:

ImageIO: createDataWithMappedFile:1322:  'open' failed '/Users/fanjie/Library/Developer/CoreSimulator/Devices/FFDFCA06-A75E-4B54-9FC2-8E2AAE3B1405/data/Containers/Data/Application/E2D26210-4A53-424E-9FE8-D522CFD4FD9E/Documents/test.png'
     error = 2 (No such file or directory)

So I made a conclusion that UIImage contentsOfFile only reads the image header.

jayess
  • 49
  • 6
Jeffery
  • 326
  • 3
  • 6
  • Jeffery, you are my hero. Great catch & explanation! Slightly modified the answer to add swift-version I used. Works like a charm now! – Konstantin Loginov Aug 22 '16 at 17:06
  • 1
    @Jeffery where did you read that `contentsOfFile` only loads image header? Here is the Discussion from docs: "This method loads the image data into memory and marks it as purgeable. If the data is purged and needs to be reloaded, the image object loads that data again from the specified path." I think the problem could be that you are calling `stopAccessingSecurityScopedResource` which purge image memory and It closes access to the image file and it can't be reloaded. – Kostiantyn Koval Aug 23 '16 at 06:48
  • @Kostiantyn Koval Sorry. No docs saying that `contentsOfFile` only loads image header. I think calling `stopAccessingSecurityScopedResource ` would not purge image memory. It is not convince to post code in comment, so I add some explanation into the answer. – Jeffery Aug 24 '16 at 00:56
  • 1
    Ahh, that's for additional code examples. Yes `imageWithContentsOfFile` requires file to exist, because it loads it's data lazily, so when you remove file or closes the access to it by calling stopAccessingSecurityScopedResource, then you get an error. – Kostiantyn Koval Aug 24 '16 at 10:57
  • @Jeffery Hi I'm also facing same issue that I'm not able to display image in expanded state. Can you please help me. – Ekta Padaliya Sep 05 '16 at 11:27
  • `NSData *imageData = [NSData dataWithContentsOfURL:imageURL];` didn't work for me. Instead `NSData *imageData = [NSData dataWithContentsOfFile:imageURLString];` worked. – mickeymoon Apr 05 '17 at 07:57
  • This was indeed what worked for me! It is quite strange that it would work for the thumbnail but not for the expanded image. Thanks for this! – kakubei Apr 05 '17 at 11:50
1

Thanks to @jeffery ,

Here is the exact code for the image shown in the notification extension:

if let attachment = notification.request.content.attachments.first {
            if attachment.url.startAccessingSecurityScopedResource() {
                let data = NSData(contentsOfFile: attachment.url.path);
                self. attachmentImage?.image = UIImage(data: data! as Data);
                attachment.url.stopAccessingSecurityScopedResource()
            }
        }
Pradeep Kachhawaha
  • 6,589
  • 2
  • 14
  • 13