『入る学科間違えた高専生』の日記

プログラミングのコードを書いたりする予定です。あとは日記等。あといつまで高専生やねん

UIImagePickerControllerのカメラで撮影した画像からPHAssetを取得する

はじめに

UIImagePickerController のカメラで撮影してすぐに UIImageWriteToSavedPhotosAlbum(_:_:_:_:) で保存して PHAsset を取得したいなって思ってやってみたときの失敗談です.

結論から書くと, UIImagePickerControllerDelegateimagePickerController(_:didFinishPickingMediaWithInfo:) の info の中身をみたり UIImageWriteToSavedPhotosAlbum(_:_:_:_:) などで引数に渡ってくる値からは PHAsset を取得することが無理なのではないかと思います.

実際に取得する

Info.plistPrivacy - Photo Library Additions Usage Description の設定が必要です

final class ViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let controller = UIImagePickerController()
        controller.sourceType = .camera
        controller.allowsEditing = false
        controller.delegate = self
        present(controller, animated: true)
    }
}

extension ViewController: UIImagePickerControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController,
                               didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
        defer { picker.dismiss(animated: true) }
        guard let image = info[.originalImage] as? UIImage else { return }
        // ここから書いていく
    }

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {}
}

extension ViewController: UINavigationControllerDelegate {}

とりあえずこんな感じのコードを用意しました.

実際に画像を保存する処理は PhotoKit を扱うことになります.

import Photos
・・・
extension ViewController: UIImagePickerControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController,
                               didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
        defer { picker.dismiss(animated: true) }
        guard let image = info[.originalImage] as? UIImage else { return }
        // ここから書いていく
        do {
            var localIdentifier: String?
            try PHPhotoLibrary.shared().performChangesAndWait {
                let changeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
                localIdentifier = changeRequest.placeholderForCreatedAsset?.localIdentifier
            }
            guard let identifier = localIdentifier,
                  let asset = PHAsset.fetchAssets(withLocalIdentifiers: [identifier], options: nil).firstObject else {
                fatalError("取得に失敗しました")
                return
            }
            print(asset)
        } catch {
            fatalError(error.localizedDescription)
        }
    }
}

というような感じになりました.

できるんじゃね?ってやったこと

PHAsset に含まれる localIdentifier さえ取得できれば PHAsset のクラスメソッドである fetchAssets(withLocalIdentifiers:options:) を呼び出せば取得できると思いました.
一応それっぽい値は imagePickerController(_:didFinishPickingMediaWithInfo:) の引数 info から発見できたものの保存のライフサイクルと上記で示したクラスメソッドの実行をタイミング合わせないといけないのでちょっと面倒だなって思って検証自体をやめた.
そもそも辞書に絶対同じ値が入ってくるのは信用できないのでやめました.

最後に

今回は, UIImagePickerController で取得した画像を PhotoKit を使ってフォトライブラリに保存して PHAsset を取得するサンプルでしたが,実際には UIImage であればなんでも同じようなことができます.

雑な記事になりましたが以上です.