ニフクラ mobile backendは3月末で終了します。
ニフクラ mobile backendからの移行先として、お勧めしているのがParse Serverです。設計思想が近く、変更するコード量が少なく済むのではないかと思います。
Parse ServerではiOS向けにSDKを提供していますが、Swift SDKについてはあまりドキュメントが充実していません。そこで、各機能の使い方を解説します。今回はデータストアオブジェクトの使い方で、特に複数データをまとめて処理する方法を解説します。
Swift SDKとObjective-C SDKのどちらを使うべきか
Parse ServerにはSwift SDKとObjective-C SDKが用意されています。機能的にいうと、Objective-C SDKの方が多いようです。しかし、公式メッセージとしてはSwift SDKを使っていくのを奨励しています。
Parse Server Swift SDKのインストール
Swift SDKのインストールは、CocoaPodsやCarthageなどが使えます。しかし、一番簡単なのはXcodeのPackage Dependenciesを使う方法でしょう。
XcodeのFileメニューより、Add Package Dependenciesを選択して、出てきたダイアログで以下のURLを指定します。
https://github.com/parse-community/Parse-Swift.git
そして、利用するファイルでSDKをインポートします。
import ParseSwift
初期化
SwiftUIの例です。初期化は (アプリ名)App.swift
にて行います。そして、初期化は ParseSwift.initialize
にて行います。指定するアプリケーションID、クライアントキー、サーバーURLはそれぞれParse Serverを立ち上げる際に指定したものを使います。
マスターキーも指定できるようですが、アプリ側では使わない方が良いかと思います。
import SwiftUI
import ParseSwift
@main
struct ParseDemoApp: App {
init() {
ParseSwift.initialize(applicationId: "YOUR_APP_ID", clientKey: "YOUR_CLIENT_KEY", serverURL: URL(string: "https://example.com/parse")!)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
データストアオブジェクトの定義
データストアで利用するオブジェクトは ParseObject
を継承して定義します。
struct GameScore: ParseObject {
// 必須のプロパティ
var objectId: String?
var createdAt: Date?
var updatedAt: Date?
var ACL: ParseACL?
var originalData: Data?
// 独自のプロパティ
var points: Int?
/*:
独自メソッド
*/
func merge(with object: Self) throws -> Self {
var updated = try mergeParse(with: object)
if updated.shouldRestoreKey(\.points,
original: object) {
updated.points = object.points
}
return updated
}
}
複数オブジェクトを保存する
データストアのオブジェクトを配列にし、 saveAll
メソッドを使えば一回で保存処理を実行できます。
[score, score2].saveAll { results in
switch results {
case .success(let otherResults):
var index = 0
otherResults.forEach { otherResult in
switch otherResult {
case .success(let savedScore):
print("""
Saved \"\(savedScore.className)\" with
points \(String(describing: savedScore.points)) successfully
""")
if index == 1 {
score2ForFetchedLater = savedScore
}
index += 1
case .failure(let error):
assertionFailure("Error saving: \(error)")
}
}
case .failure(let error):
assertionFailure("Error saving: \(error)")
}
}
一括保存時のトランザクション
これはMongoDBの設定によっては機能しないとのことですが、トランザクションも利用できます。
[score, score2].saveAll(transaction: true) { results in
switch results {
case .success(let otherResults):
var index = 0
otherResults.forEach { otherResult in
switch otherResult {
case .success(let savedScore):
print("Saved \"\(savedScore.className)\" with points \(savedScore.points) successfully")
if index == 1 {
score2ForFetchedLater = savedScore
}
index += 1
case .failure(let error):
assertionFailure("Error saving: \(error)")
}
}
case .failure(let error):
assertionFailure("Error saving: \(error)")
}
}
複数データの取得
objectIdを指定してフェッチできますが、これも複数データに対して一括処理を行えます。
let score = GameScore(objectId: "ABCDE")
let score2 = GameScore(objectId: "01234")
[scoreToFetch, score2ToFetch].fetchAll { result in
switch result {
case .success(let fetchedScores):
fetchedScores.forEach { result in
switch result {
case .success(let fetched):
print("取得成功しました: \(fetched)")
case .failure(let error):
print("取得失敗しました: \(error)")
}
}
case .failure(let error):
assertionFailure("エラーが発生しました: \(error)")
}
}
fetchAll
も同期処理が可能です。
do {
let fetchedScores = try [scoreToFetch, score2ToFetch].fetchAll()
fetchedScores.forEach { result in
switch result {
case .success(let fetched):
fetchedScore = fetched
print("Successfully fetched: \(fetched)")
case .failure(let error):
print("Error fetching: \(error)")
}
}
} catch {
assertionFailure("Error fetching: \(error)")
}
更新の一括処理
更新は save
メソッドを使います。同期、非同期ともに、保存時と同じように処理します。
削除の一括処理
削除は deleteAll
を使います。
[scoreToFetch, score2ToFetch].deleteAll { result in
switch result {
case .success(let deletedScores):
deletedScores.forEach { result in
switch result {
case .success:
print("Successfully deleted score")
case .failure(let error):
print("Error deleting: \(error)")
}
}
case .failure(let error):
assertionFailure("Error deleting: \(error)")
}
}
同期処理は try
を使う点も同じです。
do {
let fetchedScores = try [scoreToFetch, score2ToFetch].deleteAll()
fetchedScores.forEach { result in
switch result {
case .success(let fetched):
print("Successfully deleted: \(fetched)")
case .failure(let error):
print("Error deleted: \(error)")
}
}
} catch {
assertionFailure("Error deleting: \(error)")
}
まとめ
NCMBにはデータの一括処理がなかったので、これはParse Server特有の機能です。複数データをまとめて登録する際などに便利そうです。
Parse ServerとNCMBのデータストア操作は、SDKの仕組みで異なる部分が多いです。載せ替える際には、コードの修正が必要になるでしょう。
とはいえ、データの管理方法などはParse ServerとNCMBで似ています。他のmBaaSと比べると、修正量はそこまで多くないと思われます。載せ替え先として検討に挙げてください。