必要なもの
- Xcode
- Ruby
- CocoaPods
- NCMBのアカウント
ベースアプリについて
ベースアプリはNCMBMania/Swift_Todo_Handsonになります。まずGitリポジトリをクローンしてください。
git clone git@github.com:NCMBMania/Swift_Todo_Handson.git
Swift SDKの導入について
該当リポジトリにはすでにPodfileを用意しています。
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'todoapp' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for todoapp
pod 'NCMB', :git => 'https://github.com/NIFCLOUD-mbaas/ncmb_swift.git'
end
CocoaPodsのインストールはターミナルで次のように実行します。
$ gem install cocoapods
NCMB SDKをCocoaPodsでインストールします。
$ pod install
todoappApp.swiftの編集
todoappApp.swiftはアプリが立ち上がった際の初期設定を行います。まずNCMB SDKを読み込みます。
import NCMB
SDKの初期化
// NCMBの初期化処理をここに書きます
以下に記述していきます。
SDKを初期化します。アプリケーションキー、クライアントキーはあらかじめ取得してください。
// アプリがアクティブになったタイミングでNCMBを初期化
NCMB.initialize(applicationKey: "YOUR_APPLICATION_KEY", clientKey: "YOUR_CLIENT_KEY")
匿名認証を有効にする
NCMBの管理画面にて、匿名認証を有効にしてください。
さらにSwift SDKで匿名認証を有効にします。
// 匿名認証を有効にする
NCMBUser.enableAutomaticUser()
ログインユーザを取得する
現在ログインしているユーザの情報を取得します。
// 現在ログインしているかチェック
let user = NCMBUser.currentUser
認証状態のチェック
user が nil かどうかで判定します
if user == nil {
// ログインしていない場合
} else {
// ログインしている場合
}
ログインしていない場合
ログインしていない場合は匿名認証を行います。
// 匿名認証を実施
NCMBUser.automaticCurrentUserInBackground(callback: { result in
// 認証結果の判定
switch result {
case .success:
// ログインに成功した場合の処理
print("匿名ユーザーでのログインに成功しました")
case let .failure(error):
// ログインに失敗した場合の処理
print("匿名ユーザーでのログインに失敗しました: \(error)")
}
})
認証している場合
認証している場合はセッションの有効性チェックを行います。エラーだった場合はログアウトします。
// セッションの有効性チェック
// 適当なデータストア(今回はTodoクラス)にアクセス
var query : NCMBQuery<NCMBObject> = NCMBQuery.getQuery(className: "Todo")
query.limit = 1 // レスポンス件数を最小限にする
// アクセス
query.findInBackground(callback: { results in
// 結果の判定
switch results {
case let .success(obj):
// 返ってくれば問題なし
print(obj)
case let .failure(error):
// エラーの場合は強制ログアウト
NCMBUser.logOutInBackground(callback: { result in
print("強制ログアウト")
})
}
})
ContentView.swiftの修正
ContentViewにはアプリの実装を行います。
ContentViewの実装内容
以下の画面があります。
- 一覧画面
- タスク追加画面
- タスク編集画面
一覧画面
一覧画面ではタスクを取得して一覧表示、さらに一覧からタスクの削除を行います。
// 記述済み
struct ContentView /* 省略 */
タスク追加画面
タスク追加画面では新しいタスクの名前を入力します。
// 記述済み
struct InputView: View {
}
タスク編集画面
タスク編集画面では一覧画面で選択されたタスクを表示、編集します。
// 記述済み
struct EditView: View {
}
SDKの読み込み
ContentView.swiftでもNCMB SDKを読み込みます。
import NCMB
ObservableObjectの用意
タスク一覧を管理するObservableObjectを用意します。
// NCMBObjectを直接設定できないので ObservableObject を経由させます
class Todos: ObservableObject {
@Published var todos: [NCMBObject] = []
}
ObservableObjectをContentViewの中で読み込みます。
struct ContentView: View, InputViewDelegate, EditViewDelegate {
// ObservedObjectとして定義します
@ObservedObject var Todo = Todos()
追加画面への遷移
メイン画面で Add ボタンをタップすると追加画面に遷移します。
// 記述済み
NavigationLink(destination: InputView(delegate: self, text: "")) {
Text("Add")
.foregroundColor(Color.white)
.font(Font.system(size: 20))
}
NCMBの追加処理
タスク追加画面で追加ボタンをタップすると、Delegateを通じて ContentViewの addTodo
が呼び出されます。
// タスクの追加処理です
func addTodo(text: String) {
// 新しいタスクオブジェクトを作成します
let obj = NCMBObject(className: "Todo")
// 入力された文字列を適用します
obj["body"] = text
// ACL(アクセス権限)を作成します
var acl = NCMBACL.empty
// 現在のログインユーザを取得します
let user = NCMBUser.currentUser!
// ログインユーザに対して読み込み、書き込み権限を付与します
acl.put(key: user.objectId!, readable: true, writable: true)
// ついでにAdminグループに対して読み込み、書き込み権限を付与する例です
acl.put(key: "role:Admin", readable: true, writable: false)
// ACLを適用します
obj.acl = acl
// 保存します
obj.saveInBackground(callback: { result in
// 保存結果の判定
switch result {
// 保存成功
case .success(_):
// 画面に反映させるのでメインスレッドに戻します
DispatchQueue.main.async {
// Todoに新しいタスクを追加
self.Todo.todos.append(obj)
self.Todo.objectWillChange.send()
}
// 保存失敗
case let .failure(error):
print("作成に失敗しました: \(error)")
}
})
}
一覧画面の作成
ContentViewのZStack.onAppearの中で、画面表示時にTodoクラスからデータを取得し、Todoに対してデータをセットします。
以下の中に記述します。
// 記述済み
.onAppear {
// 画面表示された際のイベントです
}
記述内容です。
// Todoクラス用のクエリを用意します
let query : NCMBQuery<NCMBObject> = NCMBQuery.getQuery(className: "Todo")
// 検索処理を実行します
// 処理は非同期です
query.findInBackground(callback: { result in
// 結果の判定です
switch result {
// 取得できた場合
case let .success(array):
// メインスレッドなので main.async を使います
DispatchQueue.main.async {
// 結果を適用します
self.Todo.todos = array
}
// 取得失敗した場合
case let .failure(error):
print("取得に失敗しました: \(error)")
}
})
一覧表示
一覧表示は以下のコード内に記述します。
// 記述済み
List {
// Todo変数のデータを一覧表示します
}
記述内容です。タップした際に編集画面に遷移する処理も加えています。
// Todo変数のデータを一覧表示します
ForEach(self.Todo.todos, id: \.objectId) { todo in
// todoはNCMBObjectです
// タスク文字列をタップしたらEditViewを呼び出します
NavigationLink(destination: EditView(delegate: self, objectId: todo.objectId!, text: (todo["body"] ?? "") as String)) {
Text((todo["body"] ?? "") as String)
}
}
// 削除イベントでdeleteを呼び出します
.onDelete(perform: delete)
タスクの削除
一覧画面での onDelete イベントで ContentView.delete
を呼び出しています。
記述は次のようになります。
// タスクの削除処理です
func delete(at offsets: IndexSet) {
// 処理対象のタスクを取得します
let todo = self.Todo.todos[Array(offsets)[0]] as NCMBObject
// 削除を実行します(同期処理にしていますが非同期でも可)
let result = todo.delete()
// 処理結果の判定です
switch result {
// 成功した場合
case .success(_):
// Todoから該当のタスクを削除します
self.Todo.todos.remove(atOffsets: offsets)
// 失敗した場合
case let .failure(error):
print(error)
}
}
タスク編集処理
タスク編集画面で編集ボタンをタップすると、Delegateを通じて ContentView.editTodo
が呼び出されます。
内容は次のようになります。
// タスクの編集処理です
func editTodo(text: String, objectId: String) {
// 処理対象のタスクを特定します
if let i = self.Todo.todos.firstIndex(where: { $0.objectId == objectId}) {
let obj = self.Todo.todos[i]
// 新しい本文を適用
obj["body"] = text
// 更新します
obj.saveInBackground(callback: { result in
// 更新結果の判定
switch result {
// 更新成功
case .success(_):
// 画面に反映させるのでメインスレッドに戻します
DispatchQueue.main.async {
// Todoを書き換えます
self.Todo.todos[i] = obj
self.Todo.objectWillChange.send()
}
// 更新失敗
case let .failure(error):
print("更新に失敗しました: \(error)")
}
})
}
}
まとめ
今回のハンズオンでは次の3つの機能を利用しました。
- 会員管理
- 匿名認証
- データストア登録/更新/削除
- データストア検索