【Swift】Parse.comからサービス移行する＜ドキュメント比較＞

# 概要
![Parse移行.png](https://qiita-image-store.s3.amazonaws.com/0/112032/d68b5799-db62-6756-77ee-7db3a57c5aa8.png)

* Parse.comサービス終了に伴い、別のmBaaSへ乗り換えなければ…
という方々から[ニフクラ mobile backend ](http://mb.cloud.nifty.com/)はたくさんお問い合わせをいただいております。「乗り換えるのって簡単じゃないんでしょ？」と不安の方も多いはず。そこで[ニフクラ mobile backend ](http://mb.cloud.nifty.com/)に乗り換えた場合のドキュメントについてまとめてみました！
* Parse.comでSwiftを使っていて、Parse.com終了後もSwiftとmBaaSサービスを使いたい！！という方向けの記事になっています。
* Parse.comが提供しているのサービスでニフクラ mobile backend の機能に置き換え可能なサービスは以下です
 * データベース
 * プッシュ通知
 * ソーシャルログイン（twitter、facebook）
* これらの機能のドキュメントを書き換えてみます
 * 今回はデータベース周りを書きました


# 準備と実際にドキュメントを試す場合は…
* Parse.com利用中の既存アプリで[ニフクラ mobile backend ](http://mb.cloud.nifty.com/)を使用する場合は事前設定が必要になります
* 詳しくは下記リンクを見てください
 * [＜CocoaPods＞SwiftでmBaaSを始めよう！](http://qiita.com/natsumo/items/b2d18d87d57300c8d81c)
 * [Parse.comからNIFTY Cloud mobile backendへプッシュ通知を移行する（iOS編）](http://qiita.com/ndyuya/items/23fab5992cf23812daf3)

# **Parse.comとニフクラ mobile backend のドキュメント比較をしてみる**
Parseのドキュメントと同じ内容をニフクラ mobile backend でも同じ様に書いてみる
![ParseからmBaaS.png](https://qiita-image-store.s3.amazonaws.com/0/112032/b9c39d1c-478e-c950-368f-2431cb614f78.png)

## **Saving Objects(オブジェクトの保存)**
* `score: 1337, playerName: "Sean Plott", cheatMode: false`を保存します

```swift:Parse.com
var gameScore = PFObject(className:"GameScore")
gameScore["score"] = 1337
gameScore["playerName"] = "Sean Plott"
gameScore["cheatMode"] = false
gameScore.saveInBackgroundWithBlock {(success: Bool, error: NSError?) -> Void in
  if (success) {
    // The object has been saved.
  } else {
    // There was a problem, check error.description
  }
}
```
```swift:ニフクラmobilebackend 
var gameScore = NCMBObject(className: "GameScore")
gameScore.setObject(1337, forKey: "score")
gameScore.setObject("Sean Plott", forKey: "playerName")
gameScore.setObject(false, forKey: "cheatMode")
gameScore.saveInBackgroundWithBlock {(error: NSError?) -> Void in
  if (error == nil) {
    // The object has been saved.
  } else {
    // There was a problem, check error.description
  }
}
```
**Q. 違いは？**
A. 思ったより似ていました

* Parseは「**`PFObject`**」、ニフクラ mobile backend は「**`NCMBObject`**」を使います
* それぞれのオブジェクトにクラスインスタンスを作るところは同じ！
* 値の持たせ方が少し違うけど大差ない
 * Parse.は`インスタンス["キー"] = 値`
 * ニフクラ mobile backend は`インスタンス.setObject(値, forKey: "キー")`
* 保存には同じく`saveInBackgroundWithBlock`メソッドを使うが実行時に帰ってくる引数が少し違う

## **Retrieving Objects(オブジェクトの取得)**
* どちらも保存したデータには固有のIDである`objectId`が付与されます
* `objectId`で検索し、値を取得します

```swift:Parse.com
var query = PFQuery(className:"GameScore")
query.getObjectInBackgroundWithId("xWMyZEGZ"){(gameScore: PFObject?, error: NSError?) -> Void in
  if error == nil && gameScore != nil {
    print(gameScore)
  } else {
    print(error)
  }
}
```
```swift:ニフクラmobilebackend 
var query = NCMBQuery(className:"GameScore")
query.getObjectInBackgroundWithId("xWMyZEGZ"){(gameScore: NCMBObject?, error: NSError?) -> Void in
  if error == nil && gameScore != nil {
    print(gameScore)
  } else {
    print(error)
  }
}
```

**Q. 違いは？**
A. ほぼない！

* Parse.comは「**`PFQuery`**」、ニフクラ mobile backend は「**`NCMBQuery`**」を使います
* それ以外は全く一緒でした～！


### オブジェクトが持つ値の取得
* 「オブジェクトの取得」で取得したオブジェクトから値を取りだします

```swift:Parse.com
let score = gameScore["score"] as Int
let playerName = gameScore["playerName"] as String
let cheatMode = gameScore["cheatMode"] as Bool
```
```swift:ニフクラmobilebackend 
let score = gameScore.objectForKey("score")
let playerName = gameScore.objectForKey("playerName")
let cheatMode = gameScore.objectForKey("cheatMode")
```

#### 特別値の取得
* 先程紹介した`ObjectId`以外にも共通している値として、オブジェクトが作成された時間`createdAt`(`createdAtDate`)とオブジェクトが更新された時間`updatedAt`(`updateDate`)があります
* 表記が違うだけで後は同様に取得できます！

```swift:Parse.com
let objectId = gameScore.objectId
let updatedAt = gameScore.updatedAt
let createdAt = gameScore.createdAt
```
```swift:ニフクラmobilebackend 
let objectId = gameScore.objectId
let updatedAt = gameScore.updateDate
let createdAt = gameScore.createDate
```

**Q. 違いは？**
A. 保存と同様大差ない！

* 値の取り出しは保存と同様少し書き方が違うだけ
* 特別な値スペルが微妙に違うけれど、値としては同じものを持っていることがわかる
 * それぞれアップデート時刻と値が保存された時刻をデータとして保持する
 * Parse.comでは「`updatedAt`」、「`createdAt`」、ニフクラ mobile backend では「`updateDate`」「`createDate`」と表記が少し違う
* 構造がだいぶ近い！

## **Updating Objects(オブジェクトの更新)**

* `GameScore`クラスのオブジェクト（`objectId`で検索）の値を更新します
 * 変更後の値：`score: 1338, playerName: "Sean Plott"(変更なし), cheatMode: true`

```swift:Parse.com
var query = PFQuery(className:"GameScore")
query.getObjectInBackgroundWithId("xWMyZEGZ"){(gameScore: PFObject?, error: NSError?) -> Void in
  if error != nil {
    print(error)
  } else if let gameScore = gameScore {
    gameScore["cheatMode"] = true
    gameScore["score"] = 1338
    gameScore.saveInBackground()
  }
}
```
```swift:ニフクラmobilebackend 
var query = NCMBQuery(className:"GameScore")
query.getObjectInBackgroundWithId("xWMyZEGZ"){(gameScore: NCMBObject?, error: NSError?) -> Void in
  if error != nil {
    print(error)
  } else if let gameScore = gameScore {
    gameScore.setObject(true, forKey: "cheatMode")
    gameScore.setObject(1338, forKey: "score")
    gameScore.InBackgroundWithBlock(nil)
  }
}
```

* `score`フィールドの値（数値）を1増やします

```swift:Parse.com
gameScore.incrementKey("score")
gameScore.saveInBackgroundWithBlock{(success: Bool, error: NSError?) -> Void in
  if (success) {
    // The score key has been incremented
  } else {
    // There was a problem, check error.description
  }
}
```
```swift:ニフクラmobilebackend 
gameScore.incrementKey("score")
gameScore.saveInBackgroundWithBlock{(error: NSError?) -> Void in
  if (error == nil) {
    // The score key has been incremented
  } else {
    // There was a problem, check error.description
  }
}
```

* 既存データに新しいデータ（`skills`フィールドに配列`["flying", "kungfu"]`）を追加します

```swift:Parse.com
gameScore.addUniqueObjectsFromArray(["flying", "kungfu"], forKey:"skills")
gameScore.saveInBackground()
```
```swift:ニフクラmobilebackend 
gameScore.addUniqueObjectsFromArray(["flying", "kungfu"], forKey:"skills")
gameScore.saveInBackgroundWithBlock(nil)
```

**Q. 違いは？**
A. ほぼ一緒

* Parse.comのメソッド`saveInBackground()`はニフクラ mobile backend には無かったので`saveInBackgroundWithBlock(nil)`で代用しました

## **オブジェクトの削除(Deleting Objects)**
* オブジェクトを削除します

```swift:Parse.com
gameScore.deleteInBackground()
```
```swift:ニフクラmobilebackend 
gameScore.deleteInBackgroundWithBlock(nil)
```

* `playerName`フィールドの値を削除します

```swift:Parse.com
// After this, the playerName field will be empty
gameScore.removeObjectForKey("playerName")
// Saves the field deletion to the Parse Cloud
gameScore.saveInBackground()
```
```swift:ニフクラmobilebackend 
// After this, the playerName field will be empty
gameScore.removeObjectForKey("playerName")
// Saves the field deletion to the Parse Cloud
gameScore.saveInBackgroundWithBlock(nil)
```

**Q. 違いは？**
A. ほぼ一緒

* Parse.comのメソッド`deleteInBackground()`はニフクラ mobile backend には無かったので`deleteInBackgroundWithBlock(nil)`で代用しました

## **リレーション(Relational Data)**

### ポインター
* `Comment`クラスの`parent`フィールドに、`Post`クラスのオブジェクトをポインターとして設定します

```swift:Parse.com
// Create the post
var myPost = PFObject(className:"Post")
myPost["title"] = "I'm Hungry"
myPost["content"] = "Where should we go for lunch?"

// Create the comment
var myComment = PFObject(className:"Comment")
myComment["content"] = "Let's do Sushirrito."

// Add a relation between the Post and Comment
myComment["parent"] = myPost

// This will save both myPost and myComment
myComment.saveInBackground()
```
```swift:ニフクラmobilebackend
// Create the post
var myPost = NCMBObject(className:"Post")
myPost.setObject("I'm Hungry", forKey: "title")
myPost.setObject("Where should we go for lunch?", forKey: "content")

// Create the comment
var myComment = NCMBObject(className:"Comment")
myComment.setObject("Let's do Sushirrito.", forKey: "content")

// Add a relation between the Post and Comment
myComment.setObject(myPost, forKey: "parent")

// This will save both myPost and myComment
myComment.saveInBackgroundWithBlock(nil)
```

* `parent`フィールドに`Post`クラスのデータ（`objectId`が`"1zEcyElZ80"`）を関連付ける

```swift:Parse.com
// Add a relation between the Post with objectId "1zEcyElZ80" and the comment
myComment["parent"] = PFObject(withoutDataWithClassName:"Post", objectId:"1zEcyElZ80")
```
```swift:ニフクラmobilebackend
// Add a relation between the Post with objectId "1zEcyElZ80" and the comment
myComment.setObject(NCMBObject(className: "Post", objectId: "1zEcyElZ80"), forKey: "parent")
```

* 関連付けされたのデータにアクセスする

```swift:Parse.com
var post = myComment["parent"] as PFObject
post.fetchIfNeededInBackgroundWithBlock{(post: PFObject?, error: NSError?) -> Void in
  let title = post?["title"] as? NSString
  // do something with your title variable
}
```
```swift:ニフクラmobilebackend
var post = myComment.objectForKey("parent") as! NCMBObject
post.fetchInBackgroundWithBlock{(error: NSError?) -> Void in
  let title = post.objectForKey("title") as? NSString
  // do something with your title variable
}
```

### リレーション
* 現在ログイン中のユーザー（カレントユーザー）の`likes`フィールドに`Post`クラスを関連付ける

```swift:Parse.com
var user = PFUser.currentUser()
var relation = user.relationForKey("likes")
relation.addObject(post)
user.saveInBackgroundWithBlock{(success: Bool, error: NSError?) -> Void in
  if (success) {
    // The post has been added to the user's likes relation.
  } else {
    // There was a problem, check error.description
  }
}
```
```swift:ニフクラmobilebackend
var user = NCMBUser.currentUser()
var relation = user.relationforKey("likes")
relation.addObject(post)
user.saveInBackgroundWithBlock{(error: NSError!) -> Void in
  if (error == nil) {
    // The post has been added to the user's likes relation.
  } else {
    // There was a problem, check error.description
  }
}
```

* 関連付けを削除する

```swift:Parse.com
relation.removeObject(post)
```
```swift:ニフクラmobilebackend
relation.removeObject(post)
```

**Q. 違いは？**
A. ほぼ一緒

* ニフクラmobilebackendには`NCMBRelation`が用意されているので、`var relation = user.relationForKey("likes")`は`var relation = NCMBRelation(className: "user", key: "likes")`と表記可能だった


## **Data Types(データの種類)**
* 扱えるデータは以下です

| データ型 | サンプル |
|:----------|:-----------|
|number 数値|123|
|boolean 真偽値|true または false|
|string 文字列|ABC|
|date 日付|2013-09-06T01:51:03.606Z|
|array 配列|["orange", "apple", "grape"]|
|dictionary オブジェクト|["name":"orange"]|

```swift:値を設定.Swift
let number = 42
let bool = false
let string = "the number is \(number)"
let date = NSDate()
let array = [string, number]
let dictionary = ["number": number, "string": string]
let null = NSNull()
let pointer = PFObject(objectWithoutDataWithClassName:"MyClassName", objectId: "xyz")
```

* これらの値を保存します

```swift:Parse.com
var bigObject = PFObject(className:"BigObject")
bigObject["myNumberKey"] = number
bigObject["myBooleanKey"] = bool
bigObject["myStringKey"] = string
bigObject["myDateKey"] = date
bigObject["myArrayKey"] = array
bigObject["myObjectKey"] = dictionary // shows up as 'object' in the Data Browser
bigObject["anyKey"] = null // this value can only be saved to an existing key
bigObject["myPointerKey"] = pointer // shows up as Pointer <MyClassName> in the Data Browser
bigObject.saveInBackground()
```
```swift:ニフクラmobilebackend
var bigObject = NCMBObject(className:"BigObject")
bigObject.setObject(number, forKey: "myNumberKey") // 数値
bigObject.setObject(bool, forKey: "myBooleanKey") // 真偽値
bigObject.setObject(string, forKey: "myStringKey") // 文字列
bigObject.setObject(date, forKey: "myDateKey") // 日付
bigObject.setObject(array, forKey: "myArrayKey") // 配列
bigObject.setObject(dictionary, forKey: "myObjectKey") // オブジェクト
bigObject.setObject(null, forKey: "anyKey") // null
bigObject.setObject(pointer, forKey: "myPointerKey") // ポインター
bigObject.saveInBackgroundWithBlock(nil)
```

* すべて対応していました♪
* ちなみに、ニフクラmobilebackendだと以下も使えます

| データ型 | サンプル |
|:----------|:-----------|
|緯度経度|33.857619,122.378986|


# まとめ
* 思ったより違いが無かった！！！というはほぼ一緒だった！
* 乗り換えやすさがわかっていただけましたか?
* 乗り換えやすいだけじゃなく、サービスも充実しています！
 * 国産のmBaaS
 * 基本無料ですべての機能を利用可能！
 * 日本語でのテクニカルサポート(有償ユーザーのみ)
* ぜひこの機会に[ニフクラ mobile backend ](http://mb.cloud.nifty.com/)を使ってみませんか？

# 今すぐ体験！ニフクラ mobile backend 使いやすさ実感！
## コンテンツのご紹介
今すぐ[ニフクラ mobile backend ](http://mb.cloud.nifty.com/)をつかってみたい方に！すぐ試せるコンテンツを用意しています♪

### **【IoT入門】スマホで加速度センサーと位置情報を取得してクラウドに保存しよう！**
★iOSとAndroidハイブリッド開発ができる開発環境[Monaca](https://ja.monaca.io/)（HTML5・JavaScript）を使用してアプリを作成してみましょう(難易度☆☆☆☆★)

* 説明資料
 * [概要説明と事前準備](http://www.slideshare.net/mobilebackend/iot20160407-60589628)
 * [ハンズオン資料①](http://www.slideshare.net/mobilebackend/iot20160407)
 * [ハンズオン資料②](http://www.slideshare.net/mobilebackend/iot20160407-60589536)

### **Swiftアプリにプッシュ通知を組み込もう！**
★Swiftでプッシュ通知を実装したアプリ作成してみましょう(難易度：☆★★★★)

* 説明資料
 * [事前準備資料](http://www.slideshare.net/IKEDA_NATSUMO/swift-60100293)…プッシュ通知に必要な証明書の作り方
 * ハンズオン資料　※ただいま準備中です（_2016/04/07_現在）

### **シューティングゲームにオンラインランキングとゴースト機能を追加しましょう！**
★オンラインランキングとゴースト機能のついたUnityゲームを作りましょう！(難易度：☆☆☆☆★)

* 説明資料
 * [シューティングゲームにランキングとゴースト機能を追加しましょう！～Unity×クラウドをつかって～](http://www.slideshare.net/mobilebackend/unity-60586717)


### **Coming Soon ...**
★追加コンテンツが用意出来次第ご紹介します！！



# 参考
* [Parse.comドキュメントページ](https://parse.com/docs/ios/guide#objects)
* [ニフクラ mobile backend ドキュメントページ](http://mb.cloud.nifty.com/doc/current/datastore/basic_usage_ios.html)


#### **▼Pares.com終了に伴うニフクラ mobile backend への移行のご相談はこちら▼**
**https://inquiry.nifty.com/webeq/pub/mbaas/replace**