LoginSignup
0
0

More than 1 year has passed since last update.

NCMB Kotlin SDKを使って日報アプリを作る(その2 ログインとデータの保存)

Posted at

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("ログイン")
            }
        }
    }
}

Screenshot_20220414_172403.png

ログイン処理について

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("保存する")
                    }
                }
            }
        }
    )
}

Screenshot_20220414_172211.png

データの保存処理について

データの保存は、まず入力された内容を 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を使えば認証やデータの保存が簡単に実現できます。ぜひあなたのアプリ開発に利用してください。

今回はログイン処理とデータの保存(同じコードで編集も行えます)を行いました。次回はデータの検索と表示について解説します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0