画像をカメラロールに保存する【Swift5】

はじめに

本記事では、画像をiPhoneの”写真”アプリのカメラロールに保存する方法を紹介します。
単純にUIImageを保存する方法から、exif情報などを保持したまま保存する手順についても説明します。

UIImageの保存

カメラロールにUIImageを保存するシンプルな方法は、UIImageWriteToSavedPhotosAlbum(_:_:_:_:)を使用することです。

func UIImageWriteToSavedPhotosAlbum(
        _ image: UIImage,
        _ completionTarget: Any?,
        _ completionSelector: Selector?,
        _ contextInfo: UnsafeMutableRawPointer?
    )

保存するUIImageを引数に取り、カメラロールに保存が成功したときの処理を完了ハンドラに記述できます。

JPEG画像データの保存

画像データをUIImageに変換すると、EXIF情報や位置情報が失われてしまいます。
これらの情報を保持したままカメラロールに保存するには、JPEGデータとして保存する必要があります。

JPEGデータをカメラロールに保存するにはiOS標準のPhotosライブラリを利用します。
以下に画像ファイルをダウンロードしてきてから、カメラロールに保存するまでの一例を示します。
1. “写真”へのアクセス許可を求める
2. 画像データを適当なデータ形式に変換する (EXIFが落ちないように)
3. 変換した画像データをカメラロールに保存する
の流れです。

import Photos
// ...
// "写真"へのアクセス許可を求める
// for引数には.addOnlyと.readWriteを指定できる
PHPhotoLibrary.requestAuthorization(for: .addOnly) { status in
    switch status {
    case .authorized:
        // "写真"へのアクセスが許可されたとき
        // 画像ファイルをダウンロードしてきたと仮定する
        FileDownLoader.shared.download { downloadFile in
            // ダウンロードしたファイルをCIImageに変換する
            let ciImage = CIImage(contentsOf: downloadFile)
            // jpegデータに変換
            let jpegData = CIContext().jpegRepresentation(of: ciImage,
                                                          colorSpace: ciImage.colorSpace ?? CGColorSpaceCreateDeviceRGB(),
                                                          options: [:])
            //  カメラロールへ保存
            PhotoLibrary.shared().performChanges {
                let request = PHAssetCreateRequest.forAsset()
                request.addResource(with: .photo, data: jpegData, option: nil)
            } comlpetionHandler: { success, error in
                if success {
                    // 保存成功
                }
                if let error = error {
                    // エラー
                }
            }
        }
    case .denied:
        // 拒否されたとき
    case .notDetermined:
        // 未決定状態
    case .restricted:
        // ユーザーでは許可できないようなアクセス制御がかかっている場合
    case .limited:
        // 一部の写真を選択できるpresentLimitedLibraryPicker(from:)もしくはpresentLimitedLibraryPicker(from:completionHandler:)を使用する場合
    }
}

⚠️ 上記の例では、ダウンロードしてきたファイルをJPEGデータに変換するために、一度CIImageに変換していますが、CIImageに変換してもEXIF情報などは失われません。

Deprecated対応

前述したコードにあると似たメソッドとしてrequestAuthorization(_:)がありますが、iOS14以降ではdeprecatedとなっています。
iOS14以降では、requestAuthorization(for:handler:)の使用が推奨されています。
大した違いはなく、for引数に.addOnlyもしくは.readWriteを指定できるようになりました。
これは写真をカメラロールに「追加するのみ」か、カメラロールの写真を「読み込む」かを指定できるようになり、これによって”写真”へのアクセス許可を求めるアラートの中身が変わるようです。
あまり難しく考えず、iOS14以前のバージョンをサポートしている場合は、以下のように記述しておけば問題ないです。コードは冗長になりますが、、、

if #available(iOS14, *) {
        PHPhotoLibrary.requestAuthorization(for: .addOnly) { status in
            // ...
        }
    } else {
        PHPhotoLibrary.requestAuthorization { status in
            // ...
        }
    }

おわりに

本記事では、EXIF情報や位置情報を保持したまま画像データをカメラロールに保存する方法についてまとめました。画像データはさまざまな形で取得してくると思いますので、都度JPEGデータに上手く変換してあげる必要があります。
しかし、 Photosライブラリに用意されているメソッドを使用すれば、ほとんどの場合安全にカメラロールに保存できます🤘

参考文献

この記事は以下の情報を参考にしました。

最新情報をチェックしよう!