How to map Promise<T> to Guarantee<Bool>? - ios

I have a Promise<T> that I would like to transform into a Guarantee<Bool> where true means the promise fulfilled and false if it got rejected.
I managed to reach this using
return getPromise()
.map { _ in true }
.recover { _ in Guarantee.value(false) }
I'm wondering if there is a neater way to do this.

Expanding on the original code and the answers here, I would make the extension explicitly for Void Promises, and keep the naming more inline with PromiseKit:
extension Promise where T == Void {
func asGuarantee() -> Guarantee<Bool> {
self.map { true }.recover { _ in .value(false) }
}
}

You can extend promise as below for easy usage mentioned under Usage
extension Promise {
func guarantee() -> Guarantee<Bool> {
return Guarantee<Bool>(resolver: { [weak self] (body) in
self?.done({ (result) in
body(true)
}).catch({ (error) in
body(false)
})
})
}
}
Usage:
// If you want to execute a single promise and care about success only.
getPromise().guarantee().done { response in
// Promise success handling here.
}
// For chaining multiple promises
getPromise().guarantee().then { bool -> Promise<Int> in
return .value(20)
}.then { integer -> Promise<String> in
return .value("Kamran")
}.done { name in
print(name)
}.catch { e in
print(e)
}

Related

Combine output of a completion handler function with a function that returns AnyPublisher of combine

I have two functions, first function gets data from a combine function. It is called as following:
self.programStorageProvider.fetchHealthPrograms()
above function has following signature:
func fetchHealthPrograms() -> AnyPublisher<[HealthProgram], Error>
Now I want to get some data from another function which itself gets data from a completion handler using above returned array([HealthProgram]), something like following:
private func updateHealthProgramStageStatus(healthPrograms: [HealthProgram]) -> AnyPublisher<VoucherIDs, Error> {
Future { [weak self] promise in
self.fetchProgramsStages { result in
var updatedHealthPrograms = [HealthProgram]()
switch result {
case .success(let stagesList):
for stage in stagesList {
// Perform some operation
updatedHealthPrograms.append(program)
}
let voucherIDsResult = VoucherIDs(all: updatedHealthPrograms, valid: [""])
promise(.success(voucherIDsResult))
case .failure(let error):
promise(.failure(error))
}
}
}.eraseToAnyPublisher()
}
I want to use it like following:
public func getVoucherIDs() -> AnyPublisher<VoucherIDs, Error> {
return self
.programStorageProvider
.fetchHealthPrograms()
.map { programs in
var healthProgramsWithVoucherId = programs.filter { $0.voucherId != nil }
return self.updateHealthProgramStageStatus(healthPrograms: healthProgramsWithVoucherId)
}
.eraseToAnyPublisher()
}
But in return line in above function I am getting following error:
Cannot convert return expression of type 'AnyPublisher<VoucherIDs, any Error>' to return type 'VoucherIDs'
How can I resolve this?
You are missing switchToLatest() after your map in your getVoucherIDs() method.
public func getVoucherIDs() -> AnyPublisher<VoucherIDs, Error> {
return self
.programStorageProvider
.fetchHealthPrograms()
.map { programs in
var healthProgramsWithVoucherId = programs.filter { $0.voucherId != nil }
return self.updateHealthProgramStageStatus(healthPrograms: healthProgramsWithVoucherId)
}
.switchToLatest()
.eraseToAnyPublisher()
}

Use the 'asPromise ()' in the Observable RxSwift can be used in a PromiseKit Promise?

I write function 'asPromise()'
extension PrimitiveSequence {
public func asPromise() -> Promise<Element> {
var disposed: Disposable? = nil
return Promise<Element> { seal in
disposed = self.asObservable()
.subscribe { event in
switch event {
case .next(let element):
seal.fulfill(element)
case .error(let error):
seal.reject(error)
case .completed:
disposed?.dispose()
}
}
}
}}
but dispose is weird, I don't better idea.
so If you have any better ideas, please let me know
thank you
There is no need to capture the disposable at all. When the Observable completes it will automatically dispose of all the resources. Here is the proper implementation:
extension PrimitiveSequence where Trait == SingleTrait { // will only work properly for Singles
public func asPromise() -> Promise<Element> {
Promise<Element> { seal in
_ = subscribe { event in
switch event {
case let .success(element):
seal.fulfill(element)
case let .failure(error):
seal.reject(error)
}
}
}
}
}
Did you know that the Combine API already has a Promise like type built in?
extension PrimitiveSequence where Trait == SingleTrait {
public func asFuture() -> Future<Element, Error> {
Future { fulfill in
_ = self.subscribe(fulfill)
}
}
}

Synthax of firstly in PromiseKit 6

I would like to use this method from PromiseKit but dont know how to write propper synthax :x
public func firstly<U: Thenable>(execute body: () throws -> U) -> Promise<U.T> {
do {
let rp = Promise<U.T>(.pending)
try body().pipe(to: rp.box.seal)
return rp
} catch {
return Promise(error: error)
}
}
firstly {
return someMethodWhichReturnsPromise()
}.then{
}
...
How can I invoke this?
Code is from: https://github.com/mxcl/PromiseKit/blob/master/Sources/firstly.swift
A basic example of PromiseKit "firstly" usage:
func someMethodWhichReturnsPromise() -> Promise<Int> {
// return promise
}
firstly {
someMethodWhichReturnsPromise()
}.then {
// Execute more logic here after 'firstly' block completes
}.catch {
// handle error if you need to
handle(error: $0)
}

Chain sequence observables with arguments

I'm fairly new to RxSwift, so I have the following problem, lets suppose I have 3 Observable example functions which return different observable types:
func observableFunc1(item1: DummyObject) -> Observable<AVURLAsset> {
return Observable.create { observer in
let something_with_AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: "file"))
observer.onNext(something_with_AVURLAsset)
observer.onCompleted()
return Disposables.create()
}
}
func observableFunc2(item: AVURLAsset) -> Observable<Data> {
return Observable.create { observer in
let something_with_data = Data()
observer.onNext(something_with_data)
observer.onCompleted()
return Disposables.create()
}
}
func observableFunc3(_ params: [String:Any]) -> Observable<Any> {
let disposeBag = DisposeBag()
return Observable.create { observer in
RxAlamofire.request(api.sendData(params)).debug().subscribe(
onNext: { reponse in
observer.onCompleted()
},
onError: { error in
observer.onError(customError.theError("error"))
}
)
.addDisposableTo(disposeBag)
return Disposables.create()
}
}
How can I execute these 3 functions sequentially with the return value of func1 to be used on func2 and then when func2 is completed finally run func3.
I hope I made the question clear enough, but then again I'm really new to RxSwift and I don't know if these operations are possible or not.
Here's an example...
Assuming you have the three functions:
func func1() -> Observable<Data1>
func func2(_ data: Data1) -> Observable<Data2>
func func3(_ data: Data2) -> Observable<Data3>
Then you can:
let a = func1()
let b = a.flatMap { func2($0) }
let c = b.flatMap { func3($0) }
or:
let c = func1()
.flatMap { func2($0) }
.flatMap { func3($0) }
That said, your observableFunc3 is quite broken. You need to remove the dispose bag from it. As it stands, the network call will cancel before it starts.
If you really don't want it to emit any values then:
func observableFunc3(_ params: [String:Any]) -> Observable<Void> {
return RxAlamofire.request(api.sendData(params))
.filter { false }
}
The above will emit either a completed or an error but no next values.
Better would be to write it like:
func observableFunc3(_ params: [String:Any]) -> Observable<Void> {
RxAlamofire.request(api.sendData(params))
.map { _ in }
}
The above will emit one next and then a completed or an error. This is better because you can map or flatMap after it to have things happen once it's done.

Move function void to boolean Swift

I am rookie with Swift. I need to change the following method to a boolean function which will return true if connection is ok else false if something is wrong. Thanks
func test() {
var configuration = SessionConfiguration()
configuration.host = "ftp://ftp.mozilla.org:21"
configuration.username = "optimus"
configuration.password = "rollout"
configuration.encoding = NSUTF8StringEncoding
_session = Session(configuration: configuration)
_session.list("/") {
(resources, error) -> Void in
println("List directory with result:\n\(resources), error: \(error)\n\n")
}
}
Since the _session.list("/") have a callback, which is asynchronous, what you can do is this:
func test(_ completion: (Bool) -> Void) {
// ...
_session.list("/") { (resources, error) -> Void in
println("List directory with result:\n\(resources), error: \(error)\n\n")
guard error == nil else {
completion(false) // failed
return
}
completion(true) // succeed
}
}
And so you can call it this way:
test() { (hasSucceeded: Bool) -> Void in
if hasSucceeded {
// Do what you want
} else {
// Failed
}
}
Lots of people have told you what to do, but nobody's clearly explained why.
The function _session.list() is an async function. When you call it it returns immediately, before it has even begun executing. The code runs on a separate thread, and the closure you pass in gets called once the function is complete.
Thus, you can't have your function return a simple bool. Async programming doesn't work that way.
Instead, you need to refactor your test function to take a closure with a bool parameter, as outlined by several other answers. (#KevinHirsch's answer for example.) Then when you invoke your test function you put the code that checks to see if the test passed or failed into the closure that you pass to the test function.
Since, session.list is an async process, add a completion handler and call your function as shown:
typealias theBool = (Bool) -> ()
func test(completion: theBool) {
//your code
_session.list("/") { (resources, error) in
guard error == nil else {
completion(false)
return
}
//your code
completion(true)
}
}
And call it like so:
test { someVar in
if someVar {
//do stuff when it is true
}
else{
//do stuff if it is false
}
}
When sync code:
func test() -> Bool {
return true
}
// Use case below:
let succes = test()
Turn to async use closure:
func test(didTest didTest: (Bool)->Void) {
...
_session.list("/") { (resources, error) -> Void in
if error != nil {
didTest(false)
} else {
didTest(true)
}
}
}
// Use case below:
test(didTest: { result in
let succes = result
})
Wish this may help.
Simply do
func test() -> Bool {
...
_session.list("/") { (resources, error) -> Void in
println("List directory with result:\n\(resources), error: \(error)\n\n")
if error != nil {
return true
} else {
return false
}
}
}
Updated:
I guess you should use closure for this because session.list is an async process. as #Duncan C said in his answer _session.list() When you call it it returns immediately, before it has even begun executing. The code runs on a separate thread, and the closure you pass in gets called once the function is complete.
public typealias SuccessClosure = (data: String) -> (Void)
public typealias FailureClosure = (error: NSError?, data: NSData? = nil, statusCode: Int? = nil) -> (Void)
func test(success: SuccessClosure, failure: FailureClosure) {
...
_session.list("/") { (resources, error) -> Void in
println("List directory with result:\n\(resources), error: \(error)\n\n")
if error != nil {
success("success")
} else {
failure(error)
}
}
}

Resources