Loading image from "My Photo Stream" using UIImagePicker results URL and PHAsset on iOS8

11.7k views Asked by At

I am updated an app from ios7 to ios8 and struggling to get UIImagePicker working to load a picture from the "My Photo Stream" category of photos. The UIImagePicker implementation is standard and I retrieve the URL of the selected photo with:

NSURL *url = [info objectForKey:@"UIImagePickerControllerReferenceURL"];

I then use the new IOS8 APIs for loading this photo:

PHFetchResult *result = [PHAsset fetchAssetsWithALAssetURLs:@[url] options:nil];

This fails to work. The result.count is zero and no image is found. The URL has an different UUID than if I select the photo from "Moments" or "Camera Roll" but looks well formed. (phone is running 8.1):

url NSURL * @"assets-library://asset/asset.JPG?id=4522DBD1-862C-42BE-AF7C-0D6C76CA7590&ext=JPG"

Anyone have some code to load photos from "My Photo Stream" or a way to disable it for the UIImagePicker display?

Using the older ALAssetsLibrary assetForURL API also fails to load these images.

5

There are 5 answers

0
Andrew Simontsev On

Loading an image with a specified URL from My Photo Stream using ALAssetsLibrary is discussed here:

ALAssetsLibrary assetForURL: always returning nil for photos in "My Photo Stream" in iOS 8.1

In short, instead of using assetForURL you have to iterate through all items in a Photo Stream and compare their URL with yours. The link above contains a code example.

It looks awkward, works slower than assetForURL, and it is not so easy to determine if there is no any file with specified URL found, but I did not find any other way to do it (except of migrating to Photos framework).

1
gerram On

Work for me in 8.0

import Photos

func imageFromAsset(nsurl: NSURL) {
    let asset = PHAsset.fetchAssetsWithALAssetURLs([nsurl], options: nil).firstObject as! PHAsset
    let targetSize = CGSizeMake(300, 300)
    var options = PHImageRequestOptions()

    PHImageManager.defaultManager().requestImageForAsset(asset, targetSize: targetSize, contentMode: PHImageContentMode.AspectFit, options: options, resultHandler: {
        (result, info) in
        // imageE - UIImageView on scene
        self.imageE.image = result
    })
}
4
gabbler On

I think you can try this to get "my photo stream" album.

PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumMyPhotoStream options:nil];

Photos in My Photo Stream album are uploaded to iCloud and iCloud will manage them efficiently, photos captured within 30 days are there, and I think PHAsset or ALAssetLibrary dealt with locally stored assets. Here is a test I did on my device, I used a imagePicker to get a photo url from stream album, which was

"assets-library://asset/asset.JPG?id=60AE4E50-B835-47EB-B896-5974C21F8C9B&ext=JPG";

And using PHAsset on the same photo I get its localIdentifier:

"60AE4E50-B835-47EB-B896-5974C21F8C9B/L0/001" 

So I think you can strip id from url, and find out which asset's local identifier contains it, then you get the PHAsset you want.

0
Yuchao Zhou On

For PHAsset, I use

[[PHImageManager defaultManager] requestImageForAsset:imageAsset
                                               targetSize:targetSize
                                              contentMode:PHImageContentModeAspectFit
                                                  options:options
                                            resultHandler:^(UIImage *img, NSDictionary *info)
{ // code for handle image }];

I tried to use requestImageDataForAsset, but it return nil for streamed photos.

0
Diego Rebosio On

After not finding any answers for this anywhere, I created the following extension to PHAsset which works great as of iOS 8.2 although I assume it's theoretically slow. Even though one of the prior comments says that this is fixed on iOS8.2 beta, the bug was still present for me now that iOS8.2 is released.

import Photos
import UIKit

extension PHAsset {
    class func fetchAssetWithALAssetURL (alURL: NSURL) -> PHAsset? {
        let phPhotoLibrary = PHPhotoLibrary.sharedPhotoLibrary()
        let assetManager = PHImageManager()
        var phAsset : PHAsset?

        let optionsForFetch = PHFetchOptions()
        optionsForFetch.includeHiddenAssets = true

        var fetchResult = PHAsset.fetchAssetsWithALAssetURLs([alURL], options: optionsForFetch)
        if fetchResult?.count > 0 {
            return fetchResult[0] as? PHAsset
        } else {
            var str = alURL.absoluteString!
            let startOfString = advance(find(str, "=")!, 1)
            let endOfString = advance(startOfString, 36)
            let range = Range<String.Index>(start:startOfString, end:endOfString)
            let localIDFragment = str.substringWithRange(range)
            let fetchResultForPhotostream = PHAssetCollection.fetchAssetCollectionsWithType(PHAssetCollectionType.Album, subtype: PHAssetCollectionSubtype.AlbumMyPhotoStream, options: nil)
            if fetchResultForPhotostream?.count > 0 {
                let photostream = fetchResultForPhotostream![0] as PHAssetCollection
                let fetchResultForPhotostreamAssets = PHAsset.fetchAssetsInAssetCollection(photostream, options: optionsForFetch)
                if fetchResultForPhotostreamAssets?.count >= 0 {
                    var stop : Bool = false
                    for var i = 0; i < fetchResultForPhotostreamAssets.count && !stop; i++ {
                        let phAssetBeingCompared = fetchResultForPhotostreamAssets[i] as PHAsset
                        if phAssetBeingCompared.localIdentifier.rangeOfString(localIDFragment, options: nil, range: nil, locale: nil) != nil {
                            phAsset = phAssetBeingCompared
                            stop = true
                        }
                    }
                    return phAsset
                }
            }
            return nil
        }
    }
}