Awesome
Ax is a library written in Swift that helps you to control the flow of asynchronous executions in a organized way. Inspired by async library. ##Overview
##Requirements
- Xcode 8.0+
- Swift 3.0+
- iOS 9.0+
##What’s It For?
###The Problem
For example there could be a case when your asynchronous calls depend each other to run so a naive solution could be nesting your calls like in the example below:
runAsync(afterSeconds: 2, completion: {
let dataFromTask1 = 1
self.runAsync(afterSeconds: 2, completion: {
let dataFromTask2 = 2
self.runAsync(afterSeconds: 2, completion: {
let dataFromTask3 = 3
self.runAsync(afterSeconds: 2, completion: {
let dataFromTask4 = 4
print(dataFromTask1) // 1
print(dataFromTask2) // 2
print(dataFromTask3) // 3
print(dataFromTask4) // 4
})
})
})
})
Nesting your asynchronous calls is a common known problem in programming that the community names it as: callback hell, pyramid of doom.
We should avoid this kind of code because it can lead to some really confusing and difficult-to-read code, it is a bad practice.
The Solution
That is when it comes in play Ax, it helps you to call your async calls in a linear way giving the impression that you were running synchronous calls:
import Ax
var dataFromTask1 = 0
var dataFromTask2 = 0
var dataFromTask3 = 0
var dataFromTask4 = 0
Ax.serial(
tasks: [
{ done in
self.runAsync(afterSeconds: 2) {
dataFromTask1 = 1
done(nil)
}
},
{ done in
self.runAsync(afterSeconds: 2) {
dataFromTask2 = 2
done(nil)
}
},
{ done in
self.runAsync(afterSeconds: 2) {
dataFromTask3 = 3
done(nil)
}
},
{ done in
self.runAsync(afterSeconds: 2) {
dataFromTask4 = 4
done(nil)
}
}
],
result: { error in // feedback closure
print(dataFromTask1) // outputs 1
print(dataFromTask2) // outputs 2
print(dataFromTask3) // outputs 3
print(dataFromTask4) // outputs 4
}
)
###Important aspects to mention are:
- The variable
done
is a closure that accepts anNSError?
value, whendone
is called with anil
that means that the task was run successfully and in other hand ifdone
is called with aNSError
value then all subsequents tasks are ignored and then immediately theresult
closure is executed with theerror
passed to thedone
variable. - The closures in
tasks
andresult
are run inDispatchQoS.QoSClass.background
mode, it is up to you if you, for example, want to call the result in the main thread.
##Supported Functions
Initially the supported functions are:
- Serial
- Parallel
###Serial This function help you to make asynchronous calls in a sequence way, running them in an orderly fashion where the first call is run and after this is finished, the next call is run and so on.
Example:
import Ax
var authorId = ""
var authorBooks = [Book]()
Ax.serial(
tasks: [
{ done in
self.getAuthorBy(name: "J. K. Rowling") { error, author in
guard let author = author else {
done(NSError(domain: "AppDomain", code: 434, userInfo: [NSLocalizedDescriptionKey: "didn't get author"]))
return
}
authorId = author.id
done(error)
}
},
{ done in
self.getBooksBy(authorId: authorId, completion: { (error, books: [Book]) in
authorBooks = books
done(error)
})
}
],
result: { error in
if let error = error {
print(error)
return
}
print(authorBooks) // [Book(name: "Harry Potter and the Philosopher\'s Stone"), Book(name: "Harry Potter and the Chamber of Secrets")]
}
)
###Parallel Helps you to make asynchronous calls in a parallel way, this function will help you when you have a number of fixed calls that you need to perform but it doesn't matter if they are run all of them at the same time.
Example:
import Ax
let userId = "1"
let profileImageURL = "https://unsplash.it/100"
var userFound: User?
var userImage: UIImage?
Ax.parallel(
tasks: [
{ done in
self.getUserBy(id: userId) { error, user in
guard let user = user else {
done(NSError(domain: "AppDomain", code: 434, userInfo: [NSLocalizedDescriptionKey: "No user found."]))
return
}
userFound = user
done(error)
}
},
{ done in
self.getProfileImageBy(url: profileImageURL) { error, image in
guard let image = image else {
done(NSError(domain: "AppDomain", code: 435, userInfo: [NSLocalizedDescriptionKey: "Image not found."]))
return
}
userImage = image
done(error)
}
}
],
result: { error in
if let error = error {
print(error)
return
}
print(userFound!) // User(name: "Walter While")
print(userImage!) // <UIImage: 0x618000095a90>, {100, 100}
}
)
Installation
Ax is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod "Ax"
Usage
Once installed just import it in your file that you are working:
import Ax
Author
Wilson Balderrama, wilson.balderrama@gmail.com
License
Ax is available under the MIT license. See the LICENSE file for more info.