34

I'm in a situation where I am listing attachments from a message. Each attachment has a NSURL associated with it that represents a path to the attachment data. In the case of non-image attachments I want to load pre-generated icon thumbnails in the place of a preview (i.e. excel documents, PDF, word, etc).

I currently have these images in my project, within an XCAssets bundle. I can get at them using [UIImage imageNamed:@"someName"]. I cannot seem to get at them via NSBundle's various resource lookup methods. Does the XCAssets bundle somehow change the filenames of the icon images I'm looking for?

Here's the code I'm currently working with. Path is nil every time. Do I need to not use XCAssets in this case?

+ (NSURL *)mediaURLWithMessage:(SRMediaMessage*)message
{
  NSURL *url = message.mediaURL;

  // if this is an image URL or non-existant, there is no work left to do, return it.
  if (!url || (message.secureFile.secureFileMimeType & SRSecureFileMimeTypeImage))
    return url;

  NSString *filename = @"unknown";

  switch (message.secureFile.secureFileMimeType)
  {
    case SRSecureFileMimeTypeDOC:
      filename = @"doc";
      break;

    case SRSecureFileMimeTypePPT:
      filename = @"ppt";
      break;

    case SRSecureFileMimeTypePDF:
      filename = @"pdf";
      break;

    case SRSecureFileMimeTypeXLS:
      filename = @"exl";
      break;

    case SRSecureFileMimeTypeCSV:
      filename = @"csv";
      break;

    case SRSecureFileMimeTypeTXT:
      filename = @"txt";
      break;

    case SRSecureFileMimeTypeRTX:
      filename = @"rtf";
      break;

    default:
    case SRSecureFileMimeTypeMP4:
      // unknown icon for now.
      break;

      // unused but available:
//      @"ilife"

  }

  NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:nil];
  if (path)
    url = [NSURL fileURLWithPath:path];

  return url;
}
Jamie Taylor
  • 4,709
  • 5
  • 44
  • 66
slycrel
  • 4,275
  • 2
  • 30
  • 30

5 Answers5

22

I wanted to access some vector assets for creating UNNotificationAttachment with local resources so I came up with this helper class. It basically just gets image from assets, saves its data to disk and return file URL. I hope that helps someone.

import UIKit

class AssetExtractor {

    static 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")

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

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

        return url
    }

}
tadija
  • 2,981
  • 26
  • 37
  • 1
    Thank you man, you just answered my bounty question!! https://stackoverflow.com/questions/49360386/ios-fcm-how-to-use-image-name-from-payload-to-display-an-image-from-xcasset –  Apr 10 '18 at 08:53
14

If you're targeting iOS 7+, Xcode 5 now puts the assets into a new file format. 1 file for all of the assets. This means you can not get access to the file directly.

If you need to access the file directly, you can include it as an normal image outside of an asset catalog.

Dave Wood
  • 13,143
  • 2
  • 59
  • 67
  • 1
    That's what I was afraid of. I haven't been able to find any direct documentation to confirm this, thus the question. I may just have to make this change. Thanks. – slycrel Feb 14 '14 at 01:24
  • 2
    I've never looked into the details of that, but `[UIImage imageNamed:...]` still works with images in an asset bundle. – Holly Feb 14 '14 at 01:25
  • And you can actually save file from there to any directory you like. :) – Legoless Nov 07 '14 at 10:30
13

Swift 5

And it made a bit more sense to me as a URL extension.

extension URL {
    static func localURLForXCAsset(name: String) -> URL? {
        let fileManager = FileManager.default
        guard let cacheDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first else {return nil}
        let url = cacheDirectory.appendingPathComponent("\(name).png")
        let path = url.path
        if !fileManager.fileExists(atPath: path) {
            guard let image = UIImage(named: name), let data = image.pngData() else {return nil}
            fileManager.createFile(atPath: path, contents: data, attributes: nil)
        }
        return url
    }
}

Usage:

if let url = URL.localURLForXCAsset(name: "MyIcon") {
  // code
}
Murray Sagal
  • 8,454
  • 4
  • 47
  • 48
9

Swift 3 and Swift 2.3 versions of the answer by @tadija:

Swift 3:

import UIKit

class AssetExtractor {

    static 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
    }

}

Swift 2.3:

import UIKit

class AssetExtractor {

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

        let fileManager = NSFileManager.defaultManager()
        let cacheDirectory = fileManager.URLsForDirectory(.CachesDirectory, inDomains: .UserDomainMask)[0]
        guard
            let url = cacheDirectory.URLByAppendingPathComponent("\(name).png"),
            let path = url.path
            else { return nil }

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

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

        return url
    }

}
Community
  • 1
  • 1
Simon Pickup
  • 832
  • 12
  • 19
1

Swift 3.x

static 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
}
juliancadi
  • 987
  • 1
  • 8
  • 23