bib*_*scy 2 firebase swift firebase-realtime-database
我正在开发一个用于清洁服务的应用程序。在此应用程序中,员工(清洁工)可以读取由多个客户(用户)完成的作业(预订)列表。
所有清洁工都可以在“用户”节点中读取所有预订。最初,当用户将预订保存在数据库中时,该键claimed:的值为 “false”,这意味着清洁工尚未声明该键。
每当清洁工想要索取列表中存在的工作时,他将必须触摸按钮,该按钮将向Firebase数据库发出请求以将key的值修改claimed为trueat路径。 /Users/UID/bookings/bookingNumber
一次只能允许一名清洁工修改claimed钥匙的价值。如果允许多个清理者修改claimed密钥的值,其他清理者最终将要求相同的工作。我们不希望这种情况发生。
此外,在清理者将claimedkey 的值修改为之后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”
Run Code Online (Sandbox Code Playgroud)
为了避免任何覆盖,我决定使用Firebase事务。我可以将单个节点作为事务写入,但是在完成处理程序中写入第二个节点不是解决方案,因为在从服务器接收响应之前,清洁程序的Internet连接可能会断开,或者应用程序可能会退出,因此,完成处理程序{(error, committed,snapshot) in....将不会被评估,并且第二次写入将不会成功。
另一种情况是:执行第一次写操作,
1.在客户端应用程序中收到响应
2. 在客户端应用程序中尚未收到响应
,用户立即退出该应用程序。在这种情况下,将永远不会执行第二次写操作,因为在完成处理程序中收到(或未收到)响应后,尚未评估任何代码,因此数据库处于不一致状态。
从Firebase文档:
事务不会在应用程序重启后持续存在
即使启用了持久性,事务也不会在应用程序重启后持久化。因此,您不能依赖脱机完成的事务提交到Firebase实时数据库。
是否可以使用Swift中的Firebase事务写入Firebase数据库中的多个节点?
如果是这样,我该怎么做?我在此博客中没有看到来自Google 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
Run Code Online (Sandbox Code Playgroud)
是否可以使用Swift中的Firebase事务写入Firebase数据库中的多个节点?
用例并不十分清楚为什么需要事务-似乎需要同时将数据写入多个节点。在这种情况下,您可以“同时”写入多个节点而无需事务。
这是一个例子。给定结构
messages
msg_0
msg: "some message"
msg_1
msg: "another message"
msg_2
msg: "cool message"
Run Code Online (Sandbox Code Playgroud)
运行以下代码后
let messagesRef = self.ref.child("messages")
let path0 = "msg_0/msg"
let path1 = "msg_1/msg"
let path2 = "msg_2/msg"
let childUpdates = [
path0: "0 message",
path1: "1 message",
path2: "2 message"
]
messagesRef.updateChildValues(childUpdates)
Run Code Online (Sandbox Code Playgroud)
结果是同时写入:
messages
msg_0
msg: "0 message"
msg_1
msg: "1 message"
msg_2
msg: "2 message"
Run Code Online (Sandbox Code Playgroud)
如果这不能提供解决方案,则我们可能需要在有关使用Firebase事务的问题中更加清楚。
| 归档时间: |
|
| 查看次数: |
1464 次 |
| 最近记录: |