Search code examples
swiftcombine

Swift Combine: Return local data first and then remote data


Whenever usecase call any method from repository, it should return data saved in Core data first and start fetching data from remote server. Once repository receives data from remote server it should update in local db and emit new event with latest data.

How to do it using Swift Combine?

Class UsersRepository {

  func fetchUsers() -> AnyPublisher<[Users], Error> {
     1. Emit Users from local DB
     2. Emit Users received from remote server
   }
}

Solution

  • You could try something like this:

    import Combine
    
    class Repository {
    
      private let cache: Cache
      private let service: Service
    
      init(cache: Cache, service: Service) {
        self.cache = cache
        self.service = service
      }
    
      func fetch() -> AnyPublisher<[Int], Error> {
        return Publishers.Concatenate(
          prefix: cache.fetch(),
          suffix: service.fetch()
            .handleEvents(receiveOutput: {
              self.cache.cache = $0
            })
        ).eraseToAnyPublisher()
      }
    }
    
    class Cache {
    
      var cache: [Int] = [1, 2]
    
      func fetch() -> AnyPublisher<[Int], Error> {
        return Just(cache)
          .mapError { $0 }
          .eraseToAnyPublisher()
      }
    }
    
    class Service {
      func fetch() -> AnyPublisher<[Int], Error> {
        return Just([3, 4])
          .mapError { $0 }
          .eraseToAnyPublisher()
      }
    }
    
    let repo = Repository(cache: Cache(), service: Service())
    
    repo.fetch()
      .sink(
        receiveCompletion: { _ in },
        receiveValue: {
          print($0)
        }
      )
    
    repo.fetch()
      .sink(
        receiveCompletion: { _ in },
        receiveValue: {
          print($0)
        }
      )
    
    // PRINTS:
    // [1, 2]
    // [3, 4]
    // [3, 4]
    // [3, 4]