SwiftUIでFirebaseStorageに画像を保存する

Firebaseの仕様が自分が知っているものとかなり変わっていたのと、今までStoryboardでしかFirebaseの実装をしたことがなかったのでSwiftUIを使ってFirebaseにアクセス→FirebaseStorageに画像をアップロードするまでをメモ程度にまとめます。

# 筆者の開発環境

#### OS
macOS Monterey 12.3

#### Xcode
Version 13.3.1

# SwiftUIで最低限必要な画面を実装する
僕はまだまだStoryboardが好きなのですが、流行りはSwiftUIなの流行りに乗っていきます。現状はどの画像をアップロードるすのかわかりやすくするためにAssetsに入れてこれからアップロードする対象のImageと、画像をアップロードするためのButtonの二つのパーツを画面に実装します。

```Swift: ContentView.Swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Image("HogeHoge")
                .resizable()
                .frame(width: 200, height: 200)
            
            Button("画像をアップロード", action: {
                print("ボタンがタップされました")
            })
            
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

![CleanShot 2022-05-22 at 01.19.36@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526861/1d3dd806-33e2-a190-251f-f4f709d1e774.png)

こんな感じでImageとButtonの二つが実装できますね。ここまではSwiftUIチュートリアルみたいなところがあるのでサクッと実装してしまいましょう。Swiftはメンヘラだと思っているので地雷系の女の子をチョイスしました。


# FirebaseSDKの設定
FirebaseをSwiftのプロジェクトで使うためには
- Cocoapodsを使う方法
- Swift Package Managerを使う方法

の二つが主に挙げられます。今回は前者のCocoaPodsを使ってSDKのインストールを行なっていきます。
この記事に辿り着く人には不要かもしれませんが、一応手順を書いておきます。ターミナルを起動して以下のコマンドを入力してください。

```:ターミナル
Cocoapodsのインストール
sudo gem install cocoapods

ディレクトリに移動
cd/~~~~~(プロジェクトの当該ディレクトリへ移動)

プロジェクトにcocoapodsを設定
pod init

PodFileが作成できているのか確認
ls (中にPodFileというファイルができていればOKです)

PodFileを編集
vim PodFile
```

### Podfileを編集
```pod: Podfile
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'SwiftUI UploadImage' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for SwiftUI UploadImage
  #ここから下を入力します
  pod 'Firebase'
  pod 'FirebaseUI'
  pod 'Firebase/Storage'
  pod 'Firebase/Auth'
  pod 'Firebase/Database'
end
```

Podfileの編集が終わったらファイルを保存して閉じてインストールを行う必要があります。
この時に、M1(Pro / Max / ultra)とIntelでインストールコマンドが違うので自分のMacのCPUに合わせたコマンドを叩いてください。

```：ターミナル
IntelモデルのMacの場合
pod install

M1系列モデルのMacの場合
arch -x86_64 pod install
```

インストールには数分かかることがあるのでTwitterでもみて休憩しましょう。　
[Twitter](https://twitter.com/home)

CocoaPodsを使うとプロジェクトファイルはxcodeprojではなくxcworkspaceを開くようにしましょう。青いアイコン→白いアイコンと覚えてください。

# Firebaseの実装準備
ノリでノリノリにFirebaseでiOSのプロジェクトを作りましょう。アプリの登録→設定ファイルのダウンロードまでは指示に従って行なってください。画像のSDKの追加は先ほどCocoaPodsを使って追加したので何もせずにスルーしてください。
![CleanShot 2022-05-22 at 01.47.22@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526861/e1aca411-bed4-a849-2803-13275d504241.png)

ここからがSwiftUIでFirebaseを実装する中で最初の鬼門です。実はFirebaseはSPM（Swift package manager）を
推奨環境にしているにも関わらずSwiftUIは推奨じゃないようです😢 このままではSwiftUIで作成したプロジェクトにはAppDelegateがないのでここで詰んでしまいます。

![CleanShot 2022-05-22 at 01.49.52@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526861/e7bbd089-dee8-cdb5-d415-ba10eecfdd33.png)


そこで、Firebaseの初期化コードを初期生成されているファイルに追記する必要があります。初期生成された.Appファイルを編集していきます。筆者はこのプロジェクトを"SwiftUI UploadImage"という名前にしたのでこの名前です。
```Swift: SwiftUI UploadImage
//FirebaseCoreをimportしてください！
import SwiftUI
import FirebaseCore

@main
struct SwiftUI_UploadImageApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
    //ここに初期化処理
    init() {
        FirebaseApp.configure()
    }
}
```

これでSwiftUIプロジェクトでFirebaseを使うための初期設定が完了しました。続いてStorageの設定をしていきます。
私は書いているこの記事だけでしかプロジェクトを使用しないので本番環境モードですが、必要に応じてテストモードを使い分けてください。
![CleanShot 2022-05-22 at 01.59.23@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526861/3b911b53-4061-61f6-7c98-ccb7d9763737.png)

ロケーションを選べと言われるのでasia-northeast1を選んでください。東京だそうです。
![CleanShot 2022-05-22 at 02.01.12@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526861/c826744b-4d39-db84-ddcb-aa040cca21cc.png)

設定が済んだら、Rulesの赤枠部分を初期設定のfalseから画像のようにtrueに書き換えてください。
![CleanShot 2022-05-22 at 02.02.58@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526861/21685ac0-c8e6-ec98-e631-c04fb459eef1.png)

最後にgs://で始まっているところのURLをコピーできるか確認してください。
![CleanShot 2022-05-22 at 02.04.55@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526861/8aee53dd-ace6-b119-4f63-1b3f526b12c1.png)

これでFirebaseの設定が完了です。

# Swiftでの実装
```Swift: ContentView.Swift
import SwiftUI
import Firebase
import FirebaseStorageUI

struct ContentView: View {
    var body: some View {
        VStack {
            Image("HogeHoge")
                .resizable()
                .frame(width: 200, height: 200)
            
            Button("画像をアップロード", action: {
                print("ボタンがタップされました")
                //ButtonのActionで画像のアップロード関数を呼び出す
                UploadImage()
                
            })
            
        }
    }
}
//画像をアップロードする関数
func UploadImage(){
    let storageref = Storage.storage().reference(forURL: "gs:").child("Item")
    
    let image = UIImage(named: "HogeHoge.jpeg")
    
    let data = image!.jpegData(compressionQuality: 1.0)! as NSData
    
    storageref.putData(data as Data, metadata: nil) { (data, error) in
        if error != nil {
            return
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```
この辺りの実装方法はStoryboardを使っている時とコードは大差ないと思います。forURLのところはコピペした自分のURLを入れてください。

うまくいくと画像のように「Item」という名前でアップロードされています。ダウンロードしてみるとしっかりと画像をアップロードすることができていることがわかります。
![CleanShot 2022-05-22 at 02.21.10@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526861/aff378ec-1c3b-a864-25ae-be09fbd38c7c.png)

# 最後に
自分が困ったところをただただ書いただけなので特にプログラムの解説などはないです。Firebaseの公式ドキュメントを読んだ方がアップロード周りは理解できるかなと思います。環境構築や設定方法など少しでも参考になれば嬉しいです。

最後まで読んでいただきありがとうございました。