Skip to content

Instantly share code, notes, and snippets.

@pietrocaselani
Last active January 4, 2019 03:35
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pietrocaselani/9b992e00c7615227f8614567249429eb to your computer and use it in GitHub Desktop.
Save pietrocaselani/9b992e00c7615227f8614567249429eb to your computer and use it in GitHub Desktop.
Carlos + Moya + Rx

This gist is a sample to demonstrate the libraries Carlos, Moya, RxSwift working together.

func example() -> Single<String> {
let cache = MemoryCacheLevel()
.compose(DiskCacheLevel<GithubService, NSData>())
.compose(MoyaFetcher(provider: provider))
return cache.get(GithubService.zen)
.asObservable()
.map { data -> String in
guard let string = String(data: data as Data, encoding: .utf8) else {
throw MappingError.string(data)
}
return string
}
.asSingle()
}
import PiedPiper
import RxSwift
extension Future {
func asObservable() -> Observable<T> {
let observable = Observable<T>.create { observer in
self.onCompletion { (result: Result<T>) in
switch result {
case .cancelled:
observer.onCompleted()
case .success(let value):
observer.onNext(value)
observer.onCompleted()
case .error(let error):
observer.onError(error)
}
}
return Disposables.create {
self.cancel()
}
}
return observable
}
}
import Carlos
import Moya
enum GithubService {
case zen
}
extension GithubService: TargetType, StringConvertible {
func toString() -> String {
return self.path
}
var baseURL: URL {
return URL(string: "https://api.github.com")!
}
var path: String {
switch self {
case .zen:
return "/zen"
}
}
var method: Moya.Method {
switch self {
case .zen:
return .get
}
}
var parameters: [String: Any]? {
switch self {
case .zen:
return nil
}
}
var parameterEncoding: ParameterEncoding {
switch self {
case .zen:
return URLEncoding.default
}
}
var task: Task {
switch self {
case .zen:
return .request
}
}
var headers: [String: String]? {
switch self {
case .zen:
return nil
}
}
var sampleData: Data {
return Data()
}
}
import Carlos
import Moya
import PiedPiper
final class MoyaFetcher<Target:TargetType>: Fetcher {
typealias KeyType = Target
typealias OutputType = NSData
private let lock: ReadWriteLock = PThreadReadWriteLock()
private var pendingRequests: [Future<OutputType>] = []
private let provider: MoyaProvider<Target>
init(provider: MoyaProvider<Target>) {
self.provider = provider
}
func get(_ key: Target) -> Future<OutputType> {
let promise = startRequest(target: key)
promise.onSuccess { _ in
self.removePendingRequests(promise)
}.onFailure { _ in
self.removePendingRequests(promise)
}.onCancel {
self.removePendingRequests(promise)
}
self.addPendingRequest(promise)
return promise.future
}
private func startRequest(target: Target) -> Future<OutputType> {
let promise = Promise<OutputType>()
let cancellable = provider.request(target) { result in
DispatchQueue.main.async {
if case .success(let data) = result {
promise.succeed(data.data as NSData)
} else if case .failure(let error) = result {
promise.fail(error)
}
}
}
promise.onCancel {
cancellable.cancel()
}
return promise.future
}
private func addPendingRequest(_ request: Future<OutputType>) {
lock.withWriteLock {
self.pendingRequests.append(request)
}
}
private func removePendingRequests(_ request: Future<OutputType>) {
if let idx = lock.withReadLock({ self.pendingRequests.index(where: { $0 === request }) }) {
_ = lock.withWriteLock {
self.pendingRequests.remove(at: idx)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment