Skip to content

Instantly share code, notes, and snippets.

@AdieOlami
Created January 23, 2021 00:52
Show Gist options
  • Save AdieOlami/dcf2570081f68b542b1841c214beec07 to your computer and use it in GitHub Desktop.
Save AdieOlami/dcf2570081f68b542b1841c214beec07 to your computer and use it in GitHub Desktop.
Migrating Moya RxSwift to Combine exposing every process.
class OnlineProvider<Target> where Target: Moya.TargetType {
fileprivate let provider: MoyaProvider<Target>
fileprivate let reachabilityManager: ReachabilityManager
private let authManager: AuthManager
init(endpointClosure: @escaping MoyaProvider<Target>.EndpointClosure = MoyaProvider<Target>.defaultEndpointMapping,
requestClosure: @escaping MoyaProvider<Target>.RequestClosure = MoyaProvider<Target>.defaultRequestMapping,
stubClosure: @escaping MoyaProvider<Target>.StubClosure = MoyaProvider<Target>.neverStub,
session: Session = MoyaProvider<Target>.defaultAlamofireSession(),
trackInflights: Bool = false,
reachabilityManager: ReachabilityManager,
authManager: AuthManager
) {
self.reachabilityManager = reachabilityManager
self.authManager = authManager
self.provider = MoyaProvider(endpointClosure: endpointClosure, requestClosure: requestClosure, stubClosure: stubClosure, session: session, plugins: [NetworkLoggerPlugin()], trackInflights: trackInflights)
}
func request(_ token: Target) -> Observable<Moya.Response> {
let actualRequest = provider.rx.request(token)
return reachabilityManager
.connectionIsReachable
.ignore(value: false) // Wait until we're online
.take(1) // Take 1 to make sure we only invoke the API once.
.flatMap { _ in // Turn the online state into a network request
return actualRequest
.filterSuccessfulStatusCodes()
.do(onSuccess: {(response) in
}, onError: {[weak self] (error) in
if let error = error as? MoyaError, let self = self {
switch error {
case .statusCode(let response):
if response.statusCode == 401 {
// Unauthorized
// if self.authManager.isAuthorized {
// AuthManager.removeToken()
// Application.shared.presentInitialScreen(in: Application.shared.window)
// }
}
default: break
}
}
})
}
}
func request2(_ token: Target) -> AnyPublisher<Moya.Response, MoyaError> {
let actualRequest = provider.requestPublisher(token)
return reachabilityManager
.isReachable
.ignore(value: false)
.prefix(1)
.flatMap{ _ in
return actualRequest
.filterSuccessfulStatusCodes()
.handleEvents { (response) in
//
} receiveCompletion: {[weak self] (error) in
switch error {
case.failure(let failure):
switch failure {
case .statusCode(let response):
if response.statusCode == 401 {
}
default: break
}
default: break
}
}
}
.eraseToAnyPublisher()
}
}
protocol NetworkingType {
associatedtype T: TargetType
var provider: OnlineProvider<T> { get }
static func defaultNetworking() -> Self
// static func stubbingNetworking() -> Self
}
struct AuthNetworking: NetworkingType {
typealias T = AuthApi
let provider: OnlineProvider<T>
static func defaultNetworking() -> Self {
return AuthNetworking(provider: newAuthProvider())
}
func request(_ token: T) -> Observable<Moya.Response> {
return self.provider.request(token)
}
func request2(_ token: T) -> AnyPublisher<Moya.Response, MoyaError> {
return self.provider.request2(token)
}
}
struct HomeNetworking: NetworkingType {
typealias T = HomeAPI
let provider: OnlineProvider<T>
static func defaultNetworking() -> Self {
return HomeNetworking(provider: newProvider())
}
func request(_ token: T) -> Observable<Moya.Response> {
return self.provider.request(token)
}
func request2(_ token: T) -> AnyPublisher<Moya.Response, MoyaError> {
return self.provider.request2(token)
}
}
protocol AuthDataSource {
func login(credentials: Credentials) -> Single<BaseResponseData<AuthResponse>>
func login2(credentials: Credentials) -> Future<BaseResponseData<AuthResponse>, MoyaError>
}
class AuthRepository: AuthDataSource {
let authProvider: AuthNetworking
init(authProvider: AuthNetworking) {
self.authProvider = authProvider
}
}
extension AuthRepository {
func login2(credentials: Credentials) -> Future<BaseResponseData<AuthResponse>, MoyaError> {
return requestObject2(.login(phoneNumber: credentials.phoneNumber, password: credentials.password), type: AuthResponse.self)
}
func login(credentials: Credentials) -> Single<BaseResponseData<AuthResponse>> {
return requestObject(.login(phoneNumber: credentials.phoneNumber, password: credentials.password), type: AuthResponse.self)
}
}
extension AuthRepository {
private func request(_ target: AuthApi) -> Single<Any> {
return authProvider.request(target)
.mapJSON()
.observeOn(MainScheduler.instance)
.asSingle()
}
private func requestObject<T: Codable>(_ target: AuthApi, type: T.Type) -> Single<BaseResponseData<T>> {
return authProvider.request(target)
.mapObject(BaseResponseData<T>.self)
.observeOn(MainScheduler.instance)
.asSingle()
}
private func requestObject2<T: Codable>(_ target: AuthApi, type: T.Type) -> Future<BaseResponseData<T>, MoyaError> {
return authProvider.request2(target)
.mapObject(BaseResponseData<T>.self)
.receive(on: RunLoop.main)
.asFuture()
}
}
extension Publisher {
func asFuture() -> Future<Output, Failure> {
return Future { promise in
var ticket: AnyCancellable? = nil
ticket = self.sink(
receiveCompletion: {
ticket?.cancel()
ticket = nil
switch $0 {
case .failure(let error):
promise(.failure(error))
case .finished:
// WHAT DO WE DO HERE???
fatalError()
}
},
receiveValue: {
ticket?.cancel()
ticket = nil
promise(.success($0))
})
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment