19
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Swift5】FirestoreとFirebase Storageでの画像の扱い方(初学者向け)

Last updated at Posted at 2020-09-23

背景

Firebaseを使用して開発することが多く、画像をどのように扱うかを毎回忘れてしまうので
記事に残すことにした。

画像の扱い方について

Firebaseを用いて画像を扱う場合によく使われる方法が2つあります。

①Storageから直接取得する

保存時の流れ

  1. Storageに画像を保存

取得時の流れ

  1. Storage Refを使用して画像を取得する

メリット

  • Storageにセキュリティールールを設定できるので、セキュリティが高くなる
  • Storage Refで取得できるので実装が簡単にできる

デメリット

  • 画像の一括取得ができないので、リスト表示などをする際は時間がかかってしまう

②DownloadURLをFirestoreへ保存する

保存時の流れ

  1. Storageに画像を保存
  2. 保存した画像のdownloadURLを取得
  3. FirestoreへdownloadURLを保存

取得時の流れ

  1. FirestoreよりdownloadURLを取得する

メリット

  • Firestoreから取得を行うので、一括取得ができる(リスト表示しやすい)
  • URLなのでキャッシュできる

デメリット

  • URLがわかれば誰でもアクセスできるようになる
  • 保存時に何度か通信が行われるのでエラーハンドリングなど含めると実装がすこし複雑になる

今回は②の方の実装について書こうと思います。(エラーハンドリングは除く)

実装

保存時の処理

コメントを入れて解説してるので、少し長くなっていますが
一連の流れは以下になります。

PostViewController.swift

class PostViewController: UIViewController {

// 省略
    
    func saveToFirestore() {
        // nilチェック
        if let title = titleTextField.text,
            let content = contentTextField.text,
            let selectImage = imageView.image {
            // 今日日付をintに変換して被らない名前にする
            let imageName = "\(Date().timeIntervalSince1970).jpg"
            // 今回はpostsというフォルダーの中に画像を保存する
            let reference = Storage.storage().reference().child("posts/\(imageName)")
            // 画像データがそのままだとサイズが大きかったりするので、サイズを調整
            if let imageData = selectImage.jpegData(compressionQuality: 0.8) {
                // メタデータを設定
                let metadata = StorageMetadata()
                metadata.contentType = "image/jpeg"
                // ①ここでstorageへの保存を行う
                reference.putData(imageData, metadata: metadata, completion:{(metadata, error) in
                    if let _ = metadata {
                    // ②storageへの保存が成功した場合はdownloadURLの取得を行う
                    reference.downloadURL{(url,error) in
                            if let downloadUrl = url {
                                // downloadURLの取得が成功した場合
                                // String型へ変換を行う
                                let downloadUrlStr = downloadUrl.absoluteString
                                // ③firestoreへ保存を行う
                                Firestore.firestore().collection("posts").document().setData([
                                    "title": title,
                                    "content": content,
                                    "imageURL": downloadUrlStr,
                                    "createdAt": FieldValue.serverTimestamp()
                                ]){ error in
                                    if let error = error {
                                        // firestoreへ保存が失敗した場合
                                        
                                    } else {
                                        // firestoreへ保存が成功した場合
                                    }
                                }
                            } else {
                                // downloadURLの取得が失敗した場合の処理
                            }
                        }
                    } else {
                        // storageの保存が失敗した場合の処理
                    }
                })
            }
        }
    }
}

上記のように3回非同期の処理が入るので
エラーハンドリングを実装する必要があります。(今回は割愛します)

取得時の処理

投稿データのモデルを以下のように定義

PostData.swift
class PostData: NSObject{
    var id: String
    var title: String?
    var content: String?
    var imageURL: String?
    var date: Date?

    init(document: QueryDocumentSnapshot) {
        self.id = document.documentID

        let postDic = document.data()

        self.title = postDic["title"] as? String

        self.content = postDic["content"] as? String
        
        self.imageURL = postDic["imageURL"] as? String

        let timestamp = postDic["createdAt"] as? Timestamp
        self.date = timestamp?.dateValue()
    }
}
ListViewController.swift

class ListViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
    
    var listData:[PostData] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        // ④Firestoreからデータを取得
        Firestore.firestore().collection("posts").getDocuments{ QuerySnapshot, error in
            if let snapshot = QuerySnapshot {
                listData = snapshot.documents.map { document in
                    let postData = PostData(document: document)
                    return postData
                }
                // tableViewなどに表示する場合はここでreloadDataを呼ぶ
            }
        }
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return listData.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // 画像を表示する場合
        // stringからURL型に変換
        let imageUrl:URL = URL(string: listData[indexPath.row].imageURL as! String)!
        // URL型からData型に変換
        let imageData:Data = try! Data(contentsOf: imageUrl)
        // 画像をセットする
        cell.imageView.image = UIImage(data: imageData)!

        return cell
    }
}

上記のようにFirestoreからURLを一括取得できるので、かなり楽にリスト表示をすることができます。

19
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?