store an array of functions with closures - swift

My goal is to have an array of functions that execute one after the other.
static func GetData(success:(() ->())?, failure:((response : NSURLResponse, error : NSError) ->())?){
//do something asynchronous. make web service call
success?()
//or
failure?(response: urlResponse, error: errorThrown)
}
And then in a view controller, I'm trying to store functions similar to this one above in an array. My goal is to iterate through this array and execute the functions one after the other. The reason for this is because each function is dependent on the data that gets returned from the previous function.
So I made an enum
enum MyEnum {
case GetData()
case GetData2()...and so on
//and every other function is put here
}
Now in my view controller, I add this to an array
funcs.append(MyEnum.GetData(DataAccess.GetData({
//do stuff
}, failure: { (response, error) in
})))
funcs.append(MyEnum.GetData2(DataAccess.GetData2({
//do stuff
}, failure: { (response, error) in
})))
However, whenever I store a function, it automatically executes. I think I could do it if there weren't any closures but I need them to determine how to proceed.
Is there a better way of doing this? Am I missing something?
Storing an array of functions with closures automatically executes them
var arrayOfFuncs = [
DataAccess.GetData1({
print("success")
}, failure: { (response, error) in
print("failure")
}),
DataAccess.GetData2({
print("ok")
}, failure: { (response, error) in
print("ee")
})
]

GetData takes 2 closures as parameters, it's not two closures. I don't get what you are doing with the enum.
typealias Block = () -> Void
typealias Error = (NSURLResponse,NSError) -> Void
var funcs = [(Block,Error)]
func.forEach{ f => f._0() } // call success()
// Or you could do this
typealias Getter = (Block,Error) -> Void
var funcs2 = [Getter]
funcs2.append(DataAccess.GetData)

Related

Closures vs returning an value/object from a function

I'm currently learning Swift(4)/iOS programming. I'm fairly new to OOP, but do have experience in Functional programming. There's one concept that confuses me somewhat...
In the courses/examples I followed a function mostly looks like this:
func getUsername(forUid uid: String, handler: #escaping (_ username: String) -> Void) {
//Do stuff
handler("MyName")
}
The name is passed by the closure, I hardly ever see a function like this:
func getUsername(forUid uid: String) -> String) {
//Do stuff
return "MyName"
}
Is the second way deprecated, or is there still a use for such a function. When and why do we use the first variant ?
We use the first variant for asynchronous programming. For example, see
func getUsername(forUid uid: String, handler: #escaping (_ username: String) -> Void) {
DispatchQueue.main.async {
// do some asynchronous stuff
handler("MyName")
}
}
Note handler has to be inside the async closure or else the handler will be called immediately because async does not block. Now if we used return instead of handler, there will be a compile error because your function doesn't return any value, so to fix the compile error it has to be in the function level (not in the async block). If it's not in the async block, it will be returned immediately (same as the second handler case above, so you must use closures if you are doing asynchronous tasks. However, if you are not using asynchronous stuff, you can safely use the second variant in your code.
In addition to asynchronous programming, closures are used in synchronous programming too, for example, the map function uses a closure as a parameter to define how should the object be mapped.
Of course it's not deprecated.
You should use closures for async tasks otherwise return value.
It is easier to get it with an example. Here's I get some places from an API:
func getPlaces(onSuccess: #escaping(_ places: [Place]?) -> Void, onFailure: #escaping(Error, _ title: String?, _ message: String?) -> Void) {
//perform API request...
//[...]
//...
// Session
session.dataTask(with: requestURL) { (data, response, error) in
guard error == nil else {
//handling Error
onFailure(error!, "Error", error?.localizedDescription)
group.leave()
return
}
//...
//handling succes
else {
var places: [Place] = []
places = responseJson.places!
onSuccess(places)
}
group.leave()
}.resume()
}
group.wait()
return
}
As you can see I want to handle success and error. Here's how I use it:
APIManager.shared.getPlaces(onSuccess: { (places) in
//handling success
}
}) { (error, title, message) in
DispatchQueue.main.async {
self.present(getAlertFromError(title: title, message: message), animated: true)
}
}

How to set up when(fulfilled:) in PromiseKit?

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

How to utilize PromiseKit to ensure a queried object has been retrieved before proceeding?

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.

Confusing closures and completion handles

Im a new programmer and am very lost.
I am taking this online iOS dev course and I was configuring collection view cell.
However, closures and completion handles were used and it was never mentioned before.
import UIKit
class PersonCell: UICollectionViewCell {
#IBOutlet weak var img: UIImageView!
func configureCell(imgUrl: String) {
if let url = NSURL(string: imgUrl) {
downloadImg(url)
}
}
func downloadImg(url: NSURL) {
getDataFromURL(url) { (data, response, error) in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
guard let data = data where error == nil else {return}
self.img.image = UIImage(data: data)
}
}
}
func getDataFromURL(url: NSURL, completion: ((data: NSData?, response: NSURLResponse?, error: NSError?) -> Void)) {
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
completion(data: data, response: response, error: error)
} .resume()
}
}
Can someone explain to me what the completion handler is doing after the "getDataFromURL" function. Also what are the closures doing? is "(data, response, error)" getting passed around? How does swift know that "data" is suppose to be NSData and etc in the "(data, response, error)"?
What does the closure after the "dataTaskWithURL" do (is it setting up the completion handler"?
Thank you!
These are good questions!
A closure is simply a collection (aka block) of lines of code that you can treat like a variable and execute like a function. You can refer to a closure with a variable name and you can pass a closure around as a parameter in function calls just like any other variable, eventually executing the code when appropriate. A closure can accept certain parameters to use in its code and it can include a return value.
Example:
This is a closure that accepts two strings as parameters and returns a string.
let closure: (String, String) -> String = { (a: String, b: String) -> String in
return a + b
}
Thus, the following will print "Hello Jack!":
print(closure("Hello ", "Jack!"))
A closure also has a variable type (just like "hello" is a String and 1 is an Int). The variable type is based on the parameters that the closure accepts and the value that the closure returns. Thus, since the closure above accepts two strings as parameters and returns a string, its variable type is (String, String) -> String. Note: when nothing is returned (i.e. the return type is Void), you can omit the return type (so (Int, String) -> Void is the same thing as (Int, String)).
A completion handler is a closure that you can pass to certain functions. When the function completes, it executes the closure (e.g. when a view finished animating onto the screen, when a file finished downloading, etc.).
Example:
"Done!" will be printed when the view controller is finished presenting.
let newClosure: () -> Void = { () -> Void in
print("Done!")
}
let someViewController = UIViewController(nibName: nil, bundle: nil)
self.presentViewController(someViewController, animated: true, completion: newClosure)
Let's focus on the getDataFromURL function you wrote first. It takes two parameters: a variable of type NSData and a closure of type (NSData?, NSURLResponse?, NSError?) -> Void. Thus, the closure (which is named completion) takes three parameters of types NSData?, NSURLResponse?, and NSError?, and returns nothing, because this is how you defined the closure in the function declaration.
You then call getDataFromURL. If you read the documentation, you'll see that the closure you pass to this function as the second parameter is executed when the load task is complete. The function declaration for dataTaskWithURL is what defines the variable types that the closure accepts and returns. Within this closure, you are then calling the closure you passed to the getDataFromURL function.
Within this latter closure (the one you define in downloadImg when you are calling getDataFromURL), you are checking to see if the data that you downloaded is not nil, and if not, you are then setting the data as an image in a UIImageView. The dispatch_async(dispatch_get_main_queue(), ...) call simply ensures that you are setting the new image on the main thread, as per Apple's specifications (you can read more about threads elsewhere).
make an typealias to understand this is easy :
typealias Handle = (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void
//the func should be
func getDataFromURL(url: NSURL, completion: Handle)
//when you call it. it needs an url and an Handle
getDataFromURL(url:NSURL, completion: Handle)
// so we pass the url and handle to it
getDataFromURL(url) { (data, response, error) in
dispatch_async(dispatch_get_main_queue()) { () -> Void in
guard let data = data where error == nil else {return}
self.img.image = UIImage(data: data)
}
}
//setp into the func
func getDataFromURL(url: NSURL, completion: Handle){
// call async net work by pass url
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
// now data / response / error we have and we invoke the handle
completion(data: data, response: response, error: error)
} .resume()
}
hope it be helpful :D

How to create function with two closures as params in Swift?

I need to translate such a func from Objective-C to Swift language. But can't find an example and can't get how to send 2 closures into func in Swift.
For example, original function in Objective-C:
- (void)getForDemoDataWithToken:(Token *)token
onSuccess:(void(^)(NSArray *demoData))success
onFailure:(void(^)(NSError *error))failure {
}
I know to send 1 closure as param:
getForDemoDataWithToken(token) {(success: String) -> Void in
// some code here
print(success)
}
But, how to send two closures?
Thank you
What about this?
Declaration
func getForDemoDataWithToken(
token: Token,
onSuccess: (demoData:NSArray?) -> (),
onFailure: (error:NSError?) -> ()) {
}
Invocation
getForDemoDataWithToken(Token(),
onSuccess: { (demoData) -> () in
},
onFailure: { (demoData) -> () in
}
)
A more Swifty approach
I usually see Swift code where only one closure is used. So instead of 2 distinct onSuccess and onFailure closures you could have just completion.
Next we should remember that NSArray is compatible with Swift but it's not the Swiftest way to use an array.
Let's see an example where the 2 concepts above are applied.
func getForDemoData(token:Token, completion:(data:[Foo]?, error:NSError?) -> ()) {
}
You can invoke it with the trailing closure syntax.
getForDemoData(Token()) { (data, error) -> () in
if let data = data where error == nil {
// success!
} else {
// oh no... something wrong here
}
}
You should pass the closures as normal parameters, like this:
func acceptsTwoClosures(
onSuccess onSuccess: (success: String) -> Void,
onFailure: (failure: String) -> Void) {
onSuccess(success: "Ook")
onFailure(failure: "Eek")
}
acceptsTwoClosures(
onSuccess: { print("Success: \($0)") },
onFailure: { print("Failure: \($0)") }
)
// In the playground the above prints:
//
// Success: Ook
// Failure: Eek
The way that you used in the question is called trailing closure, and it only works for the closures that are the last arguments in the function signature.
From the documentation:
If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long, it can be useful to write it as a trailing closure instead. A trailing closure is a closure expression that is written outside of (and after) the parentheses of the function call it supports.
For example, you could also re-write my suggested snippet from above like this:
acceptsTwoClosures(onSuccess: { print("Success: \($0)") }) {
print("Failure: \($0)")
}
.. as you can see, I can pass the second (i.e. the last) closure outside of acceptsTwoClosures call as a trailing closure, but I still have to pass the first one as a normal parameter.