Swift SDKを使ってカメラメモアプリを作る（その3：写真の検索と表示処理）

ニフクラ mobile backend（NCMB）では各種言語向けにSDKを提供しています。その中で最も新しいSwift SDKについて、その使い方を紹介します。

この記事では複数回に分けて、カメラメモアプリを作ってみます。Swift SDKの使い方はもちろん、NCMBの基本的な利用法を学ぶのに役立ててください。[初回の画面仕様と認証処理](https://qiita.com/goofmint/items/937823dda1c83cf711d5)に続き、前回は[認証処理の実装と、写真アップロード処理まで](https://qiita.com/goofmint/items/e087f35c7bbb0e2cd14e)を作成しました。今回は保存したデータの表示、写真データのダウンロードについて紹介します。

なお、今回のコードは[NCMBMania/camera_memo_swift: Swift SDKを使ったカメラメモアプリのコードです](https://github.com/NCMBMania/camera_memo_swift)にアップしてあります。

## メモデータの取得

メモデータの取得は `ImageView.swift` にて実装します。すでに紹介済みですが、もう一度画面構成を掲載します。

```swift
struct ImageView: View {
    @State var memos: [NCMBObject] = []
    // LazyVGrid用
    var columns: [GridItem] = Array(repeating: .init(.fixed(200)), count: 2)
    
    var body: some View {
        ScrollView(.vertical) {
            LazyVGrid(columns: columns, alignment: .center, spacing: 200) {
                Foreach (Memos, Id: \.objectid) { Memo In
                    Gridimageview(Memo: Memo)
                }
            }
            .onAppear() {
                getAllPhotos()
            }
        }
    }
    func getAllPhotos() {
      // メモデータを取得します（後述）
    }
}
```

メモデータの取得は `getAllPhotos` にて行います。まずNCMBを検索するためのNCMBQueryを用意します。対象となるクラス名（DBでいうテーブル相当）は保存したのと同じMemoです。

```swift
let query = NCMBQuery.getQuery(className: "Memo")
```

そして検索を実行します。検索結果はそのまま `memos` の中に入れます。

```swift
query.findInBackground(callback: { result in
    switch result {
        case let .success(array):
            self.memos = array
        case let .failure(error):
            print("取得に失敗しました: \(error)")
    }
})
```

memosの中にデータが入ると、ForEachで繰り返し描画されます。この時、ユニークキーは objectId としてください。

```swift
ForEach (memos, id: \.objectId) { memo in
    GridImageView(memo: memo)
}
```

## グリッド表示について

メモのグリッド表示は `GridImageView.swift` にて行います。こちらもすでに紹介済みですが、再度掲載します。

```swift
struct GridImageView: View {
    @State private var imageData: Data? = .init(capacity:0)
    @State var memo: NCMBObject? = nil
    @State private var isShowing = false
    
    var body: some View {
        GeometryReader { geometry in
            if imageData?.count ?? 0 > 0 {
                Image(uiImage: UIImage(data: imageData!)!)
                    .resizable()
                    .scaledToFill()
                    .frame(width: geometry.size.width, height: geometry.size.width)
                    .clipped()
            } else {
                Rectangle().fill(Color.clear)
            }
        }.onAppear() {
            loadImage()
        }.onTapGesture {
            isShowing = true
        }.fullScreenCover(isPresented: $isShowing) {
            ModalView(isActive: $isShowing, memo: memo!, imageData: imageData!)
        }
        
    }

    func loadImage() {
      // 画像を読み込みます（後述）
    }
}
```

## 画像データを読み込む

ファイルストアから画像データを読み込む処理は `loadImage` 関数で行っています。ファイルストアではHTTPSアクセスも可能ですが、ACL（アクセス制限）を行っている場合にはデータを取得する方が良いでしょう。こうすることで、他のユーザからは読まれないデータとして利用できます。

まずメモデータからファイル名を取得します。

```swift
if let fileName : String = self.memo?["fileName"] {
}
```

この fileName を使ってNCMBFileオブジェクトを作ります。

```swift
let file : NCMBFile = NCMBFile(fileName: fileName)
```

ファイルデータの取得は `fetchInBackground` にて行います。

```swift
file.fetchInBackground(callback: { result in
})
```

処理がうまくいっていればデータが取得できますので、それをimageDataとして適用します。

```swift
switch result {
    case let .success(data):
        self.imageData = data
    case let .failure(error):
        print(error)
}
```

imageDataが入れば、それをUIImageのdataとして適用し、Imageオブジェクトで画像として表示します。

```swift
if imageData?.count ?? 0 > 0 {
    Image(uiImage: UIImage(data: imageData!)!)
        .resizable()
        .scaledToFill()
        .frame(width: geometry.size.width, height: geometry.size.width)
        .clipped()
} else {
    Rectangle().fill(Color.clear)
}
```

これでファイルストアから取得した画像データを描画できます。

## 画像をタップした際の処理

画像をタップした場合にはモーダル表示を行っています。

```swift
}.onTapGesture {
    isShowing = true
}.fullScreenCover(isPresented: $isShowing) {
    ModalView(isActive: $isShowing, memo: memo!, imageData: imageData!)
}
```

このModalViewは `ModalView.swift` にて定義しています。ここでは受け取ったNCMBObjectと画像データをそのまま描画しています。

```swift
struct ModalView: View {
    @Binding var isActive: Bool
    @State var memo: NCMBObject
    @State var imageData: Data
    
    var body: some View {
        HStack {
            Spacer()
            VStack {
                Spacer()
                GeometryReader { geometry in
                    Image(uiImage: UIImage(data: imageData)!)
                        .resizable()
                        .scaledToFill()
                        .frame(width: geometry.size.width, height: geometry.size.height)
                        .clipped()
                }
                if let text: String = memo["text"] {
                    Text(text).padding()
                }
                Button("閉じる") {
                    isActive = false
                }
                Spacer()
            }
            Spacer()
        }
        .padding()
        .background(Color(.black))
    }
}
```

これでカメラメモアプリの完成です。

## まとめ

今回のカメラメモアプリを通して、NCMBの次の機能を利用しました。

- 認証機能
  - ID/パスワードによるログイン
  - ログアウト
- ファイルストア
  - ファイルアップロード
  - ファイルダウンロード
- データストア
  - ファイルストアと紐付けたメモを保存

認証機能とデータストアはよく利用される機能ですし、スマートフォンアプリでは写真データもよく利用するでしょう。今回のコードを参考にSwift SDKをぜひご利用ください。

[イントロダクション (Swift) : クイックスタート | ニフクラ mobile backend](https://mbaas.nifcloud.com/doc/current/introduction/quickstart_swift.html)