How to get an ALAsset URL from a PHAsset?
Asked Answered
S

5

23

You can do it sneakily† using the undocumented PHAsset.ALAssetURL property, but I'm looking for something documented.


† In Objective-C, this will help

@interface PHAsset (Sneaky)

@property (nonatomic, readonly) NSURL *ALAssetURL;

@end
Spae answered 5/3, 2015 at 21:2 Comment(0)
T
47

Create the assetURL by leveraging the localidentifier of the PHAsset. Example: PHAsset.localidentifier returns 91B1C271-C617-49CE-A074-E391BA7F843F/L0/001

Now take the 32 first characters to build the assetURL, like:

assets-library://asset/asset.JPG?id=91B1C271-C617-49CE-A074-E391BA7F843F&ext=JPG

You might change the extension JPG depending on the UTI of the asset (requestImageDataForAsset returns the UTI), but in my testing the extensions of the assetURL seems to be ignored anyhow.

Thordia answered 6/3, 2015 at 14:0 Comment(12)
Good one. Is this relationship documented? If not, where/how did you discover this? Did you just notice the GUID strings were the same?Spae
Not documented, I saw that the GUID strings are the same and it makes sense as both the PhotoKit and AssetsLibrary work with the same CoreData objects under the hood - so this is just the UUID of the coredata object. I would prefer this method to using private API calls.Thordia
Well, props. I'll hold a bit on the green check for a documented response, since that's what I asked for, but this has me much relieved. Big thanks.Spae
I doubt that apple will provide any documented way to bridge from PhotoKit to AssetsLibrary (only vice versa). AssetsLibrary is legacy and they want developers to move to the new API.Thordia
There is a one moment which prevents me to use this approach. It is an asset extension. Camera Roll can contain not only JPG images and not only MOV movies.Gurgle
Doesn't this assume that the PHAsset is a local asset ?Madoc
@PavelOsipov Doesn't this answer already account for the UTI? Regardless, I'm thinking PHAssetMediaTypeVideo would map to &ext=MOV, or something like that.Spae
It's ridiculous that we have to do this shit just to get a URL.Armendariz
Make sure to remove the "/L0/001" from the localIdentifier or this will fail every time.Armendariz
The proper way to get the GUID from the localIdentifier is to split on the first /. (Some GUIDs are longer than 32 characters.) If anybody know of a better way of getting an asset URL, please post here. This is hacky and prone to breaking with future updates.Schmid
DO NOT use this approach. It's hack and undocumented, Apple may change the rule of composing assets URL at anytime. Besides, you can't handle some edge situations like undownloaded resources. I gave it a try before, then some users reported they can't see some images from their album. I saw a lot of error reports in my crash reporter saids AssetsLibrary can't find image at URL: assets-library://xxx, cost me a lot of time to figure it out. A better approach is just extract the PHAsset to local file system and refactor your code to use file system URLs, or just migrate to PHAsset.Equilateral
I get the error "Value of type 'PHAsset?' has no member 'localidentifier'"Locomobile
Y
9

I wanted to be able to get a URL for an asset too. However, I have realised that the localIdentifier can be persisted instead and used to recover the PHAsset.

PHAsset* asset = [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil].firstObject;

Legacy asset URLs can be converted using:

PHAsset* legacyAsset = [PHAsset fetchAssetsWithALAssetUrls:@[assetUrl] options:nil].firstObject;
NSString* convertedIdentifier = legacyAsset.localIdentifier;

(before that method gets obsoleted...)

(Thanks holtmann - localIdentifier is hidden away in PHObject.)

Yellowbird answered 19/5, 2015 at 5:39 Comment(3)
What you are saying may be true, but it seems only obliquely related to the actual (I thought, clearly stated) question.Spae
@Clay Bridges True. I guess I had a realisation that helped me solve my problem, and added an answer without re-checking the question. Sorry.Yellowbird
I find creating a separate self-answered question a better format for any such realizations you make. I've done it a lot, and despite what they tell you, haven't gone blind yet. :) You can even link to this question.Spae
A
2

Here is working code tested on iOS 11 both simulator and device

PHFetchResult *result = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil];
    [result enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        PHAsset *asset = (PHAsset *)obj;
        [asset requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) {
            NSLog(@"URL:%@",  contentEditingInput.fullSizeImageURL.absoluteString);
            NSString* path = [contentEditingInput.fullSizeImageURL.absoluteString substringFromIndex:7];//screw all the crap of file://
            NSFileManager *fileManager = [NSFileManager defaultManager];
            BOOL isExist = [fileManager fileExistsAtPath:path];
            if (isExist)
                NSLog(@"oh yeah");
            else {
                NSLog(@"damn");
            }
        }];
    }];
Arenaceous answered 14/11, 2017 at 13:18 Comment(1)
Is there a way to get video urlFlyspeck
A
2

Read the bottom!

The resultHandler for PHImageManager.requestImage returns 2 objects: result and info.

You can get the original filename for the PHAsset (like IMG_1043.JPG) as well as its full path on the filesystem with:

let url = info?["PHImageFileURLKey"] as! URL

This should work right, but for some reason it doesn't. So basically, you have to copy your image to a file then access that then delete it.

The PHImageFileURLKey is usable to get the original file name, but you cannot actually access that file. It probably has to do with the fact that code in the background can access the file while other apps can delete it.

Antipodes answered 7/10, 2018 at 20:0 Comment(0)
G
1

Here is a PHAsset extension written in Swift that will retrieve the URL.

extension PHAsset {

    func getURL(completionHandler : @escaping ((_ responseURL : URL?) -> Void)){
        if self.mediaType == .image {
            let options: PHContentEditingInputRequestOptions = PHContentEditingInputRequestOptions()
            options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData) -> Bool in
                return true
            }
            self.requestContentEditingInput(with: options, completionHandler: {(contentEditingInput: PHContentEditingInput?, info: [AnyHashable : Any]) -> Void in
                completionHandler(contentEditingInput!.fullSizeImageURL as URL?)
            })
        } else if self.mediaType == .video {
            let options: PHVideoRequestOptions = PHVideoRequestOptions()
            options.version = .original
            PHImageManager.default().requestAVAsset(forVideo: self, options: options, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [AnyHashable : Any]?) -> Void in
                if let urlAsset = asset as? AVURLAsset {
                    let localVideoUrl: URL = urlAsset.url as URL
                    completionHandler(localVideoUrl)
                } else {
                    completionHandler(nil)
                }
            })
        }
    }
}
Goosestep answered 16/10, 2019 at 18:31 Comment(1)
Please give example of uses.Monolith

© 2022 - 2024 — McMap. All rights reserved.