【iOS】DynamoDBからデータを取得する方法

## はじめに
swiftでDynamoDBと連携するアプリの開発をすることがあると思いますが、記事が少なかったのでデータを取得する方法をまとめてみました。

## 開発環境
- Swift 5
- Xcode 12
- [aws-amplify/aws-sdk-ios](https://github.com/aws-amplify/aws-sdk-ios)
- cocoapods

## 1. AWSの認証設定
こちらにまとめましたので参考にしてください。
[【iOS】AWS DynamoDBからテーブル名を取得する方法](https://qiita.com/shungo_m/items/4d06133344ccc4a23f90)

## 2. podにAWSDyanamoDBを追加
プロジェクトにすでにPodが導入されていることが前提です。
もしインストールされていなければ、インストールしてください。

Podfileに`AWDDyanmoDB`を追加します。

```:Podfile
target 'aws-sdk' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  pod 'AWSDynamoDB'
end
```

podインストールしてXcodeを開き直します。

```:ターミナル
$ pod install --repo-update
$ xed .
```

## 3. AWS DynamoDBでテーブル作成・データ挿入

テーブルの作成を押して、テーブルを作成します。
今回はテーブル名をuserにしました。

Userテーブル

|カラム名|型|プライマリパーティションキー|
|---|---|---|
|id|Number|○|
|email|String||
|password|String||


![スクリーンショット 2020-12-31 15.27.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/497683/0dc350e6-5b59-6c49-8aba-2590cfe978d1.png)

テーブルが作成されたら、項目のタブを選択します。
そして、項目の作成を押してデータを作成します。
下記のようなデータを作成しました。

|カラム名|値|
|---|---|
|email|test@test.com|
|password|password|

![スクリーンショット 2020-12-31 15.27.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/497683/8f0a3965-81f4-792c-f559-e04cb0b0d4d0.png)

## 4. modelを作成

DynamoDBのデータを取得するためにモデルを作成します。
クラス名やプロパティへ適宜変更してください。

@objcを付けないとデータを取得できません。
私はこれがわからずハマったので、忘れないように気をつけてください。。。

```User.swift
import AWSDynamoDB

class User: AWSDynamoDBObjectModel, AWSDynamoDBModeling {
    
    @objc private var id: NSNumber = 0
    @objc private var email: String = ""
    @objc private var password: String = ""

    class func dynamoDBTableName() -> String {
        return "user"  // DyanmoDBに作成したテーブル名を記載
    }

    class func hashKeyAttribute() -> String {
        return "id"
    }
}
```

## 5. データを取得するメソッドを作成

今回はデータを全件取得するのでscanを使用しています。

```ViewController.swift
import UIKit
import AWSDynamoDB

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        fetchDynamoDBData()
    }
    
    private func fetchDynamoDBData() {
        let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
        let scanExpression = AWSDynamoDBScanExpression()

        // Userの部分は作成したModelのクラス名
        dynamoDBObjectMapper.scan(User.self, expression: scanExpression).continueWith() { (task:AWSTask<AWSDynamoDBPaginatedOutput>!) -> Void in
            
            guard let items = task.result?.items as? [User] else { return }
            if let error = task.error as NSError? {
                print("The request failed. Error: \(error)")
                return
            }
            print(items)
        }
    }
}

```

これでビルドするとコンソールにデータが表示されます。

![スクリーンショット 2020-12-31 18.03.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/497683/9a42a7bf-14a9-ef6d-1c7e-6a038c2e5935.png)


### もしDyanmoDBに大量のデータがある場合
データが多いとscanでも、一度に全件取得できない可能性があります。(ここに気づかず、かなりハマった。。。)
一回のscanで1MBまでしか取得できません。

[(DynamoDB でのスキャンの使用)](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Scan.html)

全件取得できていない場合は、`task.result?.lastEvaluatedKey`というものが出現します。
つまり、これがnilにならない限り、全件取得できてきないということです。(ページネーションの現在のページのようなもの)

もしデータが大量にあるが、全件取得したい場合は、ページネーションを更新する必要があります。
下記のように、`task.result?.lastEvaluatedKey`が存在すれば、スタートするページを`task.result?.lastEvaluatedKey`に更新して再度fetchします。

```ViewController.swift
func fetchDynamoDBData() {
    dynamoDBObjectMapper.scan(User.self, expression: scanExpression).continueWith() { (task:AWSTask<AWSDynamoDBPaginatedOutput>!) -> Void in

        guard let key = task.result?.lastEvaluatedKey else {
            print("scan終了")
            return
        }

        if key != nil {
            // MEMO: スタートするページを更新
            scanExpression.exclusiveStartKey = key
            fetchDynamoDBData()
        }
    }
}
```

## リポジトリ
参考までに。
https://github.com/shungo0525/aws-sdk

## 参考記事
https://stackoverflow.com/questions/26958637/best-way-to-make-amazon-aws-dynamodb-queries-using-swift
https://stackoverflow.com/questions/34049054/aws-dynamodb-swift-query-with-multiple-hashkeyvalues