FireabaseのRealtimeDatabaseにおけるトランザクション処理のサンプルコード がひと目意味がわからなかったので、簡単に書いて理解しました。
ざっと理解
let ref = Database.database().reference().child("test/")
ref.runTransactionBlock({ (currentData) -> TransactionResult in
// ここは何度も呼ばれる
// サーバ側とデータの同期を取っていて、変更がある度に呼ばれる
// currentDataに変更を加える
// 変更後のデータを返す。これを元に変更前のデータと変更後のデータを見て、サーバ側で変更の成否を判断するみたい。
return TransactionResult.success(withValue: currentData)
}) { (error, success, snapshot) in
// 処理が完了した時に呼ばれる
// 完了とはサーバ側に反映されるか、何かしらで諦めるか
}
適当なサンプル書いて、動作させてみる
動作は下記の通りです。 1. 単にkey:x value:2というだけのデータを対象にトランザクション開始 2. x = 2に設定 3. sleep10秒 4. この間にGUI上からvalueを200に変える 5. transactionが失敗するので、再度実行される 6. transactionが成功する
ref.runTransactionBlock({ (currentData) -> TransactionResult in
print("======= transaction A start========")
print("今のデータ:\(currentData)")
var changedData = currentData
if var data = currentData.value as? [String:Int]{
print("before sleep")
data["x"] = 2
sleep(10)
print("after sleep")
changedData.value = data
}
return TransactionResult.success(withValue: changedData)
}) { (error, success, changedData) in
print("======== transaction A end ========")
print(success)
print(changedData)
print(error?.localizedDescription)
}
}
実行結果
追記コメントは★で記しています
======= transaction A start========
★ 最初はcurrentdata = nullで呼ばれる
今のデータ:FIRMutableData (top-most transaction) (null) <null>
2018-02-03 21:55:13.836666+0900 connectfb[51259:2243926] TIC Read Status [1:0x0]: 1:57
2018-02-03 21:55:13.836772+0900 connectfb[51259:2243926] TIC Read Status [1:0x0]: 1:57
======= transaction A start========
★ 今のサーバ上のデータ:x = 2で呼ばれる
今のデータ:FIRMutableData (top-most transaction) (null) {
x = 2;
}
before sleep
★ この間にGUIからデータをx=20に変更
after sleep
======= transaction A start========
★ 今のサーバ上のデータ:x = 20で呼ばれる
今のデータ:FIRMutableData (top-most transaction) (null) {
x = 20;
}
before sleep
after sleep
======== transaction A end ========
★ 変更が完了したのでクロージャが呼ばれる
true
Optional(Snap (test) {
x = 2;
})
nil
思ったこと
自分の自力不足か、サンプルを読んだだけだと動きが理解できなかったものが理解できた気がします。
今困っているのは、このトランザクションの中でdeleteの成否が検知できないところです。
例えばxというキーがいない状態を正とするトランザクションはできるのですが、xが元々いなかった場合と、自分がdeleteに成功した場合を分けたいと思っていて、実装に詰まっています。
これは解消したら別記事にしたいと思います。