PromiseKit firstly around code, not function call - swift

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)
}

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 to call recursive function with Promise Kit?

I am stuck with somewhere to call same function again in promise and because of calling multiple time it's deallocate promise. Actually in my case I have API with multiple page request and I want to call it with promise. I was implemented it as below.
func fetchContacts() -> Promise<FPGetContactResponse?> {
return Promise { seal in
let contactrequest = FPGetContactRequest()
contactrequest.pageNo = getAPICurrentPageNo(Api.API_CONTACTS) + 1
contactrequest.pageSize = SMALL_PAGE_SIZE
contactrequest.doGetContacts(parameter: [:], response: { (response) in
print("Contacts Count : \(response.Contacts?.count ?? 0)")
if(response.Contacts?.count ?? 0 != 0){
_ = self.fetchContacts()
}else{
seal.fulfill(response)
}
})
{ (error) in
print(error.localizedDescription)
seal.reject(error)
}
}
}
In above function I check for contact count != 0 then I need to call same function again. But unfortunately it's deallocate promise.
I call promise sequence like below.
func startSyncData(handler:#escaping SyncAPIHandler){
firstly {
self.fetchContacts().ensure {
handler(false,0.5,nil)
}
}.then { data in
self.fetchInteractions().ensure {
handler(false,0.7,nil)
}
}.then { data in
self.fetchAddresses().ensure {
handler(false,0.8,nil)
}
}.then { data in
self.fetchLookupQuery().ensure {
}
}
.done { contacts -> Void in
//Do something with the JSON info
print("Contacts Done")
handler(true,0.8,nil)
}
.catch(policy: .allErrors) { error in
print(error.localizedDescription)
}
}
Please provide me the right way to call same function again in promise.
Instead of using recursion you should return a response within your promise and check it inside next .then and call fetchContacts again if it's needed:
fetchContacts()
.then { response -> Promise<FPGetContactResponse> in
if (response.Contacts?.count ?? 0 != 0) {
return fetchContacts() // Make the second call
}
return .value(response) // Return fullfilled promise
}
.then {
...
}
Also you can make a special wrapper for your case using the next approach - https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#retry--polling
I implemented things with following solution.
func syncContacts() -> Promise<FPGetContactResponse?> {
return fetchContacts().then{ seal -> Promise<FPGetContactResponse?> in
if(seal?.Contacts?.count ?? 0 != 0){
return self.syncContacts()
}else{
return Promise.value(seal)
}
}
}
Now just call syncContacts() method in promise sequence, like below.
func startSyncData(handler:#escaping SyncAPIHandler){
firstly {
self.syncContacts().ensure {
handler(false,0.5,nil)
}
}.then { data in
self.syncInterections().ensure {
handler(false,0.7,nil)
}
}.then { data in
self.syncAddresses().ensure {
handler(false,0.8,nil)
}
}.then { data in
self.syncLookupQuery().ensure {
}
}
.done { contacts -> Void in
//Do something with the JSON info
print("Contacts Done")
handler(true,0.8,nil)
}
.catch(policy: .allErrors) { error in
print(error.localizedDescription)
}
}

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(()) }
}

Why use the responseWith method?

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.

PromiseKit wrapping external closure in Promises

I am using an external library in Swift so I cannot control the return statements. My understanding is that I should wrap these returns in promises in order to use PromiseKit. Is this correct?
Assuming so, I have working code as follows:
private func getChannelImage(for channel: TCHChannel, completion: #escaping (UIImage?, CAProfileError?) -> Void) {
if let members = channel.members {
members.members(completion: { (result, paginator) in
if result.isSuccessful() {
// ... do something
}
else {
completion(nil, CAProfileError.UnknownError)
}
})
}
}
This can be difficult to read. I am trying to simplify this using PromiseKit. First, I want to simplify members.members(completion: { (result, paginator) in to a promise that I can call with the firstly { ... } syntax. I thus try and do as follows:
private func asPromise(members: TCHMembers) -> Promise<TCHMemberPaginator> {
return Promise<TCHMemberPaginator> { fulfill, reject in
members.members(completion: { (result, paginator) in
if result.isSuccesful() {
fulfill(paginator)
} else {
reject()
}
})
}
}
But this approach does not work and I get "Unable to infer closure type in the current context". I'm trying to find a good example of this use case done online but am having trouble. Any thoughts on how to properly return promises?
Assuming the TCHMemberPaginator and TCHMembers as below,
class TCHMemberPaginator {}
class TCHMembers {
func members(completion: (Bool, TCHMemberPaginator?) -> Void) {}
}
Here is the method to return a Promise,
private func asPromise(members: TCHMembers) -> Promise<TCHMemberPaginator> {
return Promise { seal in
members.members(completion: { (result, paginator) in
if result == true, let p = paginator {
seal.fulfill(p)
} else {
seal.reject(NSError())
}
})
}
}