Last active
May 31, 2018 18:07
-
-
Save pteasima/8f2fbea125a15b2979b4198609737530 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ReactiveSwift | |
import enum Result.Result | |
import Alamofire | |
typealias Never = NoError | |
//This file alone wont build, its just for reading ;) | |
struct Tagged<Tag, RawValue> { | |
var rawValue: RawValue | |
} | |
struct AppContext { | |
var http: SessionManager | |
var userManager: UserManager | |
static let shared: AppContext = .init( | |
http: .default, | |
userManager: .init() | |
) | |
} | |
protocol Effect { | |
associatedtype Context | |
} | |
protocol NetworkEffect: Effect { | |
static func request(path: String) -> Tagged<Result<Data, NetworkError>, Self> | |
} | |
protocol APIEffect: NetworkEffect { | |
static func login(username: String, password: String) -> Tagged<Result<User, RequestError>, Self> | |
} | |
extension Tagged where RawValue: ReactiveEffect { | |
init(_ construct: @escaping (RawValue.Context) -> SignalProducer<Tag, Never>) { | |
rawValue = RawValue.wrappedInit { context in | |
construct(context).map { $0 as Any } | |
} | |
} | |
} | |
extension Tagged where RawValue: ReactiveEffect { | |
var producer: (_ in: RawValue.Context) -> SignalProducer<Tag, Never> { | |
return { context in self.rawValue.producer(context) | |
.filterMap { | |
guard let result = ($0 as? Tag) else { assertionFailure(); return nil } | |
return result | |
} | |
} | |
} | |
} | |
protocol ReactiveEffect: Effect { | |
var producer: (_ in: Context) -> SignalProducer<Any, Never> { get } | |
init(producer: @escaping (Context) -> SignalProducer<Any, Never>) | |
} | |
extension ReactiveEffect { | |
static func wrappedInit(producer: @escaping (Context) -> SignalProducer<Any, Never>) -> Self { | |
return self.init(producer: producer) | |
} | |
} | |
extension ReactiveEffect where Self: NetworkEffect, Context == AppContext { | |
static func request(path: String) -> Tagged<Result<Data, NetworkError>, Self> { | |
//its still possible to return a producer of the wrong type, but youd have to be a moron and not use this typesafe convenience init | |
// Preventing this would require replacing Tagged with a ReactiveSwift specific type, which would prevent the creation of other interpreters | |
return .init { context in | |
//context.http... | |
return .empty | |
} | |
} | |
} | |
extension NetworkEffect where Self: ReactiveEffect { | |
static func request<Value: Codable>(path: String) -> Tagged<Result<Value, RequestError>, Self> { | |
return Tagged.init { context in | |
self.request(path: path).producer(context) | |
.map { result in | |
switch result { | |
case let .success(data): | |
return Result { //TODO this will crash since it doesnt throw RequestError | |
try JSONDecoder().decode(Value.self, from: data) | |
} | |
case let .failure(e): | |
return .failure(RequestError.network(e)) | |
} | |
} | |
} | |
} | |
} | |
extension ReactiveEffect where Self: APIEffect, Context == AppContext { | |
static func login(username: String, password: String) -> Tagged<Result<User, RequestError>, Self> { | |
return Tagged.init { context in | |
self.request(path: "login"/*, ...*/).producer(context) | |
} | |
} | |
} | |
struct NormalEffect<Context>: ReactiveEffect { | |
let producer: (_ in: Context) -> SignalProducer<Any, Never> | |
} | |
extension NormalEffect: NetworkEffect, APIEffect where Context == AppContext {} | |
struct MockEffect<Context>: ReactiveEffect { | |
let producer: (_ in: Context) -> SignalProducer<Any, Never> | |
} | |
extension MockEffect: NetworkEffect, APIEffect where Context == AppContext { | |
static func request(path: String) -> Tagged<Result<Data, NetworkError>, MockEffect> { | |
return Tagged.init { context in | |
SignalProducer(value: Result(value: Data())) | |
} | |
} | |
} | |
//use it | |
protocol LoginViewModeling: class { | |
} | |
final class LoginViewModel<E: NetworkEffect & APIEffect> | |
: LoginViewModeling where E: ReactiveEffect { | |
init(context: E.Context) { | |
E.request(path: "").producer(context).startWithValues { | |
print($0) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment