【Swift UI/Firebase】Cloud Storageの実装方法!画像や動画をサーバー管理

この記事からわかること

  • Swift/Firebase作成したiOSアプリCloud Storage導入する方法
  • Swift UICocoa Pods使用している場合のインストール方法
  • 画像動画サーバーアップロード/ダウンロードするには?
  • クラウドバケット作成方法
  • パーミッションルールとは?

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

Firebase自体の概要や登録方法については下記記事を参考にしてください。

Cloud Storageとは?

公式リファレンス:Cloud Storage

Cloud Storage for Firebaseとはユーザーがアプリケーションを介してファイルを安全にアップロード/ダウンロードできるクラウドストレージサービスです。サーバーにファイルを保存できることで異なるデバイスやプラットフォーム間でファイルを共有することが可能になります。

アップロードできるファイルの種類は画像、動画、音声、テキストファイルなど、あらゆるファイルの形式をサポートしています。自身のサーバーを使用するより、豊富で簡潔なAPIを利用して強固なセキュリティと高いスケーラビリティが確保されているのが大きなメリットになります。

また時間のかかるアップロードやダウンロードがネットワークの環境などにより、停止してしまっても途中から再開することが可能になっているため、速度と帯域幅を節約することができるようです。

無料の使用枠

「Cloud Storage」も無料プラン(Spark プラン)で利用することが可能です。その場合は「最大容量5GB」に制限されているようです。5GB(=5120MB)あれば圧縮して容量を節約した画像(1枚1MBと仮定)だとすると約4000枚〜5000枚程度は保存できるかと思います。

高品質を保って画像でも5MBくらいだと思うので最低約1000枚程度は保存できると思います。

【Swift UI/Firebase】Cloud Storageの実装方法!画像や動画をサーバー管理

公式リファレンス:料金プラン

iOSアプリにCloud Storageを導入する流れ

流れ

  1. Firebaseプロジェクトを作成
  2. iOSアプリの登録
  3. GoogleService-Info.plistの追加
  4. SDKの導入
  5. 初期化コードの組み込み
  6. Cloud Storageバケットを作成
  7. 完了

ここでは通常の「Firebaseプロジェクトを作成」や「iOSアプリの登録」などの手順は割愛しています。詳細は以下の記事を参考にしてください。

【Swift/Xcode】Firebaseの導入方法!iOSアプリでの使い方

Cloud Storage SDKの導入

Cocoa Pods」を使用して「Cloud Storage SDK」を導入していきます。「PodFile」に以下の一文を追記してpod installを実行します。

pod 'FirebaseStorage'

おすすめ記事:【Swift UI】CocoaPodsのインストール方法と使い方!

初期化コードの組み込み

最後にアプリのエントリポイント部分に初期化のためのコードを記述していきます。

import SwiftUI
import FirebaseCore // 追加

// 追加
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication,
                    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        FirebaseApp.configure()
        return true
    }
}

@main
struct TestFirebaseApp: App {
    // 追加
    @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Cloud Storageバケットを作成

続いて実際にアップロード先となる「Cloud Storageバケット」を作成していきます。Firebaseにログインして「Storage」>「始める」をクリックします。

【Swift UI/Firebase】Cloud Storageの実装方法!画像や動画をサーバー管理

クラウドストレージのセキュリティルールは本番環境であればロックモードで、練習であればテストモードにチェックを入れて「次へ」をクリックします。

【Swift UI/Firebase】Cloud Storageの実装方法!画像や動画をサーバー管理

テスト段階でロックモードを指定した場合はクライアントがデータの読み取り/書き取りができない状態になっているので「ルール」のfalseをtrueに変更しておきます。(※本番環境実際に使用する場合は明確に制限を設けるようにしてください。)

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if true;
    }
  }
}

詳細なセキュリティルールは公式サイトを参考にしてください。

公式リファレンス:Firebase Cloud Storage セキュリティ ルールで条件を使用する

続いてロケーションを問われるので「東京(asia-northeast1)」にして進めていきます。(※大阪(asia-northeast2)でも良いかもです。)

【Swift UI/Firebase】Cloud Storageの実装方法!画像や動画をサーバー管理

以下のような画面にきりかわったらバケットの作成は完了です。ファイルがアップロードされるとこの画面からファイルを確認することができるようになります。またここから手動でアップロードすることも可能になっています。

【Swift UI/Firebase】Cloud Storageの実装方法!画像や動画をサーバー管理

また上部にあるgs:/で始まるパスがCloud Storageのルートパスになります。このディレクトリの配下にフォルダなどを作成し、実際に画像などのファイルが格納されていきます。httpとかではないので注意してください。

これで指定のiOSアプリからCloud Storageを使用できるようにすることができました。

Cloud Storageの使い方

ここからはiOSアプリからCloud Storageを操作する方法をまとめていきます。

上部にimport文が必要になるので忘れずに記述しておいてください。

import FirebaseStorage

クラウドバケットの参照を取得する

Cloud Storageを使用するためには作成したクラウドバケットの参照を取得する必要があります。Realtime Databaseを利用したことがある人はほぼ同じ操作方法なのでわかりやすいと思います。

// ストレージサービスへの参照を取得
let storage: Storage = Storage.storage()

// ストレージ参照を作成
let storageRef: StorageReference = storage.reference()
print(reference.bucket) // [プロジェクト名].appspot.com

storageRefに格納されたStorageReference型はfullPathnamebucketプロパティなどを持っており、その参照の情報を取得することができます。例えばstorage.reference()bucketプロパティを参照することで表示されていたバケットのURLを取得することが可能です。

公式リファレンス:Apple プラットフォームで Cloud Storage 参照を作成する

子供や親の階層を参照する

バケット内が参照できるようになったのでバケット内の階層の参照を取得する方法も見ていきます。まだ画像などが何もアップロードされていないので掴みにくいかもですが、バケット内は通常のPCなどと同じツリー型のフォルダ構造で管理されます。StorageReferenceから親や子を参照するためのメソッドが用意されています。

// 1つ上の階層(親)の参照
reference.parent()

// 1つ下のimagesディレクトリの中のsample.jpgの参照
let imagesRef = storageRef.child("images")
let sampleImgRef = imagesRef.child("sample.jpg")

// 1つ下のimagesディレクトリの中のsample.jpgの参照
var sampleImgRef = storageRef.child("images/sample.jpg")

// ルートの参照
let rootRef = sampleImgRef.root()

// フルパスを指定しても参照可能
let storagePath = "\(your_firebase_storage_bucket)/images/sample.jpg"
let imagesRef = storage.reference(forURL: storagePath)

childメソッドは引数に渡した名前に一致する子供の参照を取得します。名前にはimages/sample.jpgのようにパスを指定することもできるので使いやすい方を使用すればOKです。

参照名の制約

参照名(ディレクトリやファイル名など)に指定できる文字列には制約が設けられています

画像ファイルをアップロードする

ファイルをアップロードする方法は以下の手順になります。

  1. 参照を作成する
  2. ファイルをアップロード

1.参照を作成する

参照を作成する方法は先ほどのchildメソッドを使用して希望の階層参照を構築するだけです。

let sampleImgRef = reference.child("sample.jpg")

2.ファイルをアップロード

ファイルをアップロードする方法は以下の2パターン用意されています。

  1. Data型を使用する
  2. URL(パス)を使用する

Data型を使用してアップロード

1つ目は画像ファイルをSwift内でData型に変換してアップロードする方法です。UIImage型で取得した画像データをjpegDatapngDataメソッドを使用してData型に変換しputDataメソッドでアップロードを実行します。

let sampleImgRef = reference.child("sample.jpg")
// 50%に圧縮してData型に変換する
guard let data = image.jpegData(compressionQuality: 0.5) else { return }

let uploadTask = sampleImgRef.putData(data) { metadata, error in
    print(error) // パーミッションエラーなどが発生すれば取得できる
    guard let metadata = metadata else { return }
    // メタデータの取得
    let size = metadata.size
    // 成功すればサイズを取得できる
    print(size)
}

メタ情報の取得が必要なければcompletionHandlerのないputDataメソッドも用意されているのでそちらを使用するとコードがスッキリします。

let uploadTask = sampleImgRef.putData(data)

URL(パス)を使用してアップロード

対象の画像をData型に変換しなくとも端末内に保存されているパスが取得できればそのパスを使用してアップロードすることも可能です。パスはURL型でputFileメソッドの引数に渡します。

let sampleImgRef = reference.child("sample.jpg")
// path;ローカルに保存されている画像のパス
guard let url = URL(string: path) else { return }

let uploadTask = sampleImgRef.putFile(from: url) { metadata, error in
    print(error) // パーミッションエラーなどが発生すれば取得できる
    guard let metadata = metadata else { return }
    // 成功すればサイズを取得できる
    let size = metadata.size
    print(size)
}

アップロードタスクを管理する

putDataputFileメソッドの返り値はStorageUploadTask型になっており、時間のかかるアップロード処理をタスクとして操作することが可能です。


@objc(putFile:metadata:completion:) @discardableResult
open func putFile(from fileURL: URL,
                  metadata: StorageMetadata? = nil,
                  completion: ((_: StorageMetadata?, _: Error?) -> Void)?) -> StorageUploadTask {}

例えば一時停止や再開、キャンセルなどが可能になっています。

// アップロード開始
let uploadTask = sampleImgRef.putFile(from: path)

// 一時停止
uploadTask.pause()

// 再開
uploadTask.resume()

// キャンセル
uploadTask.cancel()

アップロードタスクを観測する

アップロードタスクの状態はobserveメソッドを使用して観測することができます。例えば進捗状況をプログレス値で取得するには以下のように実装します。

let observer = uploadTask.observe(.progress) { snapshot in
    if let progress = snapshot.progress {
        let percentComplete = 100.0 * Double(progress.completedUnitCount) / Double(progress.totalUnitCount)
        print("Upload progress: \(percentComplete)%")
    }
}

observeメソッドの引数にはStorageTaskStatus型で観測したい状態を指定します。

@objc(FIRStorageTaskStatus) public enum StorageTaskStatus: Int {
  case unknown
  case resume
  case progress
  case pause
  case success
  case failure
}

成功や失敗を観測したい場合は以下のように実装することができます。

// 成功
let observer = uploadTask.observe(.success) { snapshot in
    print("アップロード成功")
}

// 失敗
let observer = uploadTask.observe(.failure) { snapshot in
    if let error = snapshot.error as? NSError {
        switch (StorageErrorCode(rawValue: error.code)!) {
        case .objectNotFound:
            break
        case .unauthorized:
            break
        case .cancelled:
            break
        case .unknown:
            break
        default:
            break
        }
    }
}

画像ファイルをダウンロードする

ファイルをダウンロードする方法もアップロードと同じく以下の手順になります。

  1. 参照を作成する
  2. ファイルをダウンロード

1.参照を作成する

参照を作成する方法は先ほどのchildメソッドを使用して希望の階層参照を構築するだけです。

let sampleImgRef = reference.child("sample.jpg")

2.ファイルをダウンロード

ファイルをダウンロードする方法は以下の3パターン用意されています。

  1. UIImage(NSData)としてダウンロードする
  2. ローカルファイルパスに直接ダウンロードする
  3. オンラインのファイルを表すダウンロードURLを生成する

メモリ内のNSDataにダウンロードする

ダウンロードした画像を一時的に表示したいだけなどの際はメモリ(変数)内にダウンロードすることが可能です。参照を取得したらgetDataメソッドを使用します。引数には許容する最大サイズを指定します。completionHandlerから画像データを取得できるのでUIImage型などに変換すれば画面に表示できるようになります。

let sampleImgRef = reference.child("sample.jpg")

// 最大許容サイズ 1MB (1 * 1024 * 1024 バイト) でメモリにダウンロード
sampleImgRef.getData(maxSize: 1 * 1024 * 1024) { data, error in
    guard let data = data else { return }
    if let error = error {
        print(error)
    } else {
        let image = UIImage(data: data)
    }
}

デバイス上のファイルを表すNSURLにダウンロードする

サーバーにある画像ファイルを直接ローカルフォルダ(端末内)にダウンロードしたい場合はwrite(toFile:)メソッドを使用します。引数には保存したいローカルパスを指定します。

let sampleImgRef = reference.child("sample.jpg")

let localURL = URL(string: path)

let downloadTask = sampleImgRef.write(toFile: localURL) { url, error in
  if let error = error {
      print(error)
  } else {
      // 保存したローカルファイルURL
      print(url)
  }
}

オンラインのファイルを表すダウンロードURLを生成する

サーバーに保存してある画像ファイルを指すURLを取得したい場合はdownloadURLメソッドを使用します。completionHandlerから以下のような形式のURLが取得できます。

let sampleImgRef = reference.child("sample.jpg")
        
sampleImgRef.downloadURL { url, error in
    if let error = error {
        print(error)
    } else {
        print(url) // https://firebasestorage.googleapis.com:443/v0/b/[プロジェクト名].appspot.com/o/sample.jpg?alt=media&token=XXXXXXXXXXXXXXXXXXXXXXXXXXXX
        do {
            let data = try Data(contentsOf: url!)
            let image = UIImage(data: data)!
        } catch let error {
            print("Error : \(error.localizedDescription)")
        }
    }
}

ファイルを削除する

ファイルを削除したい場合は参照を取得してdeleteメソッドを実行するだけです。

let sampleImgRef = reference.child("sample.jpg")

do {
  try await sampleImgRef.delete()
} catch let error {
    print("Error : \(error.localizedDescription)")
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index