0
\$\begingroup\$

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)
            getState(key)
                .observeOn(scheduler)
                .subscribe(
                    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.

\$\endgroup\$

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.