NCMBでは公式SDKとしてSwift/Objective-C/Kotlin/Java/Unity/JavaScript SDKを用意しています。また、それ以外にもコミュニティSDKとして、非公式ながらFlutter/React Native/Google Apps Script/C#/Ruby/Python/PHPなど幅広い言語向けにSDKが開発されています。
今回は公式SDKの一つ、Kotlin SDKを使ってTodoアプリを作ってみます。まず画面の仕様とSDKの初期化について解説します。
完成版のコード
作成したデモアプリのコードはNCMBMania/Kotlin_Todo: Kotlin SDKを使ったTodoアプリですにアップロードしてあります。
ベースアプリのダウンロードまで
ベースについて
今回はKotlin + Jetpack Composeの組み合わせになっています。
NCMB SDKのインストール
NCMB SDKはReleases · NIFCLOUD-mbaas/ncmb_kotlinよりダウンロードします。Zipファイルをダウンロードして展開すると、NCMB.jarというファイルが取得できます。このファイルをKotlinプロジェクトの app/libs
以下にコピーします。
app/build.gradle
を開いて編集します。そして以下の4つを追加します。一番下のkotlinx-coroutinesを追加すると、保存処理などが同期的に処理できるようになります。
dependencies {
// 省略
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'
}
編集したら同期します。これでNCMB SDKの利用準備が整います。
Composeについて
今回は以下のComposeを用意しています。
- Navigation
- FormScreen
- ListScreen
- TaskRow
- DetailScreen
Navigation
各画面への遷移をハンドリングしています。Navigationコンポーネントを利用しています。
FormScreen
タスクの入力フォームです。新規登録、編集の両方で利用しています。
ListScreen
タスクを一覧表示します。各行のデータはTaskRowにて表示しています。
TaskRow
タスクの一覧表示で、各行を表示するのに使います。
DetailScreen
一覧表示で選択された、タスクの詳細表示を行います。
コードの編集
MainActivity.ktを開いてNCMBの初期化を行います。 super.onCreate
の下でNCMBを初期化します。 YOUR_APPLICATION_KEY
と YOUR_CLIENT_KEY
はそれぞれあなたのものと書き換えてください。
// 1. NCMBをインポート
import com.nifcloud.mbaas.core.NCMB
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 2. NCMBを初期化
NCMB.initialize(
this,
"YOUR_APPLICATION_KEY",
"YOUR_CLIENT_KEY"
)
setContent {
// 省略
}
}
}
これでNCMBの初期化が終わり、利用準備が整います。
タスク保存処理の実行
Navigation.ktの修正
Navigation.kt
にて、NCMB SDKをインポートします。
// 1. NCMBObjectをインポートします
import com.nifcloud.mbaas.core.NCMBObject
次に var obj = 0
をNCMBObjectに変更します。
// 2. 新しいNCMBObjectを作成
val obj = NCMBObject(className)
次の修正に入る前に、 Util.kt に関数を追加します。
Util.ktについて
文字列をNCMBObjectに戻す関数 strToNCMBObject
と JSONObjectをNCMBAclに変換する関数 getAcl
を用意します。まずSDKをインポートします。
// 1. NCMBAcl/NCMBObjectを読み込みます
import com.nifcloud.mbaas.core.NCMBObject
import com.nifcloud.mbaas.core.NCMBAcl
strToNCMBObjectの内容です。画面遷移する際に受け取った文字列をJSONObjectにし、それをNCMBObjectのフィールド(DBで言うカラム相当)にセットしていきます。
objectIdはNCMBのデータ管理におけるユニークキー、createDateは作成日時、updateDateは更新日時、aclはアクセス権限に関するフィールドになります。これらはNCMBの規定フィールドです。className(クラス名)はDBで言うところのテーブル名相当になります。
// 2. 文字列をNCMBObjectに復元する関数です
fun strToNCMBObject(str: String, className: String): NCMBObject {
val json = JSONObject(str)
val obj = NCMBObject(className = className)
json.keys().forEach { key ->
when (key) {
"objectId" -> {
obj.setObjectId(json.get(key) as String)
}
"createDate" -> {
obj.setCreateDate(getDate(json.get(key) as String))
}
"updateDate" -> {
} // obj.put("updateDate", getDate(json.get(key) as String))}
"acl" -> {
obj.setAcl(getAcl(json.get(key) as JSONObject))
}
else -> obj.put(key, json.get(key))
}
}
return obj
}
上記関数で呼ばれている getAcl
の内容です。ACLは誰がデータにアクセスできるかを指定するものです。NCMBでは以下の条件に対して、読み書き設定ができます。
- 誰でも
- 特定のユーザー
- 特定のグループ
// 3. NCMBAclを復元する関数です
fun getAcl(obj: JSONObject): NCMBAcl {
val acl = NCMBAcl()
obj.keys().forEach { key ->
val map = obj.get(key) as JSONObject
when {
key == "*" -> {
if (!map.isNull("read") && map.get("read") as Boolean) {
acl.publicReadAccess = true
}
if (!map.isNull("write") && map.get("write") as Boolean) {
acl.publicWriteAccess = true
}
}
key.indexOf("role:") > -1 -> {
val match = "role:(.*)$".toRegex().find(key)
val roleName = match?.groups?.get(1)?.value
if (roleName != null) {
if (!map.isNull("read") && map.get("read") as Boolean) {
acl.setRoleReadAccess(roleName, true)
}
if (!map.isNull("write") && map.get("write") as Boolean) {
acl.setRoleWriteAccess(roleName, true)
}
}
}
else -> {
if (!map.isNull("read") && map.get("read") as Boolean) {
acl.setUserReadAccess(key, true)
}
if (!map.isNull("write") && map.get("write") as Boolean) {
acl.setUserWriteAccess(key, true)
}
}
}
}
return acl
}
Navigation.ktの修正(2)
関数が準備できたので、 Navigation.kt
の修正に戻ります。
// 4. 受け取った文字列からNCMBObjectを復元します
val obj = strToNCMBObject(backStackEntry.arguments!!.getString("task")!!, className)
FormScreen.ktの修正
NCMB SDKをインポートします
// 1. NCMBExceptionとNCMBObjectを読み込みます
import com.nifcloud.mbaas.core.NCMBException
import com.nifcloud.mbaas.core.NCMBObject
元々はエラーを出さないためにAnyにしていたのをNCMBObjectに直します。
// 2. AnyをNCMBObjectに変更します
fun FormScreen(navController: NavController, obj: Any) {
// ↓ 修正後
fun FormScreen(navController: NavController, obj: NCMBObject) {
インスタンス変数を設定します。今回はTodoのタイトル(title)と本文(body)をデフォルト値として受け取ります。これは編集時を想定してのことです。
// 3. objectIdを入れます
var objectId = obj.getObjectId();
// 4. titleをrememberで定義します。デフォルト値はNCMBObjectのtitleです
var title by remember { mutableStateOf(if (objectId != null) obj.getString("title")!! else "") }
// 5. bodyをrememberで定義します。デフォルト値はNCMBObjectのbodyです
var body by remember { mutableStateOf(if (objectId != null) obj.getString("body")!! else "") }
保存処理
保存を実行する save
メソッドは次のようになります。関数全体を紹介します。
// 保存処理
val save = {
progress = true
// 6. 保存処理を記述します
try {
obj.put("title", title)
obj.put("body", body)
obj.save()
navController.navigate("list")
} catch(e: NCMBException){
Log.d("INFO", e.message)
message = "タスクが保存できませんでした"
showDialog = true
}
progress = false
}
一覧画面の作成
ListScreen.ktの編集
NCMB SDKを読み込みます。
// 1. NCMBCallback/NCMBObject/NCMBQueryを読み込みます
import com.nifcloud.mbaas.core.NCMBCallback
import com.nifcloud.mbaas.core.NCMBObject
import com.nifcloud.mbaas.core.NCMBQuery
NCMBからデータを取得する際には NCMBQuery
クラスを使います。 NCMBQuery.forObject
で指定するクラス名は、NCMBのデータストアにあるクラス名になります。データを取得後、 remember
で定義した ary
に結果を入れることで、画面描画に使われます。
results は List<dynamc>
なので、 List<NCMBObject>
にキャストしています。
// 2. aryをrememberのNCMBObjectのListで定義します。デフォルトは空です
var ary = remember { mutableStateOf<List<NCMBObject>>(emptyList()) }
// 3. NCMBQueryを定義します。クラス名はTaskです。
val query = NCMBQuery.forObject("Task")
// 4. 検索を実行して、結果をaryに格納します
query.findInBackground(NCMBCallback { e, results ->
if (e == null) {
ary.value = results as List<NCMBObject>
}
})
元々 ary
をただの配列で定義しています(エラー防止のため、単なる配列でした)。 remember
にしたので、利用法を変えます。
// 5. ary -> ary.value に書き直してください
items(ary) { obj ->
// ↓
items(ary.value) { obj ->
TaskRow.kt の修正
NCMB SDKを読み込みます。
// 1. NCMBObjectを読み込みます
import com.nifcloud.mbaas.core.NCMBObject
Any
型だったのを NCMBObject
にします(エラー防止のため、Any型にしてました)。
// 2. Any -> NCMBObjectにします
fun TaskRow(obj: Any, navController: NavController) {
// ↓
fun TaskRow(obj: NCMBObject, navController: NavController) {
タスクのタイトルを画面に表示します。
// 4. NCMBObjectのtitleを表示します
Text(obj.getString("title")!!, fontSize = 25.sp)
タスクをタップした後の処理の実装
TaskRow.kt の修正
詳細画面に遷移する際にはNCMBObjectを文字列にして送らないといけません。そこで、JSONObjectを使ってNCMBObjectを文字列化します。
// 3. NCMBObjectを文字列化します
obj.keys.forEach {key ->
json.put(key, obj.get(key)!!)
}
Navigation.ktの修正
detail/task={task}
のルーティングでも、文字列をNCMBObjectに戻す処理にします。
// 3. 受け取った文字列からNCMBObjectを復元します
val obj = strToNCMBObject(backStackEntry.arguments!!.getString("task")!!, className)
DetailScreen.ktの修正
NCMB SDKを読み込みます。
// 1. NCMBObjectをインポート
import com.nifcloud.mbaas.core.NCMBObject
Any型をNCMBObjectにします。
// 2. Any -> NCMBObjectに変更します
fun DetailScreen(navController: NavController, obj: Any) {
// ↓
fun DetailScreen(navController: NavController, obj: NCMBObject) {
受け取ったNCMBObjectを画面に表示します。
// 5. NCMBObjectのtitleを表示します
Text(text = obj.getString("title")!!,
fontSize = 35.sp,
modifier = Modifier.padding(5.dp)
)
// 6. NCMBObjectのbodyを表示します
Text(text = obj.getString("body")!!,
fontSize = 30.sp,
modifier = Modifier.padding(5.dp)
)
ゴミ箱アイコンをタップした際のイベントは、NCMBObjectの削除処理です。
// 4. NCMBObjectを削除します
obj.delete()
編集アイコンをタップした際のイベントは、NCMBObjectを文字列化した上で edit/task
へ遷移します。
// 3. NCMBObjectを文字列化します
obj.keys.forEach {key ->
json.put(key, obj.get(key)!!)
}
まとめ
ここまででNCMBのデータストアを使ったTodoアプリ開発は完了となります。NCMBには他にも認証やファイル保存機能、プッシュ通知などがあります。ぜひあなたのアプリ開発に役立ててください。