0

I'm trying to get the asset resources for a PHAsset and am having a little trouble. I'm able to fetch the PHAsset with no problems, yet the resources for the asset is always empty. This is not happening with every photo, but one in particular has been giving me troubles. Curious if there's something I'm missing and this is expected behaviour in certain contexts.

import UIKit
import Photos
import PhotosUI

class ViewController: UIViewController {
    @IBOutlet weak var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        requestPhotoLibraryAuthorization() { success in
            let identifier = "E218D471-E616-4E39-99C3-624D13D9B661/L0/001"
            let assets = self.fetchPHAssets(identifiers: [identifier])
            guard assets.count > 0 else { return }

            let imageOptions = PHImageRequestOptions()
            imageOptions.deliveryMode = .highQualityFormat
            imageOptions.isNetworkAccessAllowed = true

            PHImageManager.default().requestImage(for: assets[0], targetSize: PHImageManagerMaximumSize, contentMode: .default, options: imageOptions) { (image, info) in
                print(String(describing: info)) // never called
            }

            self.saveAssetToDisk(asset: assets[0]) { image in
                guard let image = image else { return }
                self.imageView.image = image
            }
        }
    }

    func fetchPHAssets(identifiers: [String]) -> [PHAsset] {
        let options = PHFetchOptions()
        options.includeAllBurstAssets = false
        options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        options.fetchLimit = identifiers.count
        options.includeAssetSourceTypes = [.typeCloudShared, .typeiTunesSynced, .typeUserLibrary]

        let assetResult = PHAsset.fetchAssets(withLocalIdentifiers: identifiers, options: options)
        var assetArray:[PHAsset] = []
        for i in 0..<assetResult.count {
            let asset = assetResult[i]
            assetArray.append(asset)
        }
        return assetArray
    }

    func saveAssetToDisk(asset: PHAsset, onComplete: @escaping (UIImage?)->()) {
        let resources = PHAssetResource.assetResources(for: asset)

        guard resources.count > 0 else { return }

        let resource = resources[0]

        let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let documentUrl = urls[0]
        let resourceUrl = documentUrl.appendingPathComponent(resource.originalFilename, isDirectory: false)

        // write image
        let options = PHAssetResourceRequestOptions()
        options.isNetworkAccessAllowed = true

        if !FileManager.default.fileExists(atPath: resourceUrl.path) {
            PHAssetResourceManager.default().writeData(for: resource, toFile: resourceUrl, options: options) { error in
                if error == nil {
                    guard let image = UIImage(contentsOfFile: resourceUrl.path) else { return }
                    onComplete(image)
                }
            }
        } else {
            onComplete(UIImage(contentsOfFile: resourceUrl.path))
        }
    }

    let requestPhotoLibraryKey = "hasRequestedPhotoAccess"
    func requestPhotoLibraryAuthorization(onComplete: @escaping (Bool) -> ()) {
        let status = PHPhotoLibrary.authorizationStatus()
        if status == .notDetermined {
            PHPhotoLibrary.requestAuthorization { status in
                if status == PHAuthorizationStatus.authorized {
                    UserDefaults.standard.set(true, forKey: self.requestPhotoLibraryKey)
                    onComplete(true)
                }
            }
        } else if status == .authorized {
            UserDefaults.standard.set(true, forKey: requestPhotoLibraryKey)
            onComplete(true)
        } else if status == .denied {
            onComplete(false)
        }
    }
}
Chris Mills
  • 89
  • 1
  • 6
  • try to use PHImageManager.default().requestImageData instead of PHAssetResource.assetResources – A.Munzer Feb 17 '19 at 08:46
  • Data is nil in the results handler when I do that. Thanks for the suggestion. – Chris Mills Feb 17 '19 at 20:53
  • `PHImageManager.default().requestImageData(for: self` makes no sense, since `self` is not a PHAsset. Are you showing _real_ code? Real code that you've actually tested and run in some meaningful context? Please show _real_ code in its _real_ context — not some snippets. – matt Feb 25 '19 at 00:46
  • Made a sample project after grabbing the localIdentifer that's causing the issue. The original was from an extension that is part of a released app, so yes it has been tested. – Chris Mills Feb 25 '19 at 07:52

1 Answers1

0

Was not seeing the error message describe in Photos Framework requestImageDataForAsset occasionally fails, but I was able to track down that the image in question was apart of an iCloud shared album. Doing as they suggests fixed my issue.

Chris Mills
  • 89
  • 1
  • 6