0

I am trying to use Combine to load initial results using cache from Realm and then making a network request but keep running into road blocks.

My work in progress code tries to fetch cache then chains the network request. It also tries to catch the correct errors and maps them to return data which is correct. I'm trying to change it to return the cached results first and then fetch network results all at the same time, I don't think currently it does this.

My function in Combine:

    func fetch(byCurrency currency: String) -> AnyPublisher<CreditType, DataError> {
        let networkPublisher = store.fetch(byCurrency: currency)

        guard let cacheStore = self.cacheStore else {
            return networkPublisher
        }
        
        let cachedPublisher = cacheStore.fetch(byCurrency: currency)
    
        return cachedPublisher
            .flatMap { cachedCredit -> AnyPublisher<CreditType, DataError> in
                return networkPublisher
                    .flatMap { networkCredit -> AnyPublisher<CreditType, DataError> in
                        return cacheStore.createOrUpdate(networkCredit)
                            .eraseToAnyPublisher()
                    }
                    .eraseToAnyPublisher()
            }
            .catch { error -> AnyPublisher<CreditType, DataError> in
                if case .nonExistent = error {
                    return networkPublisher
                        .flatMap { networkCredit -> AnyPublisher<CreditType, DataError> in
                            return cacheStore.createOrUpdate(networkCredit)
                        }
                        .eraseToAnyPublisher()
                }
                return AnyPublisher(Fail<CreditType, DataError>(error: error))
            }
            .eraseToAnyPublisher()
    }

Function to convert:

    func fetch(byCurrency currency: String, completion: @escaping (Result<CreditType, DataError>) -> Void) {

        // Use cache storage if applicable
        guard let cacheStore = cacheStore else { return store.fetch(byCurrency: currency, completion: completion) }
        
        cacheStore.fetch(byCurrency: currency) {

            // Retrieve missing cache data from cloud if applicable
            if case .nonExistent? = $0.error {

                return self.store.fetch(byCurrency: currency) {

                    guard let element = $0.value, $0.isSuccess else { return completion($0) }
                    cacheStore.createOrUpdate(element, completion: completion)
                }
            }
            
            // Immediately return local response
            completion($0)
            
            guard let cacheElement = $0.value, $0.isSuccess else { return }
            
            // Sync remote updates to cache if applicable
            self.store.fetch(byCurrency: currency) {

                // Validate if any updates that needs to be stored
                guard let element = $0.value, cacheElement.amountCents != element.amountCents, $0.isSuccess else { return }
                
                // Update local storage with updated data
                cacheStore.createOrUpdate(element) {

                    guard $0.isSuccess else {
                        return self.Log(error: "Could not save updated credit locally from remote storage: \(String(describing: $0.error))")
                    }
                    
                    // Callback handler again if updated
                    completion($0)
                }
            }
        }
    }
Rush B
  • 29
  • 1
  • 9
  • 1
    Why don't you just observe changes to a Realm Results and update the UI when you detect changes. In the background you can fetch changes from the cloud and update realm independently. Your update handler will fire once realm is updated with cloud data. This would be much simpler approach than trying to chain cached data and cloud data. – Rob C Apr 06 '22 at 01:02
  • @RobC Great comment and 100% agree! I would like to add an unrelated point for clarity; generally speaking, *fetching data from the cloud* isn't exactly how Realm works. Realm is an Offline First database so the process of fetching data will always (and only) fetch data from the local store (the local Realm files) - Realm handles syncing server and local data on it's own in the background e.g. it's job is to keep that data fresh so the developer doesn't have to do it. – Jay Apr 06 '22 at 17:20

0 Answers0