NCMBのKotlin SDKにおけるデータの取得方法

NCMB（ニフクラ mobile backend）では多数のプラットフォームに対してSDKを提供しています。公式のものとしてはObjective-C、Swift、Java、Unity、JavaScriptなどがあります。コミュニティベースではReact Native、Flutter、Google Apps Script、VBA、Ruby、PHP、C#(Xamarin)などもあります。

そしてまだデベロッパープレビューながら、新しいSDKとしてKotlin SDKが登場しました。今回はデータストアに入っているデータを取得、各フィールドの値を扱う方法を解説します。

## Androidプロジェクトの作成

Android Studioでプロジェクトを作成します。Androidは8.0以降を選択してください。

![ScreenShot_ 2021-07-12 21.36.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/0d99ad13-fe57-a0ab-536d-e1d1d7fbda6c.png)


## SDKのダウンロード

SDKは[GitHubのリリース](https://github.com/NIFCLOUD-mbaas/ncmb_kotlin/releases)にて公開されています。基本的に最新のものを選んでください。ダウンロードして解凍するとNCMB.jarというファイルがあるので、それをAndroidプロジェクトの app/libs/ 以下に保存します。

## build.gradleの修正

`app/build.gradle` を開いてライブラリを追加します。同期処理用のライブラリもありますが、アプリの場合にはネットワーク処理は非同期が基本なので通常は不要かと思います。

```
// 省略
dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.1'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    // 追加 
    implementation 'com.squareup.okhttp3:okhttp:4.8.1'
    implementation 'com.google.code.gson:gson:2.3.1'
    api files('libs/NCMB.jar')

    // 同期処理を使用する場合はこちらを追加
    // implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9'
}
```

## AndroidManifest.xmlの修正

AndroidManifest.xmlを開いて、applicationタグの上で uses-permission を追加します。

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.nifcloud.mbaas.ncmbhello">
    <!-- この2行を追加 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
```

## 必要なライブラリの読み込み

今回はMainActivity.ktにてライブラリを読み込みます。

```kotlin
import com.nifcloud.mbaas.core.NCMB
import com.nifcloud.mbaas.core.NCMBCallback
import com.nifcloud.mbaas.core.NCMBObject
```

## NCMB SDKの初期化

続いて onCreate でSDKを初期化します。

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    NCMB.initialize(
        this.getApplicationContext(),
        "YOUR_APPLICATION_KEY",
        "YOUR_CLIENT_KEY"
    );
```

## データの取得

現在はデータストアに対してクエリを実行するクラス NCMBQuery の内容が作られていません。そこでNCMBObjectのfetchInBackgroundを使います。近く、データの取得処理自体は変わると思います。

```kotlin
var obj = NCMBObject("TestClass")
obj.setObjectId("0UjRUrPe3SPM0Mfh")
obj.fetchInBackground(NCMBCallback { e, _ ->
    if (e != null) {
        // エラー
        toast.setText("エラーが発生しました")
    } else {
        // ここでデータの処理
    }
})
```

ではここから各フィールドの値を型別に取得します。

## 規定のフィールド

objectId / createDate / updateDate は規定のフィールドです。取得するための専用メソッドが用意されています。

```kotlin
// 規定のフィールド
Log.d("[DEBUG]", obj.getObjectId()!!)
// -> 0UjRUrPe3SPM0Mfh
Log.d("[DEBUG]", obj.getCreateDate().toString())
// -> Mon Jun 28 13:17:18 GMT+09:00 2021
Log.d("[DEBUG]", obj.getUpdateDate().toString())
// -> Mon May 31 11:12:13 GMT+09:00 2021
```

## 文字列型

文字列の場合は getString を使います。オプショナルなので `!!` を付けています。

```kotlin
// 文字列型
if (obj.isContainsKey("string")) {
    Log.d("[DEBUG]", obj.getString("string")!!)
    // -> 文字列です
}
```

## 数値型

数値の場合は get で取得した後、キャストします。


```kotlin
// 数字型
if (obj.isContainsKey("number")) {
    val num = obj.get("number") as Int
    Log.d("[DEBUG]", "数字： $num")
    // -> 数字： 100
}
```

## 真偽値型

真偽値型も同様です。

```kotlin
// 真偽値型
if (obj.isContainsKey("bool")) {
    val bol = obj.get("bool") as Boolean
    Log.d("[DEBUG]", "真偽値： $bol")
    // 真偽値： true
}
```

## 日付型

日付型は次のような形式になっています。

| 項目 | 意味 |
|------|------|
| __type | Date という文字列固定 |
| iso    | 日時がISO8601で入っています |

そこでJSONObjectに変換し、さらに `DateTimeFormatter.ISO_OFFSET_DATE_TIME` でパースします。これは正直面倒なので、近く専用のメソッドが用意されるかも知れません。

```kotlin
// 日付型
if (obj.isContainsKey("date")) {
    Log.d("[DEBUG]", "日付オブジェクト： ${obj.get("date")}")
    // -> 日付オブジェクト： {"__type":"Date","iso":"2021-06-06T01:51:03.606Z"}
    val o = JSONObject(obj.getString("date")!!)
    Log.d("[DEBUG]", "日付文字列： ${o["iso"]}")
    // -> 日付文字列： 2021-06-06T01:51:03.606Z
    val date = LocalDateTime.parse(o.getString("iso"), DateTimeFormatter.ISO_OFFSET_DATE_TIME)
    Log.d("[DEBUG]", "日付： $date")
    // -> 日付： 2021-06-06T01:51:03.606
}
```

## 位置情報型

位置情報も日付と同じく、専用の形式になっています。

| 項目 | 意味 |
|------|------|
| __type | GeoPoint という文字列固定 |
| latitude | 緯度が数値で入っています |
| longitude | 経度が数値で入っています |

```kotlin
// 位置情報型
if (obj.isContainsKey("geo")) {
    Log.d("[DEBUG]", "位置情報オブジェクト： ${obj.get("geo")}")
    // -> 位置情報オブジェクト： {"__type":"GeoPoint","longitude":122.378986,"latitude":33.857619}
    val o = JSONObject(obj.getString("geo")!!)
    val lat = o["latitude"] as Double
    val lng = o["longitude"] as Double
    Log.d("[DEBUG]", "緯度： $lat / 経度: $lng")
    // 緯度： 33.857619 / 経度: 122.378986
}
```

## 配列

配列はJSONArrayとして取得できます。その後は各要素ごとに変換できます。たとえば以下は `[1, 2, 3]` という数字の配列が入っている場合です。

```kotlin
// 数字の配列
if (obj.isContainsKey("numbers")) {
    Log.d("[DEBUG]", "数字の配列： ${obj.get("numbers")}")
    // -> 数字の配列： [1,2,3]
    // obj.get("numbers") -> JSONArray
    var numbers = obj.get("numbers") as JSONArray
    Log.d("[DEBUG]", "numbers[0]: ${numbers[0]}, numbers[1]: ${numbers[1]}, numbers[2]: ${numbers[2]}")
    // -> numbers[0]: 1, numbers[1]: 2, numbers[2]: 3
}
```

## オブジェクト

JSONオブジェクトの場合は JSONObject、配列の場合は JSONArrayで返ってきます。これは配列と同じように操作できます。

```kotlin
// 型の混在した配列
if (obj.isContainsKey("array")) {
    Log.d("[DEBUG]", "オブジェクト： ${obj.get("array")}")
    // -> オブジェクト： [1,"string",[1,2,3],{"a":"b"}]
    val o = obj.get("array") as JSONArray
    val i = o[0] as Int
    Log.d("[DEBUG]", "i: $i")
    // -> i: 1
    val s = o[1] as String
    Log.d("[DEBUG]", "s: $s")
    // s: string
    val n = o[2] as JSONArray
    Log.d("[DEBUG]", "n[0]: ${n[0]}, n[1]: ${n[1]}, n[2]: ${n[2]}")
    // n[0]: 1, n[1]: 2, n[2]: 3
    val j = o[3] as JSONObject
    Log.d("[DEBUG]", "j[a]: ${j["a"]}")
    // j[a]: b
}
```

## まとめ

データストアでは柔軟にデータが格納できる一方で、アプリ上で扱う際には各型へキャストしなければいけません。日付や位置情報型のように、データストア特有の形式になっているものもありますので、その扱い時には注意してください。

[NIFCLOUD-mbaas/ncmb_kotlin: NIFCLOUD mobile backendのKotlin SDK](https://github.com/NIFCLOUD-mbaas/ncmb_kotlin)