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を使って日報アプリを作ってみます。前回は画面の仕様とSDKの初期化について解説しましたので、今回はログインとデータの保存について解説します。
完成版のコード
作成したデモアプリのコードはNCMBMania/Kotlin_DailyReportにアップロードしてあります。
ナビゲーションの振り分け
今回は認証前提とし、認証していない場合はLoginScreen、認証している場合はListScreenを表示します。ナビゲーションでは、認証状態を判定し、それに合わせて画面の出し分けをします。
fun Navigation() {
var startDestination by remember { mutableStateOf("list") }
val changeLoginStatus = {
val currentUser: NCMBUser = NCMBUser().getCurrentUser()
startDestination = if (currentUser.getObjectId() == null) "login" else "list"
Unit
}
changeLoginStatus()
val navController = rememberNavController()
NavHost(navController = navController, startDestination = startDestination) {
// 省略
}
}
ナビゲーションの設定
ナビゲーションのルーティング設定は次の通りです。一覧から詳細、詳細から編集へ遷移する際には対象データ NCMBObject
を文字列で受け取り、それをオブジェクトに復元した上で画面遷移しています。 strToNCMBObject
関数についてはKotlinのNavigation コンポーネントを使ってNCMBObjectを別画面に渡す - Qiitaにて解説しています。
val navController = rememberNavController()
NavHost(navController = navController, startDestination = startDestination) {
// ログイン画面
composable(route = "login") {
LoginScreen(navController, changeLoginStatus)
}
// 新規作成画面
composable(route = "form") {
val obj = NCMBObject("DailyReport")
FormScreen(navController, obj)
}
// 一覧画面
composable(route = "list") {
ListScreen(navController = navController)
}
// 詳細画面
composable(
route = "detail/obj={obj}",
arguments = listOf(navArgument("obj") { type = NavType.StringType })
) { backStackEntry ->
val obj = strToNCMBObject(backStackEntry.arguments!!.getString("obj")!!, "DailyReport")
DetailScreen(navController, obj)
}
// 編集画面
composable(
route = "edit/obj={obj}",
arguments = listOf(navArgument("obj") { type = NavType.StringType })
) { backStackEntry ->
val obj = strToNCMBObject(backStackEntry.arguments!!.getString("obj")!!, "DailyReport")
FormScreen(navController, obj)
}
}
ログイン画面
ログイン画面は LoginScreen
として定義しています。
@Composable
fun LoginScreen(navController: NavController, callback: () -> Unit) {
// ユーザー名
var userName by remember { mutableStateOf("") }
// パスワード
var password by remember { mutableStateOf("") }
// 処理中のフラグ
var progress by remember { mutableStateOf(false) }
// ダイアログ表示のフラグ
var showDialog by remember { mutableStateOf(false) }
// ログイン処理用の関数
val login = {
// 後述
}
// ダイアログ表示用
if (showDialog) {
AlertDialog(
onDismissRequest = {},
buttons = {
Button(onClick = {
showDialog = false
}) {
Text("OK")
}
},
title = {Text("認証エラー")},
text = {Text("認証できませんでした(${userName})")}
)
}
// 以下は画面構築
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
OutlinedTextField(
value = userName,
onValueChange = { userName = it },
modifier = Modifier.padding(20.dp),
label = { Text("ユーザ名") },
)
OutlinedTextField(
value = password,
onValueChange = { password = it },
modifier = Modifier.padding(20.dp),
visualTransformation = PasswordVisualTransformation(),
label = { Text("パスワード") },
)
Button(onClick = login,
enabled = !progress,
modifier = Modifier.padding(20.dp)
) {
if (progress) {
CircularProgressIndicator(
strokeWidth = 2.dp,
modifier = Modifier.size(24.dp)
)
} else {
Text("ログイン")
}
}
}
}
ログイン処理について
progressをtrueにすると、ボタンが無効化されて多重実行を防げます。ログイン処理はNCMBUserに対してユーザー名とパスワードを設定し、login
メソッドを実行します。
認証が完了したら、callback
関数を呼び出しています。これはナビゲーションで設定した changeLoginStatus
です。これで認証後、一覧画面の表示に切り替わります。
val login = {
try {
progress = true
val user = NCMBUser()
user.userName = userName
user.password = password
user.login()
callback()
} catch(e: NCMBException){
showDialog = true
}
progress = false
}
データの新規保存
データを入力するのは FormScreen
になります。FormScreenは新規作成と編集を兼ねているので、受け取った obj
の状態によってメッセージやデフォルト値を分けています。
@Composable
fun FormScreen(navController: NavController, obj: NCMBObject) {
// 新規作成と編集でデフォルト値を変更
val objectId = obj.getObjectId()
var title by remember { mutableStateOf(if (objectId == null) "" else obj.getString("title")!!) }
var body by remember { mutableStateOf(if (objectId == null) "" else obj.getString("body")!!) }
// 画面のヘッダー用
val header = if (objectId == null) "新規作成" else "編集"
// 処理中のフラグ
var progress by remember { mutableStateOf(false) }
// ダイアログ表示のフラグ
var showDialog by remember { mutableStateOf(false) }
// ダイアログメッセージ
var message by remember { mutableStateOf("") }
// ダイアログ表示
if (showDialog) {
AlertDialog(
onDismissRequest = {},
buttons = {
Button(onClick = {
showDialog = false
}) {
Text("OK")
}
},
title = {Text("日報保存")},
text = {Text(message)}
)
}
val save = {
// 保存処理(後述)
}
// 以下画面構築
Scaffold(
topBar = {
TopAppBar(
title = {
Text("日報の${header}")
},
actions = {
IconButton(
onClick = {
}
){
Icon(Icons.Filled.Save, contentDescription = "Save")
}
}
)
},
content = {
Column {
OutlinedTextField(
value = title,
onValueChange = { title = it },
modifier = Modifier.padding(20.dp),
label = { Text("日報のタイトル") },
singleLine = true
)
OutlinedTextField(
value = body,
onValueChange = { body = it },
modifier = Modifier.padding(20.dp).height(150.dp),
label = { Text("日報の本文") },
)
Button(onClick = save,
enabled = !progress,
modifier = Modifier.padding(20.dp)
) {
if (progress) {
CircularProgressIndicator(
strokeWidth = 2.dp,
modifier = Modifier.size(24.dp)
)
} else {
Text("保存する")
}
}
}
}
)
}
データの保存処理について
データの保存は、まず入力された内容を put
メソッドでNCMBObjectに関連付けします。そして、NCMBAclを使ってアクセス権限を設定します。ログインしたユーザー情報は NCMBUser().getCurrentUser()
で取得できます。そのユーザーに対してのみ、書き込み権限を付与しています(読み込みは誰でも可能としています)。そして save
メソッドで保存完了です。
val save = {
// 保存処理(後述)
progress = true
try {
obj.put("title", title)
obj.put("body", body)
// アクセス権限の設定
val acl = NCMBAcl()
val currentUser = NCMBUser().getCurrentUser()
acl.publicReadAccess = true
acl.setUserWriteAccess(currentUser.getObjectId()!!, true)
obj.setAcl(acl)
// 保存
obj.save()
title = ""
body = ""
message = "日報を保存しました"
} catch(e: NCMBException){
message = "日報が保存できませんでした"
}
showDialog = true
progress = false
}
まとめ
NCMBを使えば認証やデータの保存が簡単に実現できます。ぜひあなたのアプリ開発に利用してください。
今回はログイン処理とデータの保存(同じコードで編集も行えます)を行いました。次回はデータの検索と表示について解説します。