/Aojet

An actor model library for swift.

Primary LanguageSwiftMIT LicenseMIT

Aojet

GitHub license GitHub release Carthage compatible

Aojet is an actor model implemetion for swift.

Features

  • Asynchronous, non-blocking and highly performant message-driven programming model
  • Safe as well as efficient messaging
  • Message ordering using Local Synchronization Constraints
  • Fair scheduling
  • Modular and extensible
  • A promise implementation for general usage
  • Portable(Support iOS and Mac platform currently)

Requirements

  • Swift 3.0
  • iOS 8.0+ or macOS 10.10+

Installation

Aojet is available through Carthage. Add this line to your Cartfile

github "aojet/Aojet"

Usage

Make an Actor

This is a simple actor implementation:

class SomeActor: Actor {
  override func onReceive(message: Any) throws {
    switch message {
    case let m as DoSomething:
      doSomething(object: m.object)
    default:
      try super.onReceive(message: message)
    }
  }

  func doSomething(object: Any) { //This should run on the actor thread.
    print(Thread.current)
    print("Do something with object: \(object)")
    //Do something
  }

  struct DoSomething {
    let object: Any
  }

}

Create ActorRef

let actorSystem = ActorSystem.system
actorSystem.traceInterface = ActorTrace() //For internal logging
let actor = try actorSystem.actorOf(path: "testActor", creator: AnyActorCreator{ () -> Actor in
  return SomeActor()
})

Send Message to ActorRef

actor.send(message: SomeActor.DoSomething(object: "An object")) //Success
actor.send(message: "An string") //Drop
actor.send(message: SomeActor.DoSomething(object: "Another object")) //Success

Make an AskableActor

class SomeActor: AskableActor {

  override func onAsk(message: Any) throws -> Promise<Any>? {
    switch message {
    case let m as AskSomething:
      return askSomething(object: m.object)
    default:
      let p = try super.onAsk(message: message)
      print("Promise: \(p)")
      return p
    }
  }

  override func onReceive(message: Any) throws {
    switch message {
    case let m as DoSomething:
      doSomething(object: m.object)
    default:
      try super.onReceive(message: message)
    }
  }

  func doSomething(object: Any) { //This should run on the actor thread.
    print(Thread.current)
    print("Do something with object: \(object)")
    //Do something
  }

  func askSomething(object: Any) -> Promise<Any> { //This should run on the actor thread.
    print(Thread.current)
    print("Ask something with object: \(object)")

    return Promise(value: "A response")
  }

  struct DoSomething {
    let object: Any
  }
  
  struct AskSomething {
    let object: Any
  }
}

Make an Ask Request

 let p1: Promise<String> = actor.ask(message: SomeActor.AskSomething(object: "An object for ask"))
 p1.then { (res) in
   print("Ask response:\(res)")
 }.failure { (error) in
   print("Ask error:\(error)")
 }
  

Promise Usage

There are some ways to create a promise:

//Define an error for test
enum TestError: Error {
  case general(message: String)
}

//Immediate Promise
let p1 = Promise(value: 1)
let p2 = Promise<Int>(error: TestError.general(message: "Test error."))

//Async Promise
let p3 = Promise<String> { (resolver) in
  let url = URL(string: "https://api.ipify.org")
  let task = URLSession.shared.dataTask(with: url!) {(data, response, error) in
    if error != nil {
      resolver.error(error!)
    } else if data != nil {
      let s = String(bytes: data!, encoding: String.Encoding.utf8)
      print(s)
      resolver.result(s)
    } else {
      resolver.result(nil)
    }
  }
  task.resume()
}