How can roll back when saving data to cloud-firestore if there is an error?

Viewed 73

I want to save data to four document in firestore. But I want to cancel all saving if in one of docs has error. I dont know it is possible with async-await. I share with you giveOrder function and button function that I call the giveOrder function in it. Finally ss is I got the error. May you help me

public func giveOrder(description: String, total: String, orderModel: [ChosenProduct]) async throws -> Bool {
    let dealerID = orderModel[0].dealerID
    let orderID = UUID().uuidString
    getAccountID { email, customerID in
        let orderInfo: [String : Any] = ["Date": Date.now,
                                         "Dealer ID": dealerID,
                                         "Customer ID": customerID,
                                         "Statu": "Preparing",
                                         "Order ID": orderID,
                                         "Description": description,
                                         "Total Cost": total]
        self.reference = self.database.document("Orders/\(customerID)/\(orderID)/Order Info")
        self.thirdReference = self.database.document("Orders/\(dealerID)/\(orderID)/Order Info")
        
        Task {
            try await self.reference.setData(orderInfo, merge: true)
            for product in orderModel {
                let orderPost: [String : Any] = ["Name" : product.name,
                                                 "Store Name" : product.storeName,
                                                 "Unit": product.unit,
                                                 "Unit Type": product.unitType,
                                                 "Price": product.price,
                                                 "Price Type": product.priceType,
                                                 "Product ID": product.id,
                                                 "Dealer ID": product.dealerID]
                self.secondReference = self.database.document("Orders/\(customerID)/\(orderID)/\(product.id)")
                try await self.secondReference.setData(orderPost, merge: true)
            }
            
            
            
            try await self.thirdReference.setData(orderInfo, merge: true)
            for product in orderModel {
                let orderPost: [String : Any] = ["Name" : product.name,
                                                 "Store Name" : product.storeName,
                                                 "Unit": product.unit,
                                                 "Unit Type": product.unitType,
                                                 "Price": product.price,
                                                 "Price Type": product.priceType,
                                                 "Product ID": product.id,
                                                 "Customer ID": customerID]
                self.fourthReference = self.database.document("Orders/\(dealerID)/\(orderID)/\(product.id)")
                try await self.fourthReference.setData(orderPost, merge: true)
            }
        }
        
    }
    return true
}

This is button function:

@objc func tappedBuyButton() async {
        let comment = commentText.text ?? "No comments"
        let total = totalLabel.text
        let ordered = NewOrderVC.chosenProducts
        do {
            let success = try await DatabaseManager.shared.giveOrder(description: comment, total: total!, orderModel: ordered)
            if success {
                NewOrderVC.chosenProducts.removeAll(keepingCapacity: false)
                self.tableView.reloadData()
            } else {
                self.makeAlert(title: "Error", message: "Could not order. Please try again!")
            }
        } catch {
            self.makeAlert(title: "Error", message: "Could not order. Please try again!")
        }
    }

And this is the thread I got: [error: memory read failed for 0x2b88f7db5e00

Thread 1: EXC_BAD_ACCESS (code=1, address=0x2b88f7db5e90)]1

1 Answers

Let us say it dies after successfully saving steps 1 and 2, but before it finishes step 3. Are you saying that you want it to gracefully stop, not saving steps 3 and 4? Or are you saying that you want it to also roll back 1 and 2, as well?

If you want it to roll back and are using Cloud FireStore, you might consider using transactions and batched writes:

Batched writes

If you do not need to read any documents in your operation set, you can execute multiple write operations as a single batch that contains any combination of set(), update(), or delete() operations. A batch of writes completes atomically and can write to multiple documents. The following example shows how to build and commit a write batch:

// Get new write batch 
let batch = db.batch()

// Set the value of 'NYC' 
let nycRef = db.collection("cities").document("NYC") 
batch.setData([:], forDocument: nycRef)

// Update the population of 'SF'
let sfRef = db.collection("cities").document("SF") 
batch.updateData(["population": 1000000 ], forDocument: sfRef)

// Delete the city 'LA' 
let laRef = db.collection("cities").document("LA") 
batch.deleteDocument(laRef)

// Commit the batch
batch.commit() { err in
    if let err = err {
        print("Error writing batch \(err)")
    } else {
        print("Batch write succeeded.")
    }
}

A batched write can contain up to 500 operations. Each operation in the batch counts separately towards your Cloud Firestore usage.

Like transactions, batched writes are atomic. Unlike transactions, batched writes do not need to ensure that read documents remain un-modified which leads to fewer failure cases. They are not subject to retries or to failures from too many retries. Batched writes execute even when the user's device is offline.

Related