in firestore/FirestoreExample/RestaurantDetailViewController.swift [126:193]
func reviewController(_ controller: NewReviewViewController,
didSubmitFormWithReview review: Review) {
guard let reference = restaurantReference else { return }
let reviewsCollection = reference.collection("ratings")
let newReviewReference = reviewsCollection.document()
// Writing data in a transaction
let firestore = Firestore.firestore()
firestore.runTransaction({ (transaction, errorPointer) -> Any? in
// Read data from Firestore inside the transaction, so we don't accidentally
// update using stale client data. Error if we're unable to read here.
let restaurantSnapshot: DocumentSnapshot
do {
restaurantSnapshot = try transaction.getDocument(reference)
} catch let error as NSError {
errorPointer?.pointee = error
return nil
}
// Error if the restaurant data in Firestore has somehow changed or is malformed.
let maybeRestaurant: Restaurant?
do {
maybeRestaurant = try restaurantSnapshot.data(as: Restaurant.self)
} catch {
errorPointer?.pointee = NSError(domain: "FriendlyEatsErrorDomain", code: 0, userInfo: [
NSLocalizedDescriptionKey: "Unable to read restaurant at Firestore path: \(reference.path): \(error)",
])
return nil
}
guard let restaurant = maybeRestaurant else {
errorPointer?.pointee = NSError(domain: "FriendlyEatsErrorDomain", code: 0, userInfo: [
NSLocalizedDescriptionKey: "Missing restaurant at Firestore path: \(reference.path)",
])
return nil
}
// Update the restaurant's rating and rating count and post the new review at the
// same time.
let newAverage = (Float(restaurant.ratingCount) * restaurant
.averageRating + Float(review.rating))
/ Float(restaurant.ratingCount + 1)
do {
try transaction.setData(from: review, forDocument: newReviewReference)
} catch let error as NSError {
errorPointer?.pointee = error
return nil
}
transaction.updateData([
"numRatings": restaurant.ratingCount + 1,
"avgRating": newAverage,
], forDocument: reference)
return nil
}) { object, error in
if let error = error {
print(error)
} else {
// Pop the review controller on success
if self.navigationController?.topViewController?
.isKind(of: NewReviewViewController.self) ?? false {
self.navigationController?.popViewController(animated: true)
}
}
}
}