Home > Software engineering >  change a value in Firestore based on another value and update the ui to reflect that change
change a value in Firestore based on another value and update the ui to reflect that change

Time:01-17

Hi I'm very new to swift and have been following tutorials for a long time and recently I decided to try and build an app unfortunately I've run into this problem and can't figure out how to solve it thanks in advance for any help.

I'm building a league table app for speedway and have chosen to use firestore to update the values within my app I'm currently able to update the values and have them shown within the UI but cannot work out how update the values based of other values within the same document for example total points needs to equal wins * 3 making 3 wins equal 9 points at the moment updating each value individually is there a way of doing this thanks in advance for any help.

image of my document

class PremierLeaugeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    let database = Firestore.firestore()
    
    private var service: premiership?
    
    private var viewModel: [LeaugeViewModel] = []
    
    var leauge = [LeaugeViewModel]() {
        didSet {
            DispatchQueue.main.asyncAfter(deadline: .now()   5) {
                self.tableView.reloadData()
            }
        }
    }
    
    private var allLeauges = [LeaugeViewModel]() {
        didSet {
            DispatchQueue.main.async {
                self.leauge = self.allLeauges
            }
        }
    }
     
    let tableView: UITableView = {
        let tb = UITableView(frame: .zero, style: .plain)
        tb.register(LeaugeHeaderTableViewHeaderView.self, forHeaderFooterViewReuseIdentifier: LeaugeHeaderTableViewHeaderView.identifier)
        tb.register(LeaugeTableViewCell.self, forCellReuseIdentifier: LeaugeTableViewCell.identifier)
        return tb
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(tableView)
        tableView.delegate = self
        tableView.dataSource = self
        loadData()
        title = "Premier leauge"
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        tableView.frame = view.bounds
    }
    
    func loadData() {
        service = premiership()
        
        service?.get(collectionID: "league", handler: { [weak self] leauge in
            self?.allLeauges = leauge.sorted(by: { first, second in
                
                //    Everything is equal
                if first.totalPoints == second.totalPoints && first.pointsDifference == second.pointsDifference && first.wins == second.wins && first.draws == second.draws && first.losses == second.losses && first.meets == second.meets {
                    print("This one 1")
                    return first.leaugePosition < second.leaugePosition
                }
                
                //      Total points is the only difference
                else if first.totalPoints > second.totalPoints && first.pointsDifference >= second.pointsDifference ||  first.pointsDifference < second.pointsDifference {
                    print("This one 2")
                    return first.totalPoints > second.totalPoints
                }
                
                //    Points difference is the only difference
                else if first.totalPoints >= second.totalPoints && first.pointsDifference > second.pointsDifference {
                    print("This one 3")
                    return first.pointsDifference > second.pointsDifference
                }
                
                else {
                    return first.pointsDifference > second.pointsDifference
                }
            })
        })
        
        tableView.reloadData()
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return leauge.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: LeaugeTableViewCell.identifier, for: indexPath) as? LeaugeTableViewCell else {
            fatalError()
        }

        cell.configure(with: leauge[indexPath.row])
        
        return cell
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: LeaugeHeaderTableViewHeaderView.identifier)
        
        return header
    }
    
    func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        
    }
}

This is the function I added to change the fields dependent on other values its working as id like for one document but id like to be able to do this for multiple documents/teams

    private func transaction() {
    // [START transaction]
    let teamRef = database.document("league/Belle Vue")

    database.runTransaction({ (transaction, errorPointer) -> Any? in
        var teamsDocuments: DocumentSnapshot
        do {
            try teamsDocuments = transaction.getDocument(teamRef)

        } catch let fetchError as NSError {
            errorPointer?.pointee = fetchError
            return nil
        }
        
        guard let meets = teamsDocuments.data()?["meets"] as? Int else {
            let error = NSError(
                domain: "AppErrorDomain",
                code: -1,
                userInfo: [
                    NSLocalizedDescriptionKey: "Unable to retrieve meets from snapshot \(teamsDocuments)"
                ]
            )
            errorPointer?.pointee = error
            return nil
        }

        
        guard let wins = teamsDocuments.data()?["wins"] as? Int else {
            let error = NSError(
                domain: "AppErrorDomain",
                code: -1,
                userInfo: [
                    NSLocalizedDescriptionKey: "Unable to retrieve wins from snapshot \(teamsDocuments)"
                ]
            )
            errorPointer?.pointee = error
            return nil
        }
        
        guard let draws = teamsDocuments.data()?["draws"] as? Int else {
            let error = NSError(
                domain: "AppErrorDomain",
                code: -1,
                userInfo: [
                    NSLocalizedDescriptionKey: "Unable to retrieve total points from snapshot \(teamsDocuments)"
                ]
            )
            errorPointer?.pointee = error
            return nil
        }
        
        guard let losses = teamsDocuments.data()?["losses"] as? Int else {
            let error = NSError(
                domain: "AppErrorDomain",
                code: -1,
                userInfo: [
                    NSLocalizedDescriptionKey: "Unable to retrieve losses from snapshot \(teamsDocuments)"
                ]
            )
            errorPointer?.pointee = error
            return nil
        }


        // Note: this could be done without a transaction
        //       by updating the population using FieldValue.increment()
        let newTotalPoints = wins * 3   draws
        
        let newMeetsValue = wins   draws   losses

        transaction.updateData(["total-points": newTotalPoints], forDocument: teamRef)
        
        transaction.updateData(["meets": newMeetsValue], forDocument: teamRef)

        return newTotalPoints
    }) { (object, error) in
        if let error = error {
            print("Error updating total Points: \(error)")
        } else {
            print("Total Points increased to \(object!)")
        }
        
    }
    
    
    // [END transaction]
}

CodePudding user response:

private func transaction() {
    let db = Firestore.firestore()
    let teamRef = db.document("league/Belle Vue")
    
    db.runTransaction({ (transaction, errorPointer) -> Any? in
        let teamsDoc: DocumentSnapshot
        
        do {
            try teamsDoc = transaction.getDocument(teamRef)
        } catch let fetchError as NSError {
            errorPointer?.pointee = fetchError
            return nil
        }
        
        // You can bundle all of your property gets in a single guard statement or continue as you did separately;
        // this is just a matter of preference.
        guard let wins = teamsDoc.get("wins") as? Int,
              let draws = teamsDoc.get("draws") as? Int,
              let losses = teamsDoc.get("losses") as? Int else {
                  let error = NSError(domain: "com.yourApp",
                                      code: 1,
                                      userInfo: [NSLocalizedDescriptionKey: "Unable to get values from document."])
                  errorPointer?.pointee = error
                  return nil
              }
        let newTotalPoints = (wins * 3)   draws
        let newMeetsValue = wins   draws   losses
        
        transaction.updateData([
            "total-points": newTotalPoints
        ], forDocument: teamRef)
        
        transaction.updateData([
            "meets": newMeetsValue
        ], forDocument: teamRef)
        
        // Update as many additional documents as needed here. You are not confined to updating only the document
        // that you read from. Treat this transaction as a normal Firestore operation and do what needs to be done
        // here before you return out of it.
        
        // You can return any object or value out of this transaction if you need the object/value when the
        // transaction completes. If you don't need anything out of this transaction, however, then simply return
        // nil.
        return newTotalPoints
    }) { (newTotalPoints, error) in
        if let error = error {
            print("Error updating total Points: \(error.localizedDescription)")
        } else if let newTotalPoints = newTotalPoints {
            print("Total Points increased to \(newTotalPoints)")
        }
    }
}

Alternatively, if you don't need anything from the transaction after it completes, then return nil:

private func transaction() {
    let db = Firestore.firestore()
    let teamRef = db.document("league/Belle Vue")
    
    db.runTransaction({ (transaction, errorPointer) -> Any? in
        let teamsDoc: DocumentSnapshot
        
        do {
            try teamsDoc = transaction.getDocument(teamRef)
        } catch let fetchError as NSError {
            errorPointer?.pointee = fetchError
            return nil
        }

        guard let wins = teamsDoc.get("wins") as? Int,
              let draws = teamsDoc.get("draws") as? Int,
              let losses = teamsDoc.get("losses") as? Int else {
                  let error = NSError(domain: "com.yourApp",
                                      code: 1,
                                      userInfo: [NSLocalizedDescriptionKey: "Unable to get values from document."])
                  errorPointer?.pointee = error
                  return nil
              }
        let newTotalPoints = (wins * 3)   draws
        let newMeetsValue = wins   draws   losses
        
        transaction.updateData([
            "total-points": newTotalPoints
        ], forDocument: teamRef)
        
        transaction.updateData([
            "meets": newMeetsValue
        ], forDocument: teamRef)

        return nil
    }) { (_, error) in
        if let error = error {
            print("Error updating total Points: \(error.localizedDescription)")
        }
    }
}
  •  Tags:  
  • Related