The latest version of the class can always be found at: https://gist.github.com/dtartaglia/bf24b3b59dd33fef4cfa568d0e47f91b

Did I miss anything? Is there anything I can do to make the code more lean or more reusable? Ideas for better names?

//  RxCache.swift
//  Created by Daniel Tartaglia on 12/20/18.
//  Copyright © 2018 Daniel Tartaglia. MIT License.

import RxSwift

final class Cache<Key: Hashable, State> {
    init(qos: DispatchQoS, source: @escaping (Key) -> Single<State>) {
        scheduler = SerialDispatchQueueScheduler(qos: qos)
        getState = source

    func data(for key: Key) -> Single<State> {
        lock.lock(); defer { lock.unlock() }
        guard let state = cache[key] else {
            let state = ReplaySubject<State>.create(bufferSize: 1)
                    onSuccess: { state.onNext($0) },
                    onError: { [weak self] _ in
                        guard let this = self else { return }
                        this.lock.lock(); defer { this.lock.unlock() }
                        this.cache.removeValue(forKey: key)
                .disposed(by: bag)
            cache[key] = state
            return state.asSingle()

        return state.asSingle()

    private var cache: [Key: ReplaySubject<State>] = [:]
    private let scheduler: SerialDispatchQueueScheduler
    private let lock = NSRecursiveLock()
    private let getState: (Key) -> Single<State>
    private let bag = DisposeBag()

The scheduler exists because Subjects aren't thread safe and I have no way of knowing what thread getData might be emitting on.

The lock exists because I don't want to create multiple responses for the same key in the event that the date(for:) method is called from multiple threads.

I added code to handle errors. Errors don't exit the class but they do remove the cache object so the next attempt will create a new ReplaySubject.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.