NCMBの操作はREST APIを介して行います。ネットワーク処理ということは、非同期処理であるということです。非同期処理はプログラミングコードが上から下に流れない、コールバック方式になったりするので、意図した動作にならないケースがあるなど、苦労することがあります。
そこでNCMBのSwift SDKでは同期処理のように扱えるメソッドも用意しています。この記事では同期、非同期処理の使い分けを紹介します。
データを一括で検索する場合
例えばデータストア(クラウドデータベース)からデータを取り出す場合です。非同期処理の場合は次のように書きます。
query.findInBackground(callback: { result in
switch result {
case let .success(array): // 検索成功
// メモデータを適用
self.memos = array
case let .failure(error): // 検索失敗
print("取得に失敗しました: \(error)")
}
})
これを同期処理で書く場合です。ぱっと見では同期処理の方が分かりやすいです。
let result = query.find()
if case let .success(array) = result {
self.memos = array;
}
実際の描画される様子です。まず非同期の場合。
次に同期の場合。
おそらく、どちらも見た目上は変わりません。
ループ処理の伴う同期、非同期処理
次にデータを取得した後、ファイルストア(クラウドファイルストレージ)から写真データをダウンロードしてグリッドに描画する処理です。この場合、検索結果が6件あれば、6回写真のダウンロードが実行されます。
非同期の場合です。これが6回繰り返されるようなものです。
// ファイルストア用のオブジェクトを用意
let file : NCMBFile = NCMBFile(fileName: fileName)
// ダウンロード実行
file.fetchInBackground(callback: { result in
switch result {
case let .success(data): // ダウンロード成功
self.imageData = data
case let .failure(error): // ダウンロード失敗
print(error)
}
})
次に同期の場合です。
// ファイルストア用のオブジェクトを用意
let file : NCMBFile = NCMBFile(fileName: fileName)
// ダウンロード実行
let result = file.fetch()
if case let .success(data) = result {
self.imageData = data
}
同期で処理した場合です。表示までにかなり時間がかかり、一気に表示されます。これはUXがよくありません。
同期処理の仕組み
同期処理は DispatchSemaphore を使って処理をwaitし、レスポンスが返ってきたら処理を再開しています。基本的に非同期処理の 〜InBackground をラッピングしているだけです。
public func find() -> NCMBResult<[T]> {
var result : NCMBResult<[T]> = NCMBResult.failure(NCMBApiErrorCode.genericError)
let semaphore = DispatchSemaphore(value: 0)
findInBackground(callback: {(res: NCMBResult<[T]>) -> Void in
result = res
semaphore.signal()
})
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
return result
}
このため、ループで同期処理を行うと、1つ目の写真から6つ目の写真まで順番に1つずつ取得する形になります。これだと時間がかかってしまいます。非同期処理の場合はパラレルでダウンロード処理されるので、写真の表示順番は保証されませんが、表示までは高速になります。
まとめ
同期処理を使うことでコードの見通しは良くなります。JavaScriptのasync/awaitのように使えるので、ぜひ活用してください。ただ、ループ処理中での利用は控えた方が、アプリのUXとして良いものになるはずです。