NCMBではSwift向けにSDKをリリースしています。最近ではiOSアプリを作る際にSwiftを利用することが増えていますので、その場合にはネイティブ言語で実装されているSwift SDKの利用をお勧めします。
そんなSwift SDKの使い方を知ってもらうためにも、ちょっとしたアプリ(Todoアプリ)を作ってみました。徐々に機能を追加していきますので、Swift SDKの利用法をぜひ覚えてください。
Swift SDKの導入法
Swift SDKはCocoaPods向けに提供しています。Xcodeで新しいiOSプロジェクトを作ったら、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
そして pod install
でSwift SDKをインストールします。
初期化について
現在、新規でiOSアプリを作成すると、InterfaceがSwiftUI、Life CycleがSwiftUI Appとなっています。この状態で作ると AppDelegate.swift
はなく、 (アプリ名)App.swift
というファイルが作られます。
この場合、まずSwift SDKを読み込みます。
import SwiftUI import NCMB // 追加
そして var body
の上に @Environment
を追加します。
@Environment(\.scenePhase) private var scenePhase // 追加 var body: some Scene {
最後に WindowGroup
に onChange
を追加します。このonChangeの中で初期化処理を行います。
WindowGroup { ContentView() } .onChange(of: scenePhase) { scene in switch scene { case .active: NCMB.initialize(applicationKey: "YOUR_APPLICATION_KEY", clientKey: "YOUR_CLIENT_KEY") } }
これで利用可能になります。
UIについて
UIは全部で2つです。1つはタスクが一覧される画面です。これはNCMBからデータを取得して、一覧表示します。データ形式が単純ではないので ObservableObject
を利用しています。
import SwiftUI import NCMB import Combine class Todos: ObservableObject { @Published var todos: [NCMBObject] = [] }
メイン画面の表示部分です。onAppear
でNCMBのTodoクラスへ問い合わせて、その結果を画面に反映しています。
struct ContentView: View, InputViewDelegate { @ObservedObject var Todo = Todos() var body: some View { NavigationView { ZStack(alignment: .bottomTrailing) { List { ForEach(self.Todo.todos, id: \.objectId) { todo in Text((todo["body"] ?? "") as String) } .onDelete(perform: delete) } NavigationLink(destination: InputView(delegate: self, text: "")) { Text("Add") .foregroundColor(Color.white) .font(Font.system(size: 20)) } .frame(width: 60, height: 60) .background(Color.blue) .cornerRadius(30) .padding() } .onAppear { let query : NCMBQuery<NCMBObject> = NCMBQuery.getQuery(className: "Todo") query.findInBackground(callback: { result in switch result { case let .success(array): DispatchQueue.main.async { self.Todo.todos = array } case let .failure(error): print("取得に失敗しました: \(error)") } }) } .navigationTitle("TODO") .navigationBarItems(trailing: EditButton()) } } }
追加されるメソッドとして、タスクの追加と削除があります。追加は次のようになります。NCMBObjectはsaveメソッドで行います。
func addTodo(text: String) { let obj = NCMBObject(className: "Todo") obj["body"] = text let result = obj.save() switch result { case .success(_): self.Todo.todos.append(obj) case let .failure(error): print("取得に失敗しました: \(error)") } }
同じく削除はNCMBObjectのdeleteメソッドを使います。
func delete(at offsets: IndexSet) { let todo = self.Todo.todos[Array(offsets)[0]] as NCMBObject let result = todo.delete() switch result { case .success(_): self.Todo.todos.remove(atOffsets: offsets) case let .failure(error): print(error) } }
タスクの追加画面
続いてタスクの追加ですが、次のようにInputViewで行います。呼ぶのは上記のaddTodoになります。
protocol InputViewDelegate { func addTodo(text: String) } struct InputView: View { @Environment(\.presentationMode) var presentation let delegate: InputViewDelegate @State var text: String var body: some View { VStack(spacing: 16) { TextField("タスクを追加してください", text: $text) .textFieldStyle(RoundedBorderTextFieldStyle()) Button("追加") { delegate.addTodo(text: text) presentation.wrappedValue.dismiss() } } .padding() } }
全体のコードは次のようになります。
// // ContentView.swift // todoapp // // Created by Atsushi on 2021/04/05. // import SwiftUI import NCMB import Combine class Todos: ObservableObject { @Published var todos: [NCMBObject] = [] } struct ContentView: View, InputViewDelegate, EditViewDelegate { func editTodo(text: String) { print(text) } @ObservedObject var Todo = Todos() var body: some View { NavigationView { ZStack(alignment: .bottomTrailing) { List { ForEach(self.Todo.todos, id: \.objectId) { todo in NavigationLink(destination: EditView(delegate: self, text: (todo["body"] ?? "") as String)) { Text((todo["body"] ?? "") as String) } } .onDelete(perform: delete) } NavigationLink(destination: InputView(delegate: self, text: "")) { Text("Add") .foregroundColor(Color.white) .font(Font.system(size: 20)) } .frame(width: 60, height: 60) .background(Color.blue) .cornerRadius(30) .padding() } .onAppear { let query : NCMBQuery<NCMBObject> = NCMBQuery.getQuery(className: "Todo") let result = query.find() switch result { case let .success(array): self.Todo.todos = array case let .failure(error): print("取得に失敗しました: \(error)") } } .navigationTitle("TODO") .navigationBarItems(trailing: EditButton()) } } func delete(at offsets: IndexSet) { let todo = self.Todo.todos[Array(offsets)[0]] as NCMBObject let result = todo.delete() switch result { case .success(_): self.Todo.todos.remove(atOffsets: offsets) case let .failure(error): print(error) } } func addTodo(text: String) { let obj = NCMBObject(className: "Todo") obj["body"] = text var acl = NCMBACL.empty let user = NCMBUser.currentUser if user != nil { acl.put(key: user?.objectId! ?? "", readable: true, writable: true) acl.put(key: "role:Admin", readable: true, writable: false) obj.acl = acl } let result = obj.save() switch result { case .success(_): self.Todo.todos.append(obj) case let .failure(error): print("取得に失敗しました: \(error)") } } } protocol EditViewDelegate { func editTodo(text: String) } struct EditView: View { @Environment(\.presentationMode) var presentation let delegate: EditViewDelegate @State var text: String var body: some View { VStack(spacing: 16) { TextField("タスクを編集してください", text: $text) .textFieldStyle(RoundedBorderTextFieldStyle()) Button("追加") { delegate.editTodo(text: text) presentation.wrappedValue.dismiss() } } .padding() } } protocol InputViewDelegate { func addTodo(text: String) } struct InputView: View { @Environment(\.presentationMode) var presentation let delegate: InputViewDelegate @State var text: String var body: some View { VStack(spacing: 16) { TextField("タスクを追加してください", text: $text) .textFieldStyle(RoundedBorderTextFieldStyle()) Button("追加") { delegate.addTodo(text: text) presentation.wrappedValue.dismiss() } } .padding() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
まとめ
TodoアプリはデータストアのCRUD操作を基本としていますので、Swift SDKの使い方を覚えるのに最適かと思います。次はこのアプリに匿名認証機能を追加する方法を紹介します。