Swiftでデータベースを使おう！①（保存・取得・更新・削除）

__【お知らせ】Swift3に対応しました！(2017/02/23更新)__

# 【Swift×mBaaS】シリーズ概要
* Swiftで[ニフクラmobile backend](https://mbaas.nifcloud.com/)(通称：mBaaS)を使ってSwiftで開発を始めたい人向けのドキュメントです。
* [ニフクラmobile backendのiOS(Objectiv-C SDK)用ドキュメント](https://mbaas.nifcloud.com/doc/current/index.html)を__Swift__用に書き換えて動かしてみたものをまとめました。
* 初心者でもわかりやすいよう心掛けて作っていますが、わかりにくい部分がありましたらコメントをいただければ訂正しますのでお気軽にご意見をお願いします^^*
* 今回は__＜データストア編＞__として*保存*・*取得*・*更新*・*削除*について説明します！
 * 注：「データストア」が[ニフクラmobile backend](https://mbaas.nifcloud.com/)の「データベース」のことです◎

# 事前準備のお願い
* 事前にニフクラ[mobil backendの登録](https://mbaas.nifcloud.com/)とアプリの新規作成をし、 [＜CocoaPods＞SwiftでmBaaSを始めよう！](https://qiita.com/natsumo/items/b2d18d87d57300c8d81c)をご覧いただき、必要な初期設定(SDKのインストールなど)をXcode上で行った上でご利用ください。

# ドキュメントの動作確認方法
* `AppDelegate.swift`ファイルに次のコードを書きます。

```swift:Swift3_AppDelegate.swift
import UIKit
import NCMB

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    //********** APIキーの設定 **********
    let applicationkey = "YOUR_NCMB_APPLICATIONKEY"
    let clientkey      = "YOUR_NCMB_CLIENTKEY"

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        //********** SDKの初期化 **********
        NCMB.setApplicationKey(applicationkey, clientKey: clientkey)

        /** ★ここに記述するとアプリ起動時に実行されます★ **/

        return true
    }
}
```

```swift:Swift2_AppDelegate.swift
import UIKit
import NCMB

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    //********** APIキーの設定 **********
    let applicationkey = "YOUR_NCMB_APPLICATIONKEY"
    let clientkey      = "YOUR_NCMB_CLIENTKEY"

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        //********** SDKの初期化 **********
        NCMB.setApplicationKey(applicationkey, clientKey: clientkey)

        /** ★ここに記述するとアプリ起動時に実行されます★ **/

        return true
    }
}
```
* ２つのAPIキーをニフクラmobil backendのダッシュボード上からコピーして書き換えてください。(※アプリケーションキーとクライアントキーをそれぞれ「YOUR_NCMB_APPLICATIONKEY」と「YOUR_NCMB_CLIENTKEY」の部分に書きます。)
* [【Swift×mBaaS】クイックスタートを作ってみました！＜CocoaPods編＞](https://qiita.com/natsumo/items/b2d18d87d57300c8d81c)で設定をしてからこのページを見ている方は、`AppDelegate.swift`に記述する部分が重複しますが、こちらの内容に書き換えてください。
* 上のコードの中央付近に「__/** ★ここに記述するとアプリ起動時に実行されます★ **/__」と書きましたが、ここに書かれた内容はアプリの起動時に実行されます。以下のSwiftドキュメントもここに書けば気軽に動作確認できます。


# __Swiftでデータベースを使おう！__
* データストアの機能はNCMBObjectを通じて利用します。
* それぞれコードの実行をし、[ニフクラmobile backend](https://mbaas.nifcloud.com/)のダッシュボードで確認をしながら動作確認をしてください。

## オブジェクトを保存する
* 非同期での保存の場合にはsaveInBackgroundWithBlockメソッドを使います。
 * TestClass：保存用クラス名（※クラスが存在しない場合は新規でクラスが作成されます。）
 * key, number, array：フィールド名
 * value, 1, ["A", "B", "C"]：保存する値（※データ型は文字列、配列、数字、日付、真偽値、オブジェクト、緯度経度が利用可能）

```swift:Swift3
// クラスのNCMBObjectを作成
let obj = NCMBObject(className: "TestClass")
// オブジェクトに値を設定
/** 文字列 **/
obj?.setObject("value", forKey: "key")
/** 数値 **/
obj?.setObject(1, forKey: "number")
/** 配列 **/
obj?.setObject(["A", "B", "C"], forKey: "array")
        
// データストアへの保存を実施
obj?.saveInBackground({ (error) in
    if error != nil {
        // 保存に失敗した場合の処理
    }else{
        // 保存に成功した場合の処理
    }
})
```

```swift:Swift2
// クラスのNCMBObjectを作成
let obj = NCMBObject(className: "TestClass")
// オブジェクトに値を設定
/** 文字列 **/
obj.setObject("value", forKey: "key")
/** 数値 **/
obj.setObject(1, forKey: "number")
/** 配列 **/
obj.setObject(["A", "B", "C"], forKey: "array")

// データストアへの保存を実施
obj.saveInBackgroundWithBlock { (error: NSError!) -> Void in
    if error != nil {
        // 保存に失敗した場合の処理
    }else{
        // 保存に成功した場合の処理
    }
}
```
## 通信状況をチェックしてオブジェクトを保存する
* 通信状況をチェックして保存処理を行う場合はsaveEventuallyメソッドを使います。
 * オンライン時：saveInBackgroundWithBlockメソッドが実行されます。
 * オフライン時：保存処理は行われず、次にオンラインになったときに保存処理が行われる。

```swift:Swift3
// クラスのNCMBObjectを作成
let obj2 = NCMBObject(className: "TestClass")
// オブジェクトに値を設定
obj2?.setObject("value2", forKey: "key")
// データストアへの保存を実施
obj2?.saveEventually({ (error) in
    if error != nil {
        // 保存に失敗した場合の処理
    }else{
        // 保存に成功した場合の処理
    }
})
```

```swift:Swift2
// クラスのNCMBObjectを作成
let obj2 = NCMBObject(className: "TestClass")
// オブジェクトに値を設定
obj2.setObject("value2", forKey: "key")
// データストアへの保存を実施
obj2.saveEventually { (error: NSError!) -> Void in
    if error != nil {
        // 保存に失敗した場合の処理
    }else{
        // 保存に成功した場合の処理
    }
}
```

## オブジェクトを取得する
* objectIdを指定して、指定したデータを取得します。
* fetchInBackgroundWithBlockメソッドを利用します。
* オブジェクトの取得は「オブジェクトの検索」を使う方法もあります。こちらについては[＜データストア編②＞](https://qiita.com/natsumo/items/cffdc5f6933988916600)で説明します。

```swift:Swift3
// クラスのNCMBObjectを作成
let obj3 = NCMBObject(className: "TestClass")
// objectIdプロパティを設定
obj3?.objectId = "***検索するデータのobjectId***"
// 設定されたobjectIdを元にデータストアからデータを取得
obj3?.fetchInBackground({ (error) in
    if error != nil {
        // 取得に失敗した場合の処理
    }else{
        // 取得に成功した場合の処理
        // (例)取得したデータの出力
        print(obj3! as NCMBObject)
    }
})
```

```swift:Swift2
// クラスのNCMBObjectを作成
let obj3 = NCMBObject(className: "TestClass")
// objectIdプロパティを設定
obj3.objectId = "***検索するデータのobjectId***"
// 設定されたobjectIdを元にデータストアからデータを取得
obj3.fetchInBackgroundWithBlock { (error: NSError!) -> Void in
    if error != nil {
        // 取得に失敗した場合の処理
    }else{
        // 取得に成功した場合の処理
        // (例)取得したデータの出力
        print(obj3)
    }
}
```

* 「\*\*\*検索するデータのobjectId\*\*\*」の部分は検索対象のobjectIdを書いてください。
 * objectIdは[ニフクラmobile backend](https://mbaas.nifcloud.com/)のダッシュボード＞データストアから確認できます。

## オブジェクトの更新
* 更新は、保存したデータに新しい値をセットしてsaveInBackgroundWithBlockメソッドを実行して保存する流れで行います。

### 例１：指定したフィールド（数値）の値をインクリメントする
* incrementKeyメソッドを使います。
* 以下はオブジェクトの取得と同様にobjectIdを指定して、そのデータの指定したフィールドの値(数値)をインクリメントするサンプルです。

```swift:Swift3
/** オブジェクトの更新(フィールドの値をインクリメントする) **/
// クラスのNCMBObjectを作成
let obj4 = NCMBObject(className: "TestClass")
// objectIdプロパティを設定
obj4?.objectId = "***更新するデータのobjectId***"
// 設定されたobjectIdを元にデータストアからデータを取得
obj4?.fetchInBackground({ (error) in
    if error != nil {
        // 取得に失敗した場合の処理
    }else{
        // 取得に成功した場合の処理
        // 指定フィールドの値をインクリメントする
        obj4?.incrementKey("number")
        obj4?.saveInBackground({ (error) in
            if error != nil {
                // 更新に失敗した場合の処理
            }else{
                // 更新に成功した場合の処理
                // (例)更新したデータの出力
                print(obj4! as NCMBObject)
            }
        })
    }
})
```

```swift:Swit2
/** オブジェクトの更新(フィールドの値をインクリメントする) **/
// クラスのNCMBObjectを作成
let obj4 = NCMBObject(className: "TestClass")
// objectIdプロパティを設定
obj4.objectId = "***更新するデータのobjectId***"
// 設定されたobjectIdを元にデータストアからデータを取得
obj4.fetchInBackgroundWithBlock { (error: NSError!) -> Void in
    if error != nil {
        // 取得に失敗した場合の処理
    }else{
        // 取得に成功した場合の処理
        // 指定フィールドの値をインクリメントする
        obj4.incrementKey("number")
        obj4.saveInBackgroundWithBlock({ (error: NSError!) -> Void in
            if error != nil {
                // 更新に失敗した場合の処理
            }else{
                // 更新に成功した場合の処理
                // (例)更新したデータの出力
                print(obj4)
            }
        })
    }
}
```
 * インクリメントなので、フィールドの値を「1」と保存しているデータであれば、上のサンプルを実行すると「2」になります。「2」なら「3」になります。

### 例２：指定したフィールド（配列）の要素がユニークになるように値を追加する
* addUniqueObjectsFromArrayメソッドを使います。
* 以下はオブジェクトの取得と同様にobjectIdを指定して、そのデータの指定したフィールドの値(配列)の要素がユニークになるように値を追加するサンプルです。

```swift:Swift3
let obj5 = NCMBObject(className: "TestClass")
obj5?.objectId = "***更新するobjectId***"
obj5?.fetchInBackground({ (error) in
    if error != nil {
        // 取得に失敗した場合の処理
    }else{
        // 取得に成功した場合の処理
        // フィールドの値（配列）の要素がユニークになるように値を追加する
        obj5?.addUniqueObjects(from: ["A", "D", "F"], forKey: "array")
        obj5?.saveInBackground({ (error) in
            if error != nil {
                // 保存に失敗した場合の処理
            }else{
                // 保存に成功した場合の処理
            }
        })
    }
})
```

```swift:Swift2
let obj5 = NCMBObject(className: "TestClass")
obj5.objectId = "***更新するobjectId***"
obj5.fetchInBackgroundWithBlock { (error: NSError!) -> Void in
    if error != nil {
        // 取得に失敗した場合の処理
    }else{
        // 取得に成功した場合の処理
        // フィールドの値（配列）の要素がユニークになるように値を追加する
        obj5.addUniqueObjectsFromArray(["A", "D", "F"], forKey: "array")
        obj5.saveInBackgroundWithBlock({ (error: NSError!) -> Void in
            if error != nil {
                // 保存に失敗した場合の処理
            }else{
                // 保存に成功した場合の処理
            }
        })
    }
}
```
* フィールドの値を["A", "B", "C"]と保存しているデータであれば、上のサンプルでは["A", "D", "F"]を追加するので、["A", "B", "C", "D", "F"]に更新されます。


## オブジェクトの削除
* 非同期で削除する場合は、deleteInBackgroundWithBlockメソッドを利用します。
* 以下はオブジェクトの取得、更新と同様にobjectIdを指定して、削除を行うサンプルです。

```swift:Swift3
// testクラスへのNCMBObjectを設定
let obj6 = NCMBObject(className: "TestClass")
// objectIdプロパティを設定
obj6?.objectId = "***更新するobjectId***"
// 設定されたobjectIdを元にデータストアからデータを取得
obj6?.fetchInBackground({ (error) in
    if error != nil {
        // 取得に失敗した場合の処理
    }else{
        // 取得に成功した場合の処理
        obj6?.deleteInBackground({ (error) in
            if error != nil {
                // 削除に失敗した場合の処理
            }else{
                // 削除に成功した場合の処理
            }
        })
    }
})
```

```swift:Swift2
// testクラスへのNCMBObjectを設定
let obj6 = NCMBObject(className: "TestClass")
// objectIdプロパティを設定
obj6.objectId = "***更新するobjectId***"
// 設定されたobjectIdを元にデータストアからデータを取得
obj6.fetchInBackgroundWithBlock({ (error: NSError!) -> Void in
    if error != nil {
        // 取得に失敗した場合の処理
    }else{
        // 取得に成功した場合の処理
        obj6.deleteInBackgroundWithBlock({ (error: NSError!) -> Void in
            if error != nil {
                // 削除に失敗した場合の処理
            }else{
                // 削除に成功した場合の処理
            }
        })
    }
})
```

# 参考
* 元サイト
 * [ニフクラmobile backendのiOS(Objectiv-C)用ドキュメント](https://mbaas.nifcloud.com/doc/current/index.html)