@coder3000
Hi Gilbert
I’ve managed to get code sorted to read the pickup data from a user and then save that data should the user want to edit those details on screen.
There are 3 functions
- getUserDocumentId() - which locates the record for the current User.
- get PickupDetails() - which retrieves the pickup data. In the ViewController I was testing this in, that data is displayed on screen in TextFields
- saveChanges() - which saves the data should the user wish to edit the details.
I’m assuming that you have the following properties for the pickup data:
unit
street
city
state
country
postalCode
latitude
longitude
instructions
To read the data the first thing that has to be done is to identify the user record and get the user document ID.
This function searches the users collection and locates the record where the uid
property is equal to the currentUser.uid. Having got that, it returns the document ID via a completion handler for that collection item.
func getUserDocumentId(completion: @escaping (String) -> Void) {
let db = Firestore.firestore()
let users = db.collection("users")
let query = users.whereField("uid", in: [Auth.auth().currentUser?.uid ?? ""])
query.getDocuments { snapshot, error in
if let error = error {
self.showError(message: error.localizedDescription)
} else if let snapshot = snapshot {
for doc in snapshot.documents {
self.userDocId = doc.documentID
completion(doc.documentID)
}
}
}
}
In this function, the pickup details are then retrieved using that documentID by building a “path” to the correct pickup record.
func getPickupDetails(docID: String) {
let db = Firestore.firestore()
// Build the path to the
let path = db.collection("users/\(docID)/pickup")
path.getDocuments { snapshot, error in
if let error = error {
self.showError(message: error.localizedDescription)
} else if let snapshot = snapshot {
for doc in snapshot.documents {
// Save the pickup document id.
self.pickupDocId = doc.documentID
// Display the pickup details in the UI
self.unitTextField.text = doc["unit"] as? String
self.streetTextField.text = doc["street"] as? String
self.cityTextField.text = doc["city"] as? String
self.stateTextField.text = doc["state"] as? String
self.countryTextField.text = doc["country"] as? String
self.postalCodeTextField.text = doc["postalCode"] as? String
self.latitudeTextField.text = String(doc["latitude"] as? Double ?? 0)
self.longitudeTextField.text = String(doc["longitude"] as? Double ?? 0)
self.instructionsTextView.text = doc["instructions"] as? String
}
}
}
}
Declare two properties of the ViewController
var userDocId: String = "" // Stores the user document id
var pickupDocId: String = "" // Stores the pickup document id
then call the getUserDocumentID which calls the getPickupDetails in the closure code.
getUserDocumentId { docID in
// Save the document id to a variable to be used in the saveChanges function
self.userDocId = docID
// Get the pickup data for the user
self.getPickupDetails(docID: docID)
}
Let’s say the user edits the pickup details on the screen and saves the updated data.
I have a button in the View Controller:
@IBAction func saveChangesTapped(_ sender: Any) {
let error = validateFields()
guard error == nil else {
showError(message: error!)
return
}
saveChanges()
}
validateFields() function which returns a String the same as the validateFields() function in the SignUp View Controller.
func validateFields() -> String? {
// 2. Check that all fields are filled in
if unitTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
streetTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
cityTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
stateTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
countryTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
postalCodeTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
latitudeTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" ||
longitudeTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" {
return "Please fill in all fields. (Instructions are optional)"
}
return nil
}
then the saveChanges() function:
func saveChanges() {
let db = Firestore.firestore()
// Build the path to save the changes made to any of the pickup fields.
let pickupData = db.collection("users").document(userDocId).collection("pickup").document(pickupDocId)
var instructions = ""
let unit = unitTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let street = streetTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let city = cityTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let state = stateTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let country = countryTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let postalCode = postalCodeTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let latitude = latitudeTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let longitude = longitudeTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
if instructionsTextView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
instructions = "None"
} else {
instructions = instructionsTextView.text.trimmingCharacters(in: .whitespacesAndNewlines)
}
pickupData.updateData(["unit": unit,
"street": street,
"city": city,
"state": state,
"country": country,
"postalCode": postalCode,
"latitude": Double(latitude) as Any,
"longitude": Double(longitude) as Any,
"instructions": instructions ])
}
Note the line that says:
let pickupData = db.collection("users").document(userDocId).collection("pickup").document(pickupDocId)
The path to the pickup data is the “users” collection followed by the document Id for that user followed by the “pickup” collection and then the document id for that collection item.
I hope you can make sense of all that.