Search code examples
swifturlassetsphasset

Why does the url sometimes come out nil when converting from PHAsset? (Swift)


Had to repost this question because no one answered, it's been 2 weeks, and I am absolutely clueless on why this is happening. I am using the following function to convert a PHAsset to a 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
            }
            options.isNetworkAccessAllowed = 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
            options.isNetworkAccessAllowed = true
            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)
                }
            })
        }
    }
}

However the url ends up coming out nil sometimes causing my app to crash. I have noticed it is more common if I choose a video on the longer side rather than a picture but I can not pin point exactly when it happens. Also, this does not seem like an issue just I am having because on the stack overflow post I got this from, the recent comment asks why the asset is sometimes nil. The post: How to get URL for a PHAsset? [duplicate]

Edit

I changed code to this:

func getURL(completionHandler : @escaping ((_ responseURL : URL?) -> Void)){
        if self.mediaType == .image {
            let options: PHContentEditingInputRequestOptions = PHContentEditingInputRequestOptions()
          //  options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData) -> Bool in
          //      return true
         //   }
           // options.isNetworkAccessAllowed = true
            self.requestContentEditingInput(with: options, completionHandler: {(contentEditingInput: PHContentEditingInput?, info: [AnyHashable : Any]) -> Void in
                if let contentEditingInput = contentEditingInput {
                   completionHandler(contentEditingInput.fullSizeImageURL)
                } else {
                   completionHandler(nil)
                }
            })
        } else if self.mediaType == .video {
            let options: PHVideoRequestOptions = PHVideoRequestOptions()
         //   options.version = .original
        //    options.isNetworkAccessAllowed = true
            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)
                }
            })
        }
    }

But still no luck. My app is not crashing its just every now and then the urls come out to be nil.


Solution

  • self.requestContentEditingInput(with: options, completionHandler: {(contentEditingInput: PHContentEditingInput?, info: [AnyHashable : Any]) -> Void in
                    completionHandler(contentEditingInput!.fullSizeImageURL as URL?)
                })
    

    contentEditingInput is marked as optional, why would anyone implicitly unwrap it is beyond me. To safely unwrap it (which will prevent your app from crashing), do something like this:

    if let contentEditingInput = contentEditingInput {
       completionHandler(contentEditingInput.fullSizeImageURL)
    } else {
       completionHandler(nil)
    }
    

    You do not need as URL? because, documentation:

    var fullSizeImageURL: URL? { get }
    

    Trying commenting out the line where you set the image options. I have an app that uses this API working fine, but I do not have those options set. This option could be the cause why contentEditingInput is nil.

    Edit - getting URL of image or movie from Photo Library using UIImagePickerController

    import MobileCoreServices
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            guard let mediaType = info[UIImagePickerController.InfoKey.mediaType] as? String,
                  mediaType == (kUTTypeMovie as String) else { return }
            let videoURL = info[.mediaURL] as? URL
        }
    

    Above is the way I used to get videoURL, below is for image.

    guard let mediaType = info[UIImagePickerController.InfoKey.mediaType] as? String,
                      mediaType == (kUTTypeImage as String) else { return }
    let photoURL = info[.imageURL] as? URL