【mBaaS】 サーバーへの保存と取得を行うアプリを作る！　～saveメソッドとfindObjectsメソッド～

サーバー連携アプリには、サーバーに保存する機能とその保存したものを取得する機能が必要になると思います。

mBaaSのSDKでは、saveメソッドで保存を行い、findObjectsメソッドで取得を行います。
この方法以外にも保存や取得が出来るのですが、この二つのメソッドを理解する事でサーバー連携アプリの基本が理解しやすくなると思います。

#saveメソッド　- NCMBObjectクラス -

簡易的にmBaaSに保存させるには以下の処理でサーバー上に保存してみてください。

```objc
// testクラスのNCMBObjectを作成
NCMBObject *object = [NCMBObject objectWithClassName:@"test"];

// オブジェクトに値を設定
[object setObject:@"value" forKey:@"key"];

// データストアへの登録を実施
[object saveInBackgroundWithBlock:^(NSError *error) {
    if (error){
        // 保存に失敗した場合の処理
    } else {
        // 保存に成功した場合の処理
    }
}];
```

この処理では、mBaaSのデータストアにtestクラスを新規で作成して、keyフィールドにvalueという値を入れて保存するといった処理になります。saveInBackgroudWithBlockメソッドを実行すると非同期処理でmBaaS上に保存されます。

![save method 01.png](https://qiita-image-store.s3.amazonaws.com/0/126404/8b7d16ad-56f2-6c7e-710b-f05d8a85cd70.png)

大体のアプリ作成者はここから保存したものを編集します。
オブジェクトを取得する方法はいろいろあるのですが、ここでは、全件取得を行うqueryの機能を使って取得する方法を紹介しようと思います。

#findObjectsメソッド　- NCMBQueryクラス -

queryの作成方法は以下のような方法になります。

```objc
// testクラスを検索するNCMBQueryを作成
NCMBQuery *query = [NCMBQuery queryWithClassName:@"test"];

// keyの値がvalueと一致するデータを検索する条件を設定
[query whereKey:@"key" equalTo:@"value"];

// データストアの検索を実施
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
    if (error) {
        // 検索失敗時の処理
    } else {
        // 検索成功時の処理
    }
}];
```

findObjectsメソッドを利用すると、先ほど作成したkeyフィールドにvalueという値が入ったデータを取得できます。
ここでは、keyの値がvalueと一致するデータのみを取得するようにしています。

#取得したデータの編集

取得したデータを編集する場合は、以下のような方法になります。

```objc
// testクラスを検索するNCMBQueryを作成
NCMBQuery *query = [NCMBQuery queryWithClassName:@"test"];

// keyの値がvalueと一致するデータを検索する条件として設定
[query whereKey:@"key" equalTo:@"value"];

// データストアの検索を実施
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
    if (error) {
        // 検索失敗時の処理
        NSLog(@"取得失敗:%@",error);
    } else {
        // 検索成功時の処理
        NSLog(@"取得成功");
        // 取得したデータからNCMBObjectを取り出す
        for (NCMBObject *object in objects) {
            // keyの値がvalueと一致するデータの場合、編集処理をする。（query検索時に条件を設定しているのでどちらか一方だけで良い）
            if ([[object objectForKey:@"key"] isEqualToString:@"value"]) {

                //オブジェクトに新しい値を設定
                [object setObject:@"newValue" forKey:@"key"];
                
                //データストアへの登録を実施
                [object saveInBackgroundWithBlock:^(NSError *error) {
                    if (error){
                        //保存に失敗した場合の処理
                    } else {
                        //保存に成功した場合の処理
                    }
                }];
            }
        }
    }
}];
```

この処理を実行すると、先ほどsaveメソッドで保存したkeyフィールドにvalueという値が入ったレコードが編集されて、keyフィールドにnewValueという新しい値が入って保存されます。

![save method 02.png](https://qiita-image-store.s3.amazonaws.com/0/126404/de7c22c3-1996-d18e-4ee2-45c53cbe9180.png)

アプリ制作では、保存している会員情報や様々なデータを編集して更新する機能が必要となる事があります。
そういった場合は、findObjectsメソッドで取得して、編集処理を行って、saveメソッドで保存するのが良いでしょう。

#取得・編集・保存をメソッド毎に分ける

アプリを作成しているとデータを取得して編集して保存するという処理を一つの操作で行う事はあまりありません。
大体の場合、取得と編集と更新はそれぞれ別のイベント時に実行されます。そのため取得・編集・保存を行う処理は、それぞれ別のメソッドに分けて行うのがよいでしょう。

###●取得メソッド - (void)getData
```objc:viewController
@interface ViewController ()
@property(nonatomic)NSArray *dataArray;
@end

// データを取得するメソッド
- (void)getData {
    // testクラスを検索するNCMBQueryを作成
    NCMBQuery *query = [NCMBQuery queryWithClassName:@"test"];
    
    // データストアの検索を実施
    [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
        if (error) {
            // 検索失敗時の処理
            NSLog(@"取得失敗:%@",error);
        } else {
            // 検索成功時の処理
            NSLog(@"取得成功");
            // 取得したデータをプロパティに代入する
            self.dataArray = objects;
        }
    }];
}
```

取得メソッドでは、取得したデータを様々なところで用いられるプロパティ(dataArray)に代入するまでを行うのが良いでしょう。この時に、NCMBObjectを自アプリで使いやすいように別のモデルクラスにパースするのも良いと思います。
###●編集メソッド - (void)editData

```objc
// データを編集するメソッド
- (void)editData {
    for (NCMBObject *object in self.dataArray) {
        // ここで様々な編集を行う

        // 編集したオブジェクトを格納する配列
        NSMutableArray *editArray = [[NSMutableArray alloc]init];
        for (NCMBObject *object in objects) {
            // keyフィールドの値がvalueの場合、値をnewValueに更新する
            if ([[object objectForKey:@"key"]isEqualToString:@"value"]) {
                [object setObject:@"newValue" forKey:@"key"];
            }
            [editArray addObject:object];
        }
        // プロパティの配列を編集後の配列に上書きする
        self.dataArray = editArray;
    }
}
```

ここでは、keyフィールドの値がvalueの場合、値をnewValueに更新する処理を行っています。
編集した内容をプロパティに上書きする処理も忘れずに行いましょう。

###●保存メソッド（一括） - (void)saveData
```objc
// データを保存するメソッド
- (void)saveData {
    // 取得したデータをすべて一括で保存する
    [NCMBObject saveAllInBackground:self.dataArray withBlock:^(NSArray *results, NSError *error) {
        if (error){
            //保存に失敗した場合の処理
        } else {
            //保存に成功した場合の処理
        }
    }];
}
```

ここで、行われているsaveAllInBackgroundメソッドは、mBaaS上に保存されているtestクラスのオブジェクトの配列を一括で更新する事が出来ます。もちろん、引数で保存するデータを渡して個別に保存することも可能です。

###●保存メソッド（個別） - (void)saveData:(NCMBObject *)object

```objc
- (void)saveData:(NCMBObject *)object {
    // 個別にデータを保存する
    [object saveInBackgroundWithBlock:^(NSError *error) {
        if (error){
            //保存に失敗した場合の処理
        } else {
            //保存に成功した場合の処理
        }
    }];
}
```
個別で保存するメソッドを作っておけば、必要なデータだけを様々なタイミングで呼び出して実行する事が出来ます。ただし、編集するレコードが多い場合は、saveAllInBackgroundで一括で保存する方が、処理的にも簡単に済みます。

#補足

####・クルクルインジケーター
これらの処理は非同期（○○InBackgroundWithBlock）で行われているため、通信処理を行っている間はUIActivityIndicatorViewを利用して、通信が完了するまでクルクルを表示させてアプリを操作出来ないようにするのが良いです。
####・複数のクラス
mBaaSのデータストアのクラスを複数使用する場合は、引数でクラス名を指定するかもしくは、メソッド名をsave○○Dataとして、複数のsave,edit,getメソッドとプロパティをそれぞれ作るのが良いかと思います。
<br>
mBaaSのSDKのsaveメソッドとfineObjectsメソッドを理解することで、サーバー連携アプリの作成がしやすくなると思います。実際にアプリを作成する場合は、上記メソッドを応用してご利用ください。