我正在開發一個專門用於清潔服務的應用程序。在這個應用程序中,員工(清潔工)可以閱讀由多個客戶(用戶)完成的工作清單(預訂)。Firebase:如何以事務方式更新多個節點? Swift 3
所有清潔工都可以讀取用戶節點中的所有預訂。最初,當用戶將預訂保存在數據庫中時,密鑰claimed:
的值爲「false」
,這意味着它沒有被清潔程序聲明。
每當清潔想要求一個工作,他可以在列表中看到,他將不得不輕觸按鈕,這將使到火力地堡數據庫的請求,在路徑的關鍵claimed
值修改到true
/Users/UID/bookings/bookingNumber
一次只允許一個清潔人員修改claimed
鍵的值。如果允許多個清潔工修改claimed
鍵的價值,其他清潔工最終會要求相同的工作。我們不希望這種情況發生。
此外,清潔修改claimed
鍵的值true
後,我們將需要另一個請求路徑CLeaners/UID/bookings/bookingNumber
爲了救他剛權利清潔工節點預訂。
- 根據firebase文檔,如果有多個併發請求試圖寫入同一資源,我們就會使用事務,只要我們希望一次只修改一個請求,則其中一個請求會成功。 但是使用事務的問題是,它使得只能向寫入一條路徑,它不會將寫入多條路徑。
如何確保即使多個用戶可以讀取此路徑/Users/UID/bookings/bookingNumber
,一次只有一個用戶可以更新它?如果寫入成功,則進一步寫入第二條路徑Cleaners/UID/bookings/bookingNumber
。
我們需要考慮到客戶端的互聯網連接可能會丟失,用戶可以退出應用程序,或者只需在寫入上述指定路徑之間的任何時間,電話就會意外關閉。
數據庫結構如下
Root
Cleaners
UID
bookings
bookingNumber
amount: 「10」
claimed: 「true」
Users
UID
otherID
bookingNumber
amount: 「10」
claimed: 「true」
bookingNumber
amount: 「50」
claimed: 「false」
爲了避免任何重寫,我決定用火力地堡交易。我可以寫入單個節點作爲事務,但是寫入完成處理程序中的第二個節點並不是一個解決方案,因爲在從服務器接收到響應之前,清理程序的Internet連接可能會丟失或者應用程序可能會退出,因此,完成處理程序{(error, committed,snapshot) in....
將不會被評估,第二次寫入不會成功。
另一種情形是:第一寫被執行時,
1.響應於客戶端應用程序接收到的
2.響應不是在客戶端應用程序
尚未收到,並且用戶立即退出該應用。在這種情況下,第二次寫入將永遠不會被執行,因爲在完成處理程序中收到響應(或沒有響應)後尚未對代碼進行評估,從而使數據庫處於不一致狀態。
從火力地堡文檔:
交易不能跨應用程序重新啓動持續
即使啓用持久性,事務不能跨 應用程序重新啓動依然存在。因此,您無法依靠脫機完成的事務被提交到您的Firebase實時數據庫 。
是否有可能寫入多個節點使用火力地堡交易斯威夫特一個火力地堡數據庫?
如果是這樣,我該怎麼做?我從谷歌https://firebase.googleblog.com/2015/09/introducing-multi-location-updates-and_86.html看到這個博客沒有例子。我明白,你可以原子寫入多個節點,但我想寫作交易。 我試圖寫else
子句中的兩個節點,但我從得到一個警告,在這條線let updated = updateInUsersAndCleaners as? FIRMutableData
演員「[FIRDatabaseReference:FIRMutableData]」無關型 「FIRMutableData」總是失敗
class ClaimDetail: UIViewController,UITableViewDelegate,UITableViewDataSource {
var valueRetrieved = [String:AnyObject]()
var uid:String?
@IBAction func claimJob(_ sender: Any) {
dbRef.runTransactionBlock({ (_ currentData:FIRMutableData) -> FIRTransactionResult in
//if valueRetrieved is nil abort
guard let val = currentData.value as? [String : AnyObject] else {
return FIRTransactionResult.abort()
}
self.valueRetrieved = val
guard let uid = FIRAuth.auth()?.currentUser?.uid else {
return FIRTransactionResult.abort()
}
self.uid = uid
for key in self.valueRetrieved.keys {
print("key is \(key)")
//unwrap value of 'Claimed' key
guard let keyValue = self.valueRetrieved["Claimed"] as? String else {
return FIRTransactionResult.abort()
}
//check if key value is true
if keyValue == "true"{
//booking already assigned, abort
return FIRTransactionResult.abort()
} else {
//write the new values to firebase
let newData = self.createDictionary()
currentData.value = newData
let usersRef = self.dbRef.child("Users").child(FullData.finalFirebaseUserID).child(FullData.finalStripeCustomerID).child(FullData.finalBookingNumber)
let cleanersRef = self.dbRef.child("Cleaners").child(self.uid!).child("bookings").child(FullData.finalBookingNumber)
//Create data we want to update for both nodes
let updateInUsersAndCleaners = [usersRef:currentData,cleanersRef:currentData]
let updated = updateInUsersAndCleaners as? FIRMutableData
return FIRTransactionResult.success(withValue: updated!)
}//end of else
}//end of for key in self
return FIRTransactionResult.abort()
}) {(error, committed,snapshot) in
if let error = error {
//display an alert with the error, ask user to try again
self.alertText = "Booking could not be claimed, please try again."
self.alertActionTitle = "OK"
self.segueIdentifier = "unwindfromClaimDetailToClaim"
self.showAlert()
} else if committed == true {
self.alertText = "Booking claimed.Please check your calendar"
self.alertActionTitle = "OK"
self.segueIdentifier = "unwindfromClaimDetailToClaim"
self.showAlert()
}
}
}//end of claimJob button
}//end of class
extension ClaimDetail {
//show alert to user and segue to Claim tableView
func showAlert() {
let alertMessage = UIAlertController(title: "", message: self.alertText, preferredStyle: .alert)
alertMessage.addAction(UIAlertAction(title: self.alertActionTitle, style: .default, handler: { (action:UIAlertAction) in
self.performSegue(withIdentifier: self.segueIdentifier, sender: self)
}))
self.present(alertMessage, animated: true,completion: nil)
}
//create dictionary with data received from completion handler and the new data
func createDictionary() -> AnyObject {
let timeStamp = Int(Date().timeIntervalSince1970)
self.valueRetrieved["CleanerUID"] = uid as AnyObject?
self.valueRetrieved["TimeStampBookingClaimed"] = timeStamp as AnyObject?
self.valueRetrieved["Claimed"] = "true" as AnyObject?
print("line 89 extension CLaim Detail")
return self.valueRetrieved as AnyObject
}
} // end of extension ClaimDetail
該博客文章顯示了Objective C中的一個示例。您是否難以將其轉換爲Swift?如果是這樣:更新您的問題,以顯示你已經嘗試過。 –
@FrankvanPuffelen我沒有看到使用此函數'runTransactionBlock'的任何示例,如https://firebase.google.com/docs/database/ios/read-and-write#save_data_as_transactions中所述。我應該只是創建兩個引用到firebase數據庫中的不同節點,然後將數據寫入事務?我在博客中找不到任何示例,顯示使用Firebase交易將數據寫入兩條不同的路徑。 – bibscy
@FrankvanPuffelen請參閱我更新的問題 – bibscy