简体   繁体   中英

Get PHAsset from iOS Share Extension

I am developing a share extension for photos for my iOS app. Inside the extension, I am able to successfully retrieve the UIImage object from the NSItemProvider.

However, I would like to be able to share the image with my container app, without having to store the entire image data inside my shared user defaults. Is there a way to get the PHAsset of the image that the user has chosen in the share extension (if they have picked from their device)?

The documentation on the photos framework ( https://developer.apple.com/library/ios/documentation/Photos/Reference/Photos_Framework/ ) has a line that says "This architecture makes it easy, safe, and efficient to work with the same assets from multiple threads or multiple apps and app extensions."

That line makes me think there is a way to share the same PHAsset between extension and container app, but I have yet to figure out any way to do that? Is there a way to do that?

You can get PHAsset if image is shared from Photos app. The item provider will give you a URL that contains the image's filename, you use this to match PHAsset.

/// Assets that handle through handleImageItem:completionHandler:
private var handledAssets = [PHAsset]()

/// Key is the matched asset's original file name without suffix. E.g. IMG_193
private lazy var imageAssetDictionary: [String : PHAsset] = {

    let options = PHFetchOptions()
    options.includeHiddenAssets = true

    let fetchResult = PHAsset.fetchAssetsWithOptions(options)

    var assetDictionary = [String : PHAsset]()

    for i in 0 ..< fetchResult.count {
        let asset = fetchResult[i] as! PHAsset
        let fileName = asset.valueForKey("filename") as! String
        let fileNameWithoutSuffix = fileName.componentsSeparatedByString(".").first!
        assetDictionary[fileNameWithoutSuffix] = asset
    }

    return assetDictionary
}()

...

provider.loadItemForTypeIdentifier(imageIdentifier, options: nil) { imageItem, _ in
    if let image = imageItem as? UIImage {
      // handle UIImage
    } else if let data = imageItem as? NSData {
      // handle NSData 
    } else if let url = imageItem as? NSURL {
         // Prefix check: image is shared from Photos app
         if let imageFilePath = imageURL.path where imageFilePath.hasPrefix("/var/mobile/Media/") {
             for component in imageFilePath.componentsSeparatedByString("/") where component.containsString("IMG_") {

        // photo: /var/mobile/Media/DCIM/101APPLE/IMG_1320.PNG
        // edited photo: /var/mobile/Media/PhotoData/Mutations/DCIM/101APPLE/IMG_1309/Adjustments/FullSizeRender.jpg

                // cut file's suffix if have, get file name like IMG_1309.
                let fileName = component.componentsSeparatedByString(".").first!
                if let asset = imageAssetDictionary[fileName] {
                    handledAssets.append(asset)
                    imageCreationDate = asset.creationDate
                }
                    break
                }
            }
    }

This only works if the NSItemProvider gives you a URL with the format:

file:///var/mobile/Media/DCIM/100APPLE/IMG_0007.PNG

which is not always true for all your assets, but if it returns a URL as:

file:///var/mobile/Media/PhotoData/OutgoingTemp/2AB79E02-C977-4B4A-AFEE-60BC1641A67F.JPG

then PHAsset will never find your asset. Further more, the latter is a copy of your file, so if you happen to have a very large image/video, iOS will duplicate it in that OutgoingTemp directory. Nowhere in the documentation says when it's going to be deleted, hopefully soon enough.

I think this is a big gap Apple has left between Sharing Extensions and PHPhotoLibrary framework. Apple should've be creating an API to close it, and soon.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM