2

I want to save images in my iOS app in Core Data, but all of the images I want to save are being imported from the photo library. So it would make way more sense to save the already existing reference to that photo from the photo library rather than try convert the image to NSData and store it in the phone. Especially because images take up a lot of memory and ideally they shouldn't be saved in two places. Is what I'm trying to do possible, or will apple not let me read references to where photos from the photo library are stored?

Also if it's possible to grab the reference to where an image is stored from the photo library, how can I access it in Swift?

Kyle Somers
  • 614
  • 1
  • 8
  • 21

2 Answers2

2

the localIdentifier property of the PHObject class is A unique string that persistently identifies the object. Meanwhile you can use it for the fetchAssetsWithLocalIdentifiers: method for the PHAsset that can fetch the image data.

justin
  • 46
  • 6
1

This is a late answer, but hopefully it will help someone else.

Yes, it is possible. You can select photos using a PHPickerViewController to get a PHPickerResult, which has a assetIdentifier attribute. The assetIdentifier persistently and uniquely identifies the photo. When you want to load the image, read the saved assetIdentifier and use PHAsset.fetchAssets() to retrieve the asset and then use PHImageManager.requestImage() to get the UIImage. In this way, all that you need to store in your app is the assetIdentifier, not the image data.

Here is some example code (for iOS15) for selecting one photo and getting the assetIdentifier:

import PhotosUI
import SwiftUI

struct PhotoPicker: UIViewControllerRepresentable {
    @Environment(\.dismiss) var dismiss
    @Binding var assetIdentifier: String?

    typealias UIViewControllerType = PHPickerViewController
    func makeUIViewController(context: Context) -> PHPickerViewController {
        var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared())
        configuration.selectionLimit = 1
        configuration.filter = .images

        let picker = PHPickerViewController(configuration: configuration)
        picker.delegate = context.coordinator

        return picker
    }

    func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
        // Do nothing.
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, UINavigationControllerDelegate, PHPickerViewControllerDelegate {
        let parent: PhotoPicker

        init(_ parent: PhotoPicker) {
            self.parent = parent
        }

        func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
            parent.dismiss()

            // Only 1 image can be selected, so ignore any additional elements.
            guard let result = results.first else {
                return
            }

            parent.assetIdentifier = result.assetIdentifier
        }
    }
}

To use it: PhotoPicker(assetIdentifier: $assetIdentifier)

And then to retrieve the image from the assetIdentifier use:

import Photos
import SwiftUI

struct LoadedImageView: View {
    @State var assetIdentifier: String
    @State private var image: Image? = nil

    var body: some View {
        VStack {
            if let image = image {
                image
                    .resizable()
                    .aspectRatio(contentMode: .fit)
            }
        }
        .onAppear(perform: loadImage)
    }

    func loadImage() {
        let fetchResults: PHFetchResult<PHAsset> =
            PHAsset.fetchAssets(withLocalIdentifiers: [assetIdentifier], options: nil)

        guard let asset: PHAsset = fetchResults.firstObject else {
            return
        }

        let manager = PHImageManager()
        manager.requestImage(for: asset, targetSize: PHImageManagerMaximumSize,
                                contentMode: .aspectFit, options: nil) { (uiImage, _) in
            if let uiImage = uiImage {
                self.image = Image(uiImage: uiImage)
            }
        }
    }
}
Andrew
  • 904
  • 5
  • 17
  • The example works fine if the user has set full photo library access for the app. In limited mode it does not work if the picked photo is not in the current limited photo list. And obviously photo library access denied fails too. – Will Loew-Blosser Jul 20 '22 at 21:49