Kotlin Jetpack Componentなどを使っていて、画面遷移を行う場合にはNavigation コンポーネントを使うと思います。このNavigation コンポーネントでは基本的に文字列や数字などを引数に指定できますが、オブジェクトをそのまま渡せないようです。
デスティネーション間でデータを渡す | Android デベロッパー | Android Developers
マスター・詳細画面を作る場合には、NCMBObjectのリストをマスター画面に表示して、詳細画面には選択されたNCMBObjectを表示するというフローがよくあります。そうした時に、選択される度に都度ネットワークから取得し直すのは帯域もAPIコール数も勿体ないです。そこで一旦NCMBObjectを文字列にして、Navigation コンポーネントで戻してみました。
遷移元の処理
遷移元では、以下のようにして選択されたNCMBObject obj
をJSONObjectにします。その上で toString
を使ってJSON文字列としてナビゲーションに渡します。
val moveDetail = {
val json = JSONObject()
obj.keys.forEach {key ->
json.put(key, obj.get(key)!!)
}
navController.navigate("detail/obj=${json.toString()}")
}
ナビゲーションの処理
ナビゲーション側では、objを文字列として受け取ります。その文字列を後述する関数 strToNCMBObject
に渡してNCMBObjectを受け取ります。遷移先の画面では普通に引数が受け取れるので、NCMBObjectを引き渡します。strToNCMBObjectの2つ目の引数はデータストアのクラス名です。
composable(
route = "detail/obj={obj}",
arguments = listOf(navArgument("obj") { type = NavType.StringType })
) { backStackEntry ->
val obj = strToNCMBObject(backStackEntry.arguments!!.getString("obj")!!, "DailyReport")
DetailScreen(navController, obj)
}
strToNCMBObjectの処理
strToNCMBObjectは、基本的にJSONに入っているデータをNCMBObjectのキーとして適用するだけです。ただ、幾つかの規定のフィールドの場合は処理を分けています。
特に updateDate
があると更新に失敗する(v1.1.0の場合。次の版では問題なくなるはずです)ので、省いています。
fun strToNCMBObject(str: String, className: String): NCMBObject {
val json = JSONObject(str)
val obj = NCMBObject(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
}
日付の変換
日付はISO8601フォーマットなので、それに合わせて変換しています。
fun getDate(str: String): Date {
val pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
val format = SimpleDateFormat(pattern)
format.timeZone = SimpleTimeZone(0, "UTC")
return format.parse(str)
}
ACLの変換
ACLの変換はぱっと見ではかなりややこしいです。これはACLが全員、ユーザー単位、グループ単位の3つに分かれているためです。この辺りも含め、将来のバージョンでは簡単に文字列とオブジェクトのやりとりができるようなる予定です。
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
}
まとめ
文字列とNCMBObjectの相互変換ができれば、アプリ内で自由にデータが取り回せるようになります。画面遷移する際には必須になると思うので、ぜひご利用ください。