NCMBのSwift SDKを使って日報アプリを作る（その2：日報データの保存）

NCMBのSwift SDKを使ってデモアプリを作ってみます。今回は業務系でよくあるニーズの日報アプリを作ってみます。実際にはデータストアやファイルストアを使うので、応用すれば汎用的に使えるはずです。

[前回は画面の説明とSDKの導入まで](https://qiita.com/goofmint/items/0dde24a05b0c0b4f9adb)行いましたので、今回は日報データの保存処理を作成します。

## コードについて

今回のコードは[NCMBMania/swift\_daily\_report](https://github.com/NCMBMania/swift_daily_report)にアップロードしてあります。実装時の参考にしてください。

## InputViewについて

日報の入力と保存はInputViewにて行います。画面は日報の日付、内容、そして写真選択といったフォームを表示しているのみです。

```swift
import SwiftUI
import NCMB

struct InputView: View {
    @State private var date = Date()     // 日報の日付
    @State private var text = ""         // 日報の本文
    @State private var showAlert = false // アラート表示用のフラグ
    @State private var message = ""      // アラートのメッセージ
    
    @State var imageData : Data = .init(capacity:0) // // 選択された写真データ
    @State var source:UIImagePickerController.SourceType = .photoLibrary // カメラまたはフォトライブラリ
    @State var isImagePicker = false // 写真ピッカーを表示する際のフラグ
    
    var body: some View {
        NavigationView {
            VStack(spacing: 10) {
                // 画像モーダルへの遷移用
                NavigationLink(
                    destination: Imagepicker(
                        show: $isImagePicker,
                        image: $imageData,
                        sourceType: source
                    ),
                    isActive:$isImagePicker,
                    label: {
                        Text("")
                    })
                // 日報の日付を選択
                DatePicker("日付",
                    selection: $date,
                    displayedComponents: .date
                )
                // 日報の内容を記述するTextEditor
                TextEditor(text: $text)
                    .frame(width: UIScreen.main.bounds.width * 0.8, height: 200)
                    .overlay(
                        RoundedRectangle(cornerRadius: 5)
                            .stroke(Color.blue, lineWidth: 2)
                    )
                // 画像が指定されれば、サムネイル表示
                if imageData.count != 0 {
                    Image(uiImage: UIImage(data: self.imageData)!)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(height: 100)
                        .cornerRadius(15)
                        .padding()
                }
                // 写真選択モーダルの表示
                Button(action: {
                    self.source = .photoLibrary
                    self.isImagePicker.toggle()
                }, label: {
                    Text("写真を選択")
                })
            }
            .navigationBarTitle("日報入力", displayMode: .inline)
            .toolbar {
                // 右上のアイコンで保存しょりを実行
                ToolbarItem(placement: .navigationBarTrailing){
                    Button(action: {
                        save()
                    }) {
                        Image(systemName: "icloud.and.arrow.up")
                    }
                }
            }
        }
        .alert(isPresented: $showAlert) {
            Alert(title: Text(self.message))
        }
    }
    
    // 日報を保存する処理
    func save() {
    }
}
```

![Simulator Screen Shot - iPod touch (7th generation) - 2021-10-06 at 14.57.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/c54f32cb-97af-dbaa-6d65-09180bf5bfd5.png)


### 保存処理について

データの保存は save 関数にて行います。ここからはsave関数の内容について解説します。まずNCMBObject（DBでいう行相当）を準備します。クラス名というのは、DBでいうテーブル名相当です。

```swift
// DailyReportクラス（DBでいうテーブル相当）のインスタンス（DBでいう行相当）を準備
let obj = NCMBObject(className: "DailyReport")
```

この obj にキーを指定して、text（日報の本文）とdate（日報の日付）を指定します。これはDBでいうカラム相当になります。

```swift
// 指定されている値をセット
obj["text"] = self.text
obj["date"] = self.date
```

次に画像が指定されているかどうかを判定します。画像が指定されている場合には、そのデータをファイルストアに保存します。ファイル名はUUIDを使ってユニークなものにしています。

saveメソッドは同期的にファイル保存を行います。非同期処理が良い場合には `saveInBackground` も用意しています。必要に応じて使い分けてください。写真の保存処理がうまくいったら、そのファイル名をobjのfileNameキーに紐付けます。

```swift
// 画像があるか判断
if self.imageData.count > 0 {
    // 画像があれば、ファイルストアへ保存する
    // ファイル名はUUIDで生成
    let fileName = "\(UUID()).jpg"
    // ファイルストアのインスタンスを準備
    let file = NCMBFile(fileName: fileName)
    // 保存（ファイルストアへのアップロード）
    _ = file.save(data: self.imageData)
    // ファイル名をDailyReportクラスのインスタンスに紐付ける
    obj["fileName"] = fileName
}
```

そしてobjも保存します。

```swift
// データを保存（データストアにアップロード）
let result = obj.save()
```

こちらは処理判定を行っています。resultが .success（処理成功）か .failure（処理失敗）によって処理内容を変えています。最終的にアラートを出して処理完了です。

```swift
// 結果を判定
switch result {
case .success(): // 保存がうまくいったら、入力内容をリセット
    self.text = ""
    self.imageData.count = 0
    self.date = Date()
    // メッセージを設定
    self.message = "保存しました"
    break
case let .failure(err): // エラーの場合はエラーメッセージをセット
    self.message = err.localizedDescription
    break
}
// アラートを表示
self.showAlert = true
```

![Simulator Screen Shot - iPod touch (7th generation) - 2021-10-06 at 14.59.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/07104829-1cb7-17cd-fec4-6c15e6a37b2a.png)


ここまでで日報データの保存処理が完成となります。

## まとめ

今回は日報アプリにおける日報データの保存処理を解説しました。次は日報データの検索と一覧表示周りを解説します。