0

i'm currently struggling to fetch any changes from an published variable in SwiftUI. Most of the code is created after this tutorial on YouTube.

It's basically an app, that fetches cryptos from a firebase database. To avoid high server costs I want to update any changes of the coins to the database but not have an observer to lower the download rate.

What's the bug?

When I'm adding a coin to my favorites, it sends the data correctly to the database and updates the UI. However when I try to filter the coins the Coin-array switches back to it's previous state. I also added a breakpoint on the CoinCellViewModel(coin: coin)-Line but it only gets executed when I change the filterBy. Here's a little visualisation of the bug:

Repository

class CoinsRepository: ObservableObject {
    
    @Published var coins = [Coin]()
    
    var ref: DatabaseReference!
    
    init() {
        self.ref = Database.database().reference()
        
        loadDatabase(ref)
        
    }

    func loadDatabase(_ ref: DatabaseReference) {
        ref.child("coins").observeSingleEvent(of: .value) { snapshot in
            guard let dictionaries = snapshot.value as? [String: Any] else { return }
            var coinNames: [String] = []
            
            self.coins = dictionaries.compactMap({ (key: String, value: Any) in
                guard let dic = value as? [String: Any] else { return nil }
                coinNames.append(dic["name"] as? String ?? "")
                return Coin(dic)
            })
        }
    }
    
    func updateFavorite(_ coin: Coin, state: Bool) {
        let path = ref.child("coins/\(coin.name)")
        var flag = false
        
        path.updateChildValues(["favorite": state]) { err, ref in
            if let err = err {
                print("ERROR: \(err.localizedDescription)")
            } else {
                
                var i = 0
                var newCoinArray = self.coins
                for coinA in newCoinArray {
                    if coinA.name == coin.name {
                        newCoinArray[i].favorite = state
                    }
                    i += 1
                }
                
                // I guess here's the error
                DispatchQueue.main.async {
                    self.objectWillChange.send()
                    self.coins = newCoinArray
                }
            }
        }
    }

}

ViewModel

class CoinListViewModel: ObservableObject {
    
    @Published var coinRepository = CoinsRepository()
    @Published var coinCellViewModels = [CoinCellViewModel]()

    @Published var filterBy: [Bool] = UserDefaults.standard.array(forKey: "filter") as? [Bool] ?? [false, false, false]
    @Published var fbPrice: Double = 0.00
    @Published var searchText: String = ""

    private var cancellables = Set<AnyCancellable>()
    
    init() {
        
        $searchText
            .combineLatest(coinRepository.$coins, $fbPrice, $filterBy)
            .map(filter)
            .sink { coins in

                self.coinCellViewModels = coins.map { coin in
                    CoinCellViewModel(coin: coin)
                }

            }
            .store(in: &cancellables)
   
    }

    ...

}

updateFavorite(_ coin: Coin, state: Bool) get's called in the CoinCellViewModel() but I guess the code isn't necessary here...

I'm fairly new to the Combine topic and not quite getting all the new methods, so any help is appreciated!

gavisio
  • 79
  • 5
  • 2
    We can’t really answer your question with the information given. You have left out a bunch of code. Please see: [How do I ask a good question?](https://stackoverflow.com/help/how-to-ask) and [How to create a Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example). FWIW, often times the "not updating a published var" problem comes down to you instantiating multiple different models in your code. Remember, every time you call. `CoinListViewModel()` or `CoinsRepository()`, etc., you are creating a new and different instance of those objects. – Yrb Oct 13 '21 at 14:42
  • Hi Yrb, thank you for your answer. I had feared it... I wasn't sure how to insert all necessary code but keep it as short as possible. However, your suggestion solved my problem!! And it was in the CellViewModel, so the one I didn't posted here. So I definitely need to work on how to ask questions... Nevertheless thank you very much! – gavisio Oct 13 '21 at 15:05
  • 1
    The thing you need to keep in mind with your models is "Single Source of Truth". What is the actual, instantiated object that holds my data. It will save you from a world of this kind of hurt. – Yrb Oct 13 '21 at 15:12

0 Answers0