Why use the responseWith method? - swift

In the process of reading the RXAlamofire source code, there is a place that I don't understand very well.
Since this method is an observable object for creating a DataRequest, why call the responseWith method?
func request<R: RxAlamofireRequest>(_ createRequest: #escaping (SessionManager) throws -> R) -> Observable<R> {
return Observable.create { observer -> Disposable in
let request: R
do {
request = try createRequest(self.base)
observer.on(.next(request))
request.responseWith(completionHandler: { response in
if let error = response.error {
observer.on(.error(error))
} else {
observer.on(.completed)
}
})
if !self.base.startRequestsImmediately {
request.resume()
}
return Disposables.create {
request.cancel()
}
} catch {
observer.on(.error(error))
return Disposables.create()
}
}
}

I believe the authors of RXAlamofire use this as their convention. If you look at there request implementation All of the request methods return the result of a method responseXYZ. The response methods typically execute the request and respond with something (JSON, String, etc.) Sounds a bit confusing but its kind of like this request some data respond with something.

Related

Run multiple request at a time and continue as soon there is success

Given an array of urls, is there a way to run those at once simultaneously? But in such a way so as soon as there is one success move to the next request using that successful url.
So far I tried chaining concatMap, and failed with zip.
func updateAccountInfo() -> Single<Bool> {
var disposable: Disposable? = nil
return Single<Bool>.create { observer in
do {
var urls = try self.settings.domains.value()
disposable = Observable.from(urls)
.enumerated()
.concatMap { index, url -> Single<URL> in
return self.verifyUrl(url)
}
.concatMap { url -> Single<Account> in
return self.apiManager.loadAccountInfo(from: url)
}
.observeOn(MainScheduler.instance)
.do(onNext: { (account: AccountInfo) in
// use account unfo here
disposable!.dispose()
})
.subscribe()
} catch {
observer(.error(error))
}
return Disposables.create()
}
}
Tried like so too:
disposable = Observable.from(urls)
.enumerated()
.concatMap { index, url -> Single<(Bool, URL)> in
return self.verifyUrl(url)
}
.subscribe(onNext: { reachable, url in
if reachable {
self.apiManager.loadAccountInfo(from: url)
.subscribe(onSuccess: { accountInfo in
// use account info here
}, onError: { error in
})
.disposed(by: self.bag)
disposable!.dispose()
} else {
}
}, onError: { error in
}, onCompleted: {
})
Maybe I use zip but how would I create an array of verifyUrl(url) calls? Does zip accept arrays of Observable at all?
let obs = Observable.from(urls)
.enumerated()
.concatMap { index, url -> Single<URL> in
return self.verifyUrl(url)
}
let test = Observable
.zip(obs).map { [urls] in
return [urls]
}
If I understand the question correctly, you are looking for something like this:
func example() throws {
let urls = try self.settings.domains.value()
Observable.merge(urls.map { verifyUrl($0).asObservable() })
.flatMap { [apiManager] url in
apiManager!.loadAccountInfo(from: url)
}
.observe(on: MainScheduler.instance)
.subscribe(onNext: { account in
// use account unfo here
})
.disposed(by: self.disposeBag)
}
But it's hard to tell. Your code samples are a bit jumbled. Putting all your code in a Single.create is odd. Returning a Disposables.create() from the closure when you have a disposable to return is odd. Calling dispose() on a disposable inside the do block is odd. So much weirdness... I suggest you post some code on https://codereview.stackexchange.com or look at sample code.

How do I reverse a promise?

I'm using PromiseKit to handle flow through a process.
Prior, I did a similar app without promises but decided frick it I'm gonna try promises just because, well, why not?
So I'm throwing a back button in the mix as I did in the prior app. Only problem is, I'm not exactly sure how to handle "reversing" if you want to call it that.
So say I have a flow of
doSomething().then {
// do something else
}.then {
// do something else
}.done {
// wrap it up, boss
}.catch {
// you're an idiot, bud
}
Say I'm in the first or second part of the chain then and I want to go back up the chain - is this possible?
Is there a link y'all can give me that I can use to read up on how to do that?
I'm thinking I might have to restart the "chain", but then how would I step through the flow....WAIT (light bulb), I can programmatically fulfill the necessary promises with whatever the data is that initially was fulfilled with until I get to the point in the "chain" where I needed to go back to, right?
Advice D:?
You can always have a catch and a then on the same promise.
var somePromise = doSomething()
// first chain
somePromise.catch { error in
// handle error
}
// second chain from the same starting point
somePromise.then {
// do something else
}.then {
// do something else
}.catch {
// you can still catch the error here too
}
You're basically creating two promise chains from the same original promise.
No, you can not do that. Once you commit a promise, you can not reverse that. Because the chain is supposed to finish in the descending order, it's cumbersome to track the order in each .then block.
What you can do is, handle the internal logic responsible to fulfill or reject a promise and start the chain from the beginning.
func executeChain() {
doSomething().then {
// do something else
}.then {
// do something else
}.done {
// condition to
executeChain()
}.catch {
// you're an idiot, bud
}
}
func doSomething() -> Promise<SomeThing>{
if (condition to bypass for reversing) {
return .value(something)
}
// Normal execution
}
But if you can improve your question with an actual use case and code then it could help providing more suitable explanation.
No you can't but you can set order in array.
bar(promises: [foo1(), foo2(), foo3()])
func bar<T>(promises: [Promise<T>]) {
when(fulfilled: promises)
.done { _ in
// TODO
}
.catch { error in
// When get error reverse array and call it again
self.bar(promises: promises.reversed())
}
}
func foo1() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
func foo2() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
func foo3() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
or alternatively
bar(foo1, foo2, foo3)
.done { _ in
// TODO
}
.catch { error in
print(error.localizedDescription)
self.bar(self.foo3, self.foo2, self.foo1)
.done { _ in
// TODO
}
.catch { error2 in
print(error2.localizedDescription)
}
}
func bar<T>(_ promise1: () -> Promise<T>,
_ promise2: #escaping () -> Promise<T>,
_ promise3: #escaping () -> Promise<T>) -> Promise<T> {
return Promise { seal in
promise1()
.then { _ in return promise2() }
.then { _ in return promise3() }
.done { model in
seal.fulfill(model)
}
.catch {
seal.reject($0)
}
}
}
func foo1() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
func foo2() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}
func foo3() -> Promise<Void> {
return Promise { $0.fulfill(()) }
}

How do you sequentially chain observables in concise and readable way

Im new to RXSwift and I've begun investigating how I can perform Promise like function chaining.
I think I'm on the right track by using flatmap but my implementation is very difficult to read so I suspect theres a better way to accomplish it.
What I have here seems to work but I'm getting a headache thinking about what It might looks like if I added another 3 or functions to the chain.
Here Is where I declare my 'promise chain'(hard to read)
LOGIN().flatMap{ (stuff) -> Observable<Int> in
return API(webSiteData: stuff).flatMap
{ (username) -> Observable<ProfileResult> in
return accessProfile(userDisplayName: username) }
}.subscribe(onNext: { event in
print("The Chain Completed")
print(event)
}, onError:{ error in
print("An error in the chain occurred")
})
These are the 3 sample functions I'm chaining
struct apicreds
{
let websocket:String
let token:String
}
typealias APIResult = String
typealias ProfileResult = Int
// FUNCTION 1
func LOGIN() -> Observable<apicreds> {
return Observable.create { observer in
print("IN LOGIn")
observer.onNext(apicreds(websocket: "the web socket", token: "the token"))
observer.on(.completed)
return Disposables.create()
}
}
// FUNCTION 2
func API(webSiteData: apicreds) -> Observable<APIResult> {
return Observable.create { observer in
print("IN API")
print (webSiteData)
// observer.onError(myerror.anError)
observer.on(.next("This is the user name")) // assiging "1" just as an example, you may ignore
observer.on(.completed)
return Disposables.create()
}
}
//FUNCTION 3
func accessProfile(userDisplayName:String) -> Observable<ProfileResult>
{
return Observable.create { observer in
// Place your second server access code
print("IN Profile")
print (userDisplayName)
observer.on(.next(200)) // 200 response from profile call
observer.on(.completed)
return Disposables.create()
}
}
This is a very common problem we run into while chaining operations. As a beginner I had written similar code using RxSwift in my projects as well. And there are two areas of improvement -
1. Refactor the code to remove nested flatMaps
2. Format it differently to make the sequence easier to follow
LOGIN()
.flatMap{ (stuff) -> Observable<APIResult> in
return API(webSiteData: stuff)
}.flatMap{ (username) -> Observable<ProfileResult> in
return accessProfile(userDisplayName: username)
}.subscribe(onNext: { event in
print("The Chain Completed")
print(event)
}, onError:{ error in
print("An error in the chain occurred")
})
In addition to nested flatMap and code formatting, you could omit return and explicit return types:
LOGIN()
.flatMap { webSiteData in API(webSiteData: webSiteData) }
parameter names
LOGIN()
.flatMap { API(webSiteData: $0) }
or even remove parameters at all where appropriate:
LOGIN()
.flatMap(API)
.flatMap(accessProfile)
.subscribe(
onNext: { event in
print(event)
}, onError:{ error in
print(error)
}
)
FYI there is Observable.just method which would be convenient here:
struct ApiCredentials {
let websocket: String
let token: String
}
func observeCredentials() -> Observable<ApiCredentials> {
let credentials = ApiCredentials(websocket: "the web socket", token: "the token")
return Observable.just(credentials)
}
Try to follow official Swift API Guidelines to make your code more readable.
You can also use the point-free style and just pass function references to flatMap:
LOGIN()
.flatMap(API)
.flatMap(accessProfile)
.subscribe(onNext: { event in
print("The Chain Completed")
print(event)
}, onError:{ error in
print("An error in the chain occurred")
})

PromiseKit firstly around code, not function call

I don't want to write a separate function to return a Promise in my firstly call. I just want to write this:
firstly
{
return Promise<Bool>
{ inSeal in
var isOrderHistory = false
let importTester = CSVImporter<String>(url: url)
importTester?.startImportingRecords(structure:
{ (inFieldNames) in
if inFieldNames[2] == "Payment Instrument Type"
{
isOrderHistory = true
}
}, recordMapper: { (inRecords) -> String in
return "" // Don't care
}).onFinish
{ (inItems) in
inSeal.resolve(isOrderHistory)
}
}
}
.then
{ inIsOrderHistory in
if inIsOrderHistory -> Void
{
}
else
{
...
But I'm getting something wrong. ImportMainWindowController.swift:51:5: Ambiguous reference to member 'firstly(execute:)'
None of the example code or docs seems to cover this (what I thought was a) basic use case. In the code above, the CSVImporter operates on a background queue and calls the methods asynchronously (although in order).
I can't figure out what the full type specification should be for Promise or firstly, or what.
According to my understanding, since you are using then in the promise chain, it is also meant to return a promise and hence you are getting this error. If you intend not to return promise from your next step, you can directly use done after firstly.
Use below chain if you want to return Promise from then
firstly {
Promise<Bool> { seal in
print("hello")
seal.fulfill(true)
}
}.then { (response) in
Promise<Bool> { seal in
print(response)
seal.fulfill(true)
}
}.done { _ in
print("done")
}.catch { (error) in
print(error)
}
If you do not want to return Promise from then, you can use chain like below.
firstly {
Promise<Bool> { seal in
print("hello")
seal.fulfill(true)
}
}.done { _ in
print("done")
}.catch { (error) in
print(error)
}
I hope it helped.
Updated:
In case you do not want to return anything and then mandates to return a Promise, you can return Promise<Void> like below.
firstly {
Promise<Bool> { seal in
print("hello")
seal.fulfill(true)
}
}.then { (response) -> Promise<Void> in
print(response)
return Promise()
}.done { _ in
print("done")
}.catch { (error) in
print(error)
}

RXAlamofire not returning data ( error or not)

This is my non-reactive code that works just fine.
func getLatestHtml2 () {
Alamofire.request("https://www.everfest.com/fest300").responseString { response in
print("\(response.result.isSuccess)")
if let html = response.result.value {
self.parseHTML(html: html)
}
}
}
However when I make it reactive using this code.
func getLatestHtml1() -> Observable<String> {
return Observable<String>.create { (observer) -> Disposable in
let request = Alamofire
.request("https://www.everfest.com/fest300")
.responseString { response in
print(response.result.value)
observer.onNext(response.result.value!)
observer.onCompleted()
}
return Disposables.create { request.cancel() }
}
}
I get no data in the print statement. I even used RxAlamofire, which I feel is the right way with this code and it has error checking:
func getLatestHtml() -> Observable<String?> {
return RxAlamofire
.requestData(.get,"https://web.archive.org/web/20170429080421/https://www.everfest.com/fest300" )
.debug()
.catchError { error in
print(error)
return Observable.never()
}
.map { (response, value) in
print(response.statusCode)
guard response.statusCode == 200 else { return nil }
print(value)
return String(data: value, encoding: String.Encoding.utf8)
}
.asObservable()
}
which produced no data or errors anywhere. I need to know if my syntax is wrong or my thinking regarding reactive programming is wrong.
I cam calling it as .getLatestHTMLX(). Thanks !
Observable's are lazy, they don't do any work unless they are being watched (and will generally stop working as soon as nobody is watching.) This means you have to subscribe to an observable in order for it to start emitting values.
Also, unless you explicitly share the observable, it will start a new request for every subscriber.