I have a function set up to return a Promise<PFObject>. I would like to use this function in PromiseKit's when(fulfilled:) functionality, but whenever I try to do so, I get an error. Here is the function which returns the Promise<PFObject>:
func Query() -> Promise<PFObject>{
return Promise{ fulfill, reject in
let linkQueryy = PFUser.query()
linkQueryy?.findObjectsInBackground(block: { (objectss, error) in
if let objects = objectss{
for object in objects{
fulfill(object)
}
}
})
}
}
As you can see, the function returns the Promise upon fulfillment. Thus, I tried to set up a when statement in my viewDidLoad() as follows:
override func viewDidLoad() {
super.viewDidLoad()
when(fulfilled: Query()).then{
//do more asynch stuff
}
}
However, I get the error that xcode cannot "invoke 'when' with an argument list type of '(fulfilled: Promise<PFObject>)'". I do not know how to fix this as I thought I had it set up correctly. The when needs a promise, and I am giving it one so I am not sure what to do.
Try as follows :
when(fulfilled: [linkQueryy()] as! [Promise<Any>]).then { _ in
// do more asynch stuff
}
The parameter fulfilled: needs to be an iterable.
By the way, when(fulfilled:) is necessary only when you have many promises and need wait for all to complete successfully. But in your code, you need to wait for only one promise.
For a single promise, the better way is to form a chain as follows :
firstly {
linkQueryy()
}.then { _ -> Void in
// do more asynch stuff
}.catch { _ in
// error!
}
Related
I am programming an app which utilizes a parse-server (hosted by heroku) database. I have several functions which pull information from the DB, but they are all inherently asynchronous (because of the way parse's .findObjectinBackground works.) The issue with this as that the later DB queries require information from previous queries. Since the information being pulled is asynchronous, I decided to implement PromiseKit to ensure that the object has been found from findObjectinBackground from the first query, before running the second query.
The general form of the queries is as follows:
let query = PFQuery(classname: "Hello")
query?.findObjectsInBackground(block: { (objects, error) in
if let objectss = objects{
for object in objectss{ //object needs to be pulled
arrayOfInterest.append(object)
//array must be appended before moving on to next query
}
}
})
I just do not know how exactly to do this. This is the way I would like to implement it:
import PromiseKit
override func viewDidLoad(){
when(/*query object is retrieved/array is appended*/).then{
//perform the next query
}
}
I simply don't know exactly what to put in the when() and the .then{}. I tried making the queries into their own individual functions and calling them inside those two (when and then) functions, but I basically get told that I cannot because they return void. Also, I cannot simply ensure the first query is run in the when() as the query.findObjectinBackground(in the query) being asynchronous is the issue. The object specifically needs to be pulled, not just the query run, before the next one can fire.
Do you want create your promise?
You need write a function that return a Promise<Any>. In your case, need to encapsulate the entire code inside of Promise { fulfill, reject in HERE}. For example:
func foo(className: String) -> Promise<[TypeOfArrayOfInterest]> {
return Promise { fulfill, reject in
let query = PFQuery(classname: className)
query?.findObjectsInBackground(block: { (objects, error) in
if let error = error {
reject(error) // call reject when some error happened
return
}
if let objectss = objects {
for object in objectss{
arrayOfInterest.append(object)
}
fulfill(arrayOfInterest) // call fulfill with some value
}
})
}
}
Then, you call this function in firstly:
firstly {
foo(className: "Hello")
}.then { myArrayOfInterest -> Void in
// do thing with myArrayOfInterest
}.catch { error in
// some error happened, and the reject was called!
}
Also, I wrote a post in my blog about, among other things, PromiseKit and architecture. It may be helpful: http://macalogs.com.br/ios/rails/ifce/2017/01/01/experiencias-eventbee.html
Edit
More complete example:
func foo() -> Promise<Int> {
...
}
func bar(someText: String) -> Promise<String> {
...
}
func baz() -> Promise<Void> {
...
}
func runPromises() {
firstly {
foo()
}.then { value -> Promise<Any> in
if value == 0 {
return bar(someText: "no")
} else {
return bar(someText: "yes")
}
}.then { _ /* I don't want a String! */ -> Promise<Void> in
baz()
}.catch { error in
// some error happened, and the reject was called!
}
}
Or if you don't want a catch:
_ = firstly {
foo()
}.then { _ in
// do some thing
}
Swift have a greate type inference, but, when use PromiseKit, I recommend always write a type in then closure, to avoid erros.
I have to fetch three types of data (AType, BType, CType) using three separate API requests. The objects returned by the APIs are related by one-to-many:
1 AType object is parent of N BType objects
1 BType object is parent of P CType objects)
I'm using the following three functions to fetch each type:
func get_A_objects() -> Observable<AType> { /* code here */ }
func get_B_objects(a_parentid:Int) -> Observable<BType> { /* code here */}
func get_C_objects(b_parentid:Int) -> Observable<CType> { /* code here */}
and to avoid nested subscriptions, these three functions are chained using flatMap:
func getAll() -> Observable<CType> {
return self.get_A_objects()
.flatMap { (aa:AType) in return get_B_objects(aa.id) }
.flatMap { (bb:BType) in return get_C_objects(bb.id) }
}
func setup() {
self.getAll().subscribeNext { _ in
print ("One more item fetched")
}
}
The above code works fine, when there are M objects of AType, I could see the text "One more item fetched" printed MxNxP times.
I'd like to setup the getAll() function to deliver status updates throughout the chain using ReplaySubject<String>. My initial thought is to write something like:
func getAll() -> ReplaySubject<String> {
let msg = ReplaySubject<String>.createUnbounded()
self.get_A_objects().doOnNext { aobj in msg.onNext ("Fetching A \(aobj)") }
.flatMap { (aa:AType) in
return get_B_objects(aa.id).doOnNext { bobj in msg.onNext ("Fetching B \(bobj)") }
}
.flatMap { (bb:BType) in
return get_C_objects(bb.id).doOnNext { cobj in msg.onNext ("Fetching C \(cobj)") }
}
return msg
}
but this attempt failed, i.e., the following print() does not print anything.
getAll().subscribeNext {
print ($0)
}
How should I rewrite my logic?
Problem
It's because you're not retaining your Disposables, so they're being deallocated immediately, and thus do nothing.
In getAll, you create an Observable<AType> via get_A_objects(), yet it is not added to a DisposeBag. When it goes out of scope (at the end of the func), it will be deallocated. So { aobj in msg.onNext ("Fetching A \(aobj)") } will never happen (or at least isn't likely to, if it's async).
Also, you aren't retaining the ReplaySubject<String> returned from getAll().subscribeNext either. So for the same reason, this would also be a deal-breaker.
Solution
Since you want two Observables: one for the actual final results (Observable<CType>), and one for the progress status (ReplaySubject<String>), you should return both from your getAll() function, so that both can be "owned", and their lifetime managed.
func getAll() -> (Observable<CType>, ReplaySubject<String>) {
let progress = ReplaySubject<String>.createUnbounded()
let results = self.get_A_objects()......
return (results, progress)
}
let (results, progress) = getAll()
progress
.subscribeNext {
print ($0)
}
.addDisposableTo(disposeBag)
results
.subscribeNext {
print ($0)
}
.addDisposableTo(disposeBag)
Some notes:
You shouldn't need to use createUnbounded, which could be dangerous if you aren't careful.
You probably don't really want to use ReplaySubject at all, since it would be a lie to say that you're "fetching" something later if someone subscribes after, and gets an old progress status message. Consider using PublishSubject.
If you follow the above recommendation, then you just need to make sure that you subscribe to progress before results to be sure that you don't miss any progress status messages, since the output won't be buffered anymore.
Also, just my opinion, but I would re-word "Fetching X Y" to something else, since you aren't "fetching", but you have already "fetched" it.
In Swift, if I'm inside of a closure, that is itself inside of another function, is there a way to exit out of the function itself?
Here's an example of what this might look like using closures from the GCDKit library.
func test() {
GCDQueue.Default.async {
print("Print me!")
return //Is there a statement that does this?
}.notify(.Main) {
print("Never print me.")
}
}
No there is not. Closures run in self-contained environments. For all you know, by the time that closure is executed, the thread on which test() was called is no longer executing the test() method.
Let's consider a simpler version that doesn't include any third-party libraries, extra queues, or other complexity. We'll just create a closure and immediately execute it.
func dothing(andPrint shouldPrint: Bool) {
let closure = {
guard shouldPrint else { return }
print("I printed!")
}
closure()
print("did it return?")
}
dothing(andPrint: false) // Prints "did it return?"
The return here exits the closure, not dothing. Since closure could be passed to some other function, or stored in a property and executed at some later time (possibly on a different queue as in your example), there's no way for the return to exit anything beyond itself. Consider if we were to refactor the creation of the closure into its own function:
func fetchClosure(andPrint shouldPrint: Bool) -> () -> Void {
return {
guard shouldPrint else { return }
print("I printed!")
}
}
func dothing(andPrint shouldPrint: Bool) {
let closure = fetchClosure(andPrint: shouldPrint)
closure()
print("did it return?")
}
dothing(andPrint: false) // Prints "did it return?"
It shouldn't be surprising this has the same behavior (otherwise this wouldn't be a simple refactor). Now imagine how it would (or even could) work if return behaved any other way.
Your example is just a much more complicated version of the same thing. return exits the closure.
/* Checks Is Username Already Exists
Take - username
Return - Bool (True/False) */
func checkIsUserExists(username: String, completion: ((isUser: Bool?) -> Void)!) {
var isPresent: Bool = false;
let query: PFQuery = PFQuery(className: "your_class_name")
query.whereKey("your_key", equalTo:username)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if (objects!.count > 0) {
isPresent = true;
}
} else {
// Log details of the failure
println("Error: \(error) \(error!.userInfo!)")
}
completion(isUser: isPresent);
}
}
I found this function method that checks if the user exists in parse database but I dont get it what is the proper way to call it?
checkIsUserExists("user#gm.com, comletion.. ?)
It requires a closure that acts as a callback. So you'd call it in the following way:
checkIsUserExists("User Name"){isUser in if let user = isUser where user {
userFound()
}
else {
userNotFound()
}
}
Assuming you also had the following methods in place:
func userFound() {
// do something if the user was found
}
func userNotFound() {
// do something if the user was not found
}
The important keyword is in. Before the keyword, within the curly braces, I've named the value that is being captured isUser. It is a Bool as we can see from the method. And the closure is not required to return anything: (isUser: Bool?) -> Void)! hence the word Void. After the in keyword we can use the captured value to do whatever we wish with the optional true or false value. Here I call one of two functions.
In terms of code it is a very poor callback because while we know whether the user exists by the time the callback happens there might be several callbacks waiting in line and so we won't know which user exists because the user name is not passed to the closure.
You can find out plenty about closures in the Swift documentation.
got it!
The correct way is
checkIsUserExists(userEmail.text!)
{
(isUser: Bool?) in
print(isUser)
}
WOW! what a weird function call
I first tried this solution to return a bool in the spot I want to return it. However, due to the parse.com function saveInBackgroundWithBlock() being a void return function, I got the error "Unexpected non-void return value in void function".
func saveObjectToParse(gameLocal: Game) -> Bool {
let game = PFObject(className:"Game")
game["sport"] = gameLocal.sport.rawValue
var saved = false
game.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
print("Object has been saved.")
saved = true
return saved
} else {
print("parse error")
return saved
}
}
}
So, I tried moving the return statements out of the subfunction like this:
func saveObjectToParse(gameLocal: Game) -> Bool {
let game = PFObject(className:"Game")
game["sport"] = gameLocal.sport.rawValue
var saved = false
game.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
print("Object has been saved.")
saved = true
} else {
print("parse error")
}
}
return saved
}
However, this returns saved before the saveInBackgroundWithBlock() block executes because it is a background process. Therefore, saved will never be true, even when it is intended to be. I have tried adding a boolean flag called done and tried waiting with a while(!done) loop, but this freezes the program on the loop and the background process never executes. How can I fix these problems?
I agree with restructuring not needing a bool returned, but if you really, really need this set up, you could save your object synchronously (so your code will wait) like so,
do {
try game.save()
} catch {
print(error)
}
Returning a value from a function but from another function doesn't make architectural sense. Nor is it possible.
You either will need to change your implementation and make both methods independent or think of using a semaphore.
http://www.g8production.com/post/76942348764/wait-for-blocks-execution-using-a-dispatch
What you are trying to do (create a helper function to wrap the Parse save function) makes perfect sense and can be easily accomplished.
You do not need to use semaphores and you certainly don't want to perform the operation synchronously. Instead, use a completion hander to let you know when the save has completed. For more information on completion handlers see this link
func saveObjectToParse(gameLocal: Game, completion: (gameSaved: Bool) -> Void) {
let game = PFObject(className:"Game")
game["sport"] = gameLocal.sport.rawValue
game.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
// Set the completion handler to be result of the Parse save operation
completion(gameSaved: success)
}
}
You may then call this function like so
saveObjectToParse(someGameObject) { (gameSaved: Bool) in
if gameSaved {
print("The game has been saved.")
} else {
print("Error while saving the game")
}
}
Using this technique, you could similarly propagate the entire callback of saveInBackgroundWithBlock through your function so you could inspect errors when they occur.
Edit: It looks like you may also be using your own custom class to represent the Game object. I would recommend looking into subclassing PFObject so you can easily and directly model your Parse classes. More details in the documentation