Skip to content

Instantly share code, notes, and snippets.

@hoc081098
Created December 10, 2022 12:10
Show Gist options
  • Save hoc081098/806560213b063523e9d475efdd60829d to your computer and use it in GitHub Desktop.
Save hoc081098/806560213b063523e9d475efdd60829d to your computer and use it in GitHub Desktop.
//
// Demo.swift
// PhDownloader Example
//
// Created by Hoc Nguyen T. on 12/10/22.
//
import Foundation
import RxSwift
import RxRelay
enum SessionServiceDelegateError: Error {
case destroyed
}
class SessionServiceDelegate {
private let isRefreshing = AtomicBoolean()
private let tokenRelay = BehaviorRelay<String?>(value: nil)
private let tokenScheduler = SerialDispatchQueueScheduler(qos: .userInitiated)
/// Get from local storage
private func tokenObservable() -> Observable<String?> {
tokenRelay.asObservable()
}
/// Save token to local storage
private func saveToken(token: String) -> Completable {
.deferred { [tokenRelay] in
tokenRelay.accept(token)
return .empty()
}.subscribe(on: tokenScheduler)
}
/// Refresh token from server
private func refreshToken() -> Single<String> {
Single
.just("token")
.delay(
.seconds(3),
scheduler: ConcurrentDispatchQueueScheduler.init(qos: .utility)
)
.do(
onSubscribe: {
print("getTokenRefreshService >>>> refreshToken start...")
},
onDispose: {
print("getTokenRefreshService >>>> refreshToken done")
}
)
}
func getTokenRefreshService() -> Single<Void> {
.deferred { [weak self] in
guard let self = self else { return Single.error(SessionServiceDelegateError.destroyed) }
if !self.isRefreshing.getAndSet(value: true) {
print("getTokenRefreshService [execute]: \(self.isRefreshing.get())")
return self.refreshToken()
.flatMap { token in
self
.saveToken(token: token)
.andThen(Single.just(()))
}
.do(
onDispose: {
_ = self.isRefreshing.getAndSet(value: false)
}
)
}
print("getTokenRefreshService [ignore]: \(self.isRefreshing.get())")
return self.tokenObservable()
.filter { $0 != nil }
.take(1)
.map { _ in () }
.asSingle()
}
}
}
typealias Byte = UInt8
class AtomicBoolean {
private var val: Byte = 0
/// Sets the value, and returns the previous value.
/// The test/set is an atomic operation.
func getAndSet(value: Bool) -> Bool {
if value {
return OSAtomicTestAndSet(0, &val)
} else {
return OSAtomicTestAndClear(0, &val)
}
}
/// Returns the current value of the boolean.
/// The value may change before this method returns.
func get() -> Bool {
return val != 0
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment