Swifttor is a library which allows you to use the actors paradimng from the popular akka library.
- All the messages are being reicived on parallel.
- Uses structs as actor so is easy to paralellize.
- Works in all platforms.
- Includes the ask and tell actions.
- Can compose the ask calls
In the Example folder we have an example project which is using swifttor.
I've created 3 different actors
struct NetworkActor: ActorAsk {
typealias ResultType = Result<Data,String>
typealias MessageType = URL
func reiciveAsk(message: URL,completion:@escaping (Result<Data,String>)->()) {
perfomNetWorkCall(url: message, completion: completion)
}
func perfomNetWorkCall(url:URL,completion:@escaping (Result<Data,String>)->()){
let session = URLSession(configuration: .ephemeral)
session.dataTask(with: url, completionHandler: { (data, response, err) in
if let dat = data{
completion(Result.success(dat))
}else{
completion(Result.failure("error happend"))
}
}).resume()
}
}
struct ParserActor: ActorAsk {
typealias ResultType = Result<Model,String>
typealias MessageType = Result<Data,String>
func reiciveAsk(message: Result<Data,String>, completion: @escaping (Result<Model, String>) -> ()) {
let result = message.fmap { (data) -> Result <Model,String> in
if let result = try? JSONDecoder().decode(Model.self, from: data){
return Result.success(result)
}
return Result.failure("Cannot parse")
}
completion(result)
}
}
struct MainActor: ActorTell {
var queue: DispatchQueue {
return DispatchQueue.main
}
typealias MessageType = MainActorMessages
func reiciveTell(message: MainActorMessages) {
switch message {
case .refreshStuffWithData(let callback,let data):
callback(data)
case .refreshStuff(let callback):
callback()
case .displayInfo(let callback,let data):
callback(data)
}
}
}
And this are the enums used for messaging between them
enum MainActorMessages {
case displayInfo(callback:(Model) -> (),data:Model)
case refreshStuffWithData(callback:(String) -> (),data:String)
case refreshStuff(callbac:() -> ())
}
enum Result<A,B>{
case success(A)
case failure(B)
}
extension Result {
func map<C>(_ transform: (A) -> C) -> Result<C,B> {
switch self {
case .success(let value): return .success(transform(value))
case .failure(let error): return .failure(error)
}
}
func fmap<C>(_ transform: (A) -> Result<C,B>) -> Result<C,B> {
switch self {
case .success(let value):
return transform(value)
case .failure(let error):
return .failure(error)
}
}
}
And now you are able to do this
let actorRef = ActorSystem.actorOf(actorType: MainActor.self)
let networkActor = ActorSystem.actorOf(actorType: NetworkActor.self)
let parserActor = ActorSystem.actorOf(actorType: ParserActor.self)
let actorChain = networkActor.ask(URL(string: "https://httpbin.org/ip")!) >>> parserActor.ask
actorChain.onResult { (result) in
switch result {
case .success(let model):
actorRef.tell(MainActorMessages.displayInfo(callback: { print($0) }, data: model))
case .failure(let err):
actorRef.tell(MainActorMessages.refreshStuffWithData(callback: { print($0) }, data: err))
}
}
Create a new actor
struct MainActor:ActorAsk,ActorTell {
/* Optional if you don't specify creates a new one*/
var queue: DispatchQueue {
return DispatchQueue.main
}
typealias MessageType = String
typealias ResulType = String
func reiciveAsk(message: String,completion:@escaping(String)->Void) {
completion(message)
}
func reiciveTell(message: String) {
print(message)
}
}
Inicialization
/*If we use this we are saving the actor in a cache*/
let actor1 = ActorSystem.actorOf(actorType: MainActor.self)
/*We are just craeting the actor without saving anywhere*/
let main = MainActor()
let actorRef = ActorSystem.actorOfInstance(main)
Compose calls
let actor1 = ActorSystem.actorOf(actorType: MainActor.self)
let actor2 = ActorSystem.actorOf(actorType: MainActor.self)
let result = actor1 !! "Hello" >>> actor2.ask
result.onResult(callback:{
print($0)
})
Using one of this depency managers:
- Carthage
- Cocoapods
- SwiftPM
- Deadletter
- Mailbox
Apache 2.0