Suppose there are two asyncronous requests to an API which need to be executed in sequence. The result of the first request is dictionary which is used by second request in order to get the final dictionary. Interstellar (https://github.com/JensRavens/Interstellar) enables chaining the asyncronous requests in a way to execute them one after another in sequence: For example:
typealias JSONDictionary = [String: AnyObject]
func create(parameters: JSONDictionary, completion: Result<JSONDictionary> -> Void) {
Request.POST("objects", parameters: parameters, completion: completion)
}
func verify(parameters: JSONDictionary, completion: Result<JSONDictionary> -> Void) {
guard let id = parameters["id"] as? String else {
let error = NSError(domain: "", code: 401, userInfo: nil)
completion(.Error(error))
return
}
Request.POST(String(format: "objects/%@/verify", id), parameters: nil, completion: completion)
}
"create" needs to be executed first and the result dictionary will be used to execute "verify": This works great:
address.flatMap(Address.create).flatMap(Address.verify)
.next { dictionary in
expectation.fulfill()
}
.error { error in
print("There was a an error: \(message)")
}
address.update(self.parameters)
And with code bellow (from Interstellar slides):
infix operator >>> { associativity left precedence 160 }
func >>> <A, B> (left: Signal<A>, right: A->Result<B>) -> Signal<B> {
return left.flatMap(right)
}
func >>> <A, B> (left: Signal<A>, right: (A, (Result<B> -> Void)) -> Void) -> Signal<B> {
return left.flatMap(right)
}
func >>> <A, B> (left: Signal<A>, right: A -> B) -> Signal<B> {
return left.flatMap(right)
}
it's possible to use:
address >>> Address.create >>> Address.verify
which is even more clear. But, what if the verify is defined as (accepting id instead of dictionary):
func verify2(id: String, completion: Result<[String: String]> -> Void) { ... }
in order to chain it, I defined another func:
static func idValue(dictionary: JSONDictionary, completion: Result<String> -> Void) { ... )
which basically unpacks value of id key from the dictionary (transforms dictionary to string) than I can use:
address >>> Address.create >>> Address.idValue >>> Address.verify2
Is this common practice in FRP? Can I avoid the step in the middle?
great to hear you're using this library. And yes - that's the way it's supposed to work. Here are some small ideas how to make it a bit easier to read:
flatMap
is way more readable in complex cases)Address.create
returns an Address
-struct, the next call can just use the id
-property of it. Using json as arguments almost never works as soon as the communication gets complex.Lens
as Chris Eidhof explains in Lenses in Swift.