title: Firebase: How to update multiple nodes transactionally? Swift 3
body: <p>I am developing an application designed for cleaning services. In this application the employees (cleaners) can read a list of jobs (bookings) which have been made by multiple customers (Users).   </p>
  
  <p>All cleaners can read all bookings in Users node. Initially, when a booking is saved in the database by a user, the key <code>claimed:</code> has a value of  <code>“false”</code>,meaning it has not been claimed by a cleaner.    </p>
  
  <p>Whenever a cleaner wants to claim a job present in the list, he will have to touch a button which will make a request to Firebase Database to modify the value of key <code>claimed</code> to <code>true</code> at path  <code>/Users/UID/bookings/bookingNumber</code>    </p>
  
  <p>Only one cleaner at a time should be allowed to modify the value of <code>claimed</code> key. If multiple cleaners were allowed to modify the value of <code>claimed</code> key, other cleaners would end up claiming the same job. We don't want that to happen.    </p>
  
  <p>Furthermore, after a cleaner modifies the value of <code>claimed</code> key to <code>true</code>, we will need to make another request to path <code>CLeaners/UID/bookings/bookingNumber</code> in order to save the booking he has just claimed in the cleaners node.<br>
      - According to the firebase docs, we use transactions whenever we want a resource to be modified by only one request at a time, if there are multiple concurrent requests trying to write to the same resource, one of them will succeed. 
  But the problem with using transactions is that it enables writing to <strong>only one path</strong>, it does not enable writing <strong>to multiple paths</strong>.</p>
  
  <p>How can I ensure that even though multiple users can read this path  <code>/Users/UID/bookings/bookingNumber</code>, only one user at a time can update it? And if the write is successful, further write to the second path   <code>Cleaners/UID/bookings/bookingNumber</code>.</p>
  
  <p>We need to take into account that the client's internet connection can drop, the user can quit the app, or simply the phone will switch off unexpectedly any time in-between writing to the paths specified above.</p>
  
  <p>The database structure is as follows</p>
  
  <pre><code>Root
    Cleaners
      UID
       bookings
         bookingNumber
           amount: “10”
           claimed: “true”
  
  
     Users
       UID
        otherID
          bookingNumber
            amount: “10”
            claimed: “true”
  
           bookingNumber
            amount: “50”
            claimed: “false”
  </code></pre>
  
  <p>To avoid any overwrites, I have decided to use Firebase transactions. I can write to a single node as transaction, but writing to the second node in the completion handler is not a solution since the cleaner's internet connection may drop or app could be quit before a response is received from the server, thus the code in the completion handler <code>{(error, committed,snapshot) in....</code>would not be evaluated and the second write would not succeed.   </p>
  
  <p>Another scenario would be:  first write is executed,<br>
   1. response is received  in client app<br>
   2. response is not yet received in client app<br>
       and the user quits the app immediately. In this case the second write will never be executed since no code is yet evaluated after response is received (or not) in the completion handler, thus leaving my database in an inconsistent state. </p>
  
  <hr>
  
  <p>From Firebase docs:</p>
  
  <blockquote>
    <p>Transactions are not persisted across app restarts</p>
    
    <p>Even with persistence enabled, transactions are not persisted across
    app restarts. So you cannot rely on transactions done offline being
    committed to your Firebase Realtime Database.</p>
  </blockquote>
  
  <ul>
  <li><p>Is it possible to write to multiple nodes in a Firebase Database using Firebase Transactions in Swift?     </p></li>
  </ul>
  
  <p>If so, how can I do this? I see no example in this blog from google <a href="https://firebase.googleblog.com/2015/09/introducing-multi-location-updates-and_86.html" rel="nofollow noreferrer">https://firebase.googleblog.com/2015/09/introducing-multi-location-updates-and_86.html</a>  . I do understand that you can write atomically to multiple nodes, but I'd like to write as transaction.
  I am trying to write to two nodes in the <code>else</code> clause, but I get a warning on this line <code>let updated = updateInUsersAndCleaners as? FIRMutableData</code>    </p>
  
  <blockquote>
    <p>Cast from '[FIRDatabaseReference : FIRMutableData]' to unrelated type 
    'FIRMutableData' always fails</p>
  </blockquote>
  
  <pre><code>    class ClaimDetail: UIViewController,UITableViewDelegate,UITableViewDataSource {
  
   var valueRetrieved = [String:AnyObject]()
   var uid:String?
  
     @IBAction func claimJob(_ sender: Any) {
  
        dbRef.runTransactionBlock({ (_ currentData:FIRMutableData) -&gt; 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() -&gt; 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
  </code></pre>