🐷

Swift Realmで画像の保存・表示・削除

2021/02/13に公開
1

Realmを使って画像を保存する2つの方法

Realmには、Image型をそのまま保存できないため、別の何らかの形にして保存してあげる必要があります。

1.ImageをNSData型に変更し保存する

こちらの記事が参考になります。
https://qiita.com/_ha1f/items/593ca4f9c97ae697fc75
しかし、私はこちらの記事通りに行っても上手くいきませんでした。(原因は不明...)

2.画像をDocumentフォルダに保存し、RealmにはそのファイルURLを保存する

私はこの方法で上手く行きました。そのため、本記事もこの方法を下に実装しています。
stackoverflowを読み漁ったところ、こちらの手段のほうがベストプラクティスだとか。
主にこちらの記事を参考に実装しました。Documentフォルダについての説明もされているので、一度目を通しておくとよいと思います。
https://qiita.com/ryomaDsakamoto/items/ab8e5cd9da1ea70f3302

Realmに画像を保存する方法

hogeImageViewに表示されている画像を保存します
tableの部分を任意のテーブル名に変えてください

import UIKit
import RealmSwift

class AddImageViewController: UIViewController {
    @IBOutlet weak var hogeImageView: UIImageView!
    
    let realm = try! Realm()
    
    // ドキュメントディレクトリの「ファイルURL」(URL型)定義
    var documentDirectoryFileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]

    // ドキュメントディレクトリの「パス」(String型)定義
    let filePath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
   
    override func viewDidLoad() {
        super.viewDidLoad()
      
    }
    
    @IBAction func addImage(_ sender: UITextField) {
        //Realmのテーブルをインスタンス化
        let table = Table()
        do{
            try table.imageURL = directory.documentDirectoryFileURL.absoluteString
        }catch{
            print("画像の保存に失敗しました")
        }
        try! realm.write{realm.add(table)}
    
        
    }
    
    //保存するためのパスを作成する
    func createLocalDataFile() {
        // 作成するテキストファイルの名前
        let fileName = "\(NSUUID().uuidString).png"

        // DocumentディレクトリのfileURLを取得
        if documentDirectoryFileURL != nil {
            // ディレクトリのパスにファイル名をつなげてファイルのフルパスを作る
            let path = documentDirectoryFileURL.appendingPathComponent(fileName)
            documentDirectoryFileURL = path
        }
    }
    
    //画像を保存する関数の部分
    func saveImage() {
        createLocalDataFile()
        //pngで保存する場合
        let pngImageData = hogeImageView.image?.pngData()
        do {
            try pngImageData!.write(to: documentDirectoryFileURL)
        } catch {
            //エラー処理
            print("エラー")
        }
    }

}

これで画像の保存ができます
一部コード解説

try table.imageURL = directory.documentDirectoryFileURL.absoluteString

RealmにはURL型を保存できないため、String型に変更する必要があります

let fileName = "\(NSUUID().uuidString).png"

NSUUID().uuidStringは任意の文字列を生成してくれます

Realmから画像を表示する方法

今、TableのimageURLカラムには、画像の保存先のファイルURLがString型に保存されています

@IBOutlet weak var showImageView: UIImageView!

let realm = try! Realm()
tableData = realm.objects(Table.self)
//URL型にキャスト
let fileURL = URL(string: tableData[0].imageURL)
//パス型に変換
let filePath = fileURL?.path       
showImageView.image = UIImage(contentsOfFile: filePath!)

UIImage(contentsOfFile:)の引数は パスである必要があるので注意
ファイルURLとパスの違いについては以下のサイトを参考にしてください
http://galakutaapp.blogspot.com/2017/11/pathurl.html

画像の削除について

let realm = try! Realm()
            
//先程同様にパスを取得
let fileURL = URL(string: tableData[0].imageURL)
let filePath = fileURL?.path
//ファイルの削除
if filePath != nil{
    try? FileManager.default.removeItem(atPath: filePath!)
}
try! realm.write{
    realm.delete(self.tableData[dataNumber])
}

Documantsフォルダのファイルの削除はこの部分でしています

try! FileManager.default.removeItem(atPath: filePath!)

//ファイルの操作について以下のサイトを参考にしてください
https://qiita.com/nomunomu0504/items/69632904303cf6b762dd

あとがき

調べても古い記事しか出てこず、実装に苦労しました。
この記事がSwiftを学習中の方に役立てばと思います。
不明点あればできる限り返答しますので、気軽にコメントください。

Discussion

K SK S

mikesanさん、はじめまして。
コメントありがとうございます。

私もmikesanさんと同様の現象が発生したと記憶しております。
残念ですが、解決に至ることができませんでした。
おそらく、シュミレーターを再起動することによって、何らかのデータの不整合が起きてしまっているものだと推測しています。
シュミレーターの仕様もしくは不具合だと割り切って問題ないと思います。(画像をその都度挿入し直すのが、手間ですが...)
お力になれず、申し訳ないです。
私の本番環境(リリース後にアップルストアからダウンロードしたもの)では、問題なく動いていますのでご安心いただけると幸いです。