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)
}
}
}
Related
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()
}
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)
}
I want to execute code with dispatchqueue. But only if a certain condition is true. I'm doing this so far, but there has to be a less ugly way.
if !sync {
return self.tasksInQueue[userID]
}
return SYNC_QUEUE.sync {
return self.tasksInQueue[userID]
}
I'm not sure its any cleaner.. but you could define a variable to define the common job to do...
let task = { self.tasksInQueue[userID] }
if sync {
return SYNC_QUEUE.sync { task() }
} else {
return task()
}
If this type of if happens in a bunch of places, you could in theory create some generic function to do the check for you..
func optionalSync <T> (_ sync: Bool, task: () -> T) {
if sync {
return SYNC_QUEUE.sync { task() }
} else {
return task()
}
}
Then whenever you need to do an op...
optionalSync(sync) { self.tasksInQueue[userID] }
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)
}
I'm trying to create a completion block that can execute another function after its completed, in this case it's a tableview reload. I get the error :
'async' produces '()', not the expected contextual result type 'Bool'
This is the function:
func appendAllData (completion: () -> Bool) {
if self.movieDetailsData?.poster != nil {
if let posterImage = self.movieDetailsData?.poster {
self.posterArray.append(posterImage)
}
}
if self.movieDetailsData?.overview != nil {
if let overview = self.movieDetailsData?.overview {
self.overviewArray.append(overview)
}
}
if self.movieDetailsData?.releaseData != nil {
if let releaseDate = self.movieDetailsData?.releaseData {
self.releaseInfoArray.append(releaseDate)
}
}
if self.movieDetailsData?.runtime != nil {
if let runtime = self.movieDetailsData?.runtime {
self.releaseInfoArray.append(String(describing: runtime))
}
}
if self.movieDetailsData?.genre != nil {
if let genre = self.movieDetailsData?.genre {
if genre.isEmpty {
} else {
self.releaseInfoArray.append(genre[0].name)
}
}
}
if self.movieDetailsData?.budget != nil {
if let budget = self.movieDetailsData?.budget {
self.boxOfficeArray.append(budget)
}
}
if self.movieDetailsData?.revenue != nil {
if let revenue = self.movieDetailsData?.revenue {
self.boxOfficeArray.append(revenue)
}
}
if self.movieDetailsData?.homepage != nil {
if let homepage = self.movieDetailsData?.homepage {
self.homePageArray.append(homepage)
}
}
if self.movieDetailsData?.images != nil {
if let images = self.movieDetailsData?.images {
let posters = images.backdropImages
for poster in posters {
self.imageArray.append(poster.filePath)
}
}
}
}
This is how it's used:
self.appendAllData(completion: { _ in
DispatchQueue.main.async { //error here: 'async' produces '()', not the expected contextual result type 'Bool'
self.detailTableView.reloadData()
}
})
Your completion closure signature is completion: () -> Bool but you used () -> (). Just change function parameter from completion: () -> Bool to completion: () -> Void or completion: () -> ().
And you should follow njzk2's comment.