Swift Actor-Model
3 min readFeb 16, 2021
Flynn Actor-Model architecture
Foreword:
I really like the actor-model architecture. And I plan to implement it in App. In near time, I saw this libraries make me exciting:
https://github.com/KittyMac/flynn
So let us use this to make a fast、safely、efficiently App
Installation
Refer to https://github.com/KittyMac/flynn
Build first Actor:
import Flynn
class HelloFlynnActor: Actor {
private func _beSayHello() {
print("Hello Flynn")
}
}
// MARK: - Autogenerated by FlynnLint
// Contents of file after this marker will be overwritten as needed
extension HelloFlynnActor {
@discardableResult
public func beSayHello() -> Self {
unsafeSend(_beSayHello)
return self
}
}
remember use keyword:_be and FlynnLint can auto generate extention after building project
Say hello to Flynn:
HelloFlynnActor().beSayHello()
Concurrent Data store:
import Flynn
protocol ConcurrentDatastoreRetrievable {
@discardableResult
func beRetrieveValue(_ key: String, _ value: Any?) -> Self
}
class ConcurrentDatastore: Actor {
private var storage: [String: Any?] = [:]
private func _beGet(_ key: String, _ sender: ConcurrentDatastoreRetrievable) {
if let value = storage[key] {
sender.beRetrieveValue(key, value)
}
}
private func _beGet(_ key: String, _ sender: Actor, _ block: @escaping (Any?) -> Void) {
if let value = storage[key] {
sender.unsafeSend {
block(value)
}
}
}
private func _beSet(_ key: String, _ value: Any?) {
storage[key] = value
}
}
// MARK: - Autogenerated by FlynnLint
// Contents of file after this marker will be overwritten as needed
extension ConcurrentDatastore {
@discardableResult
public func beGet(_ key: String, _ sender: ConcurrentDatastoreRetrievable) -> Self {
unsafeSend { self._beGet(key, sender) }
return self
}
@discardableResult
public func beGet(_ key: String, _ sender: Actor, _ block: @escaping (Any?) -> Void) -> Self {
unsafeSend { self._beGet(key, sender, block) }
return self
}
@discardableResult
public func beSet(_ key: String, _ value: Any?) -> Self {
unsafeSend { self._beSet(key, value) }
return self
}
}
Scenario actors:
class Scenario1: Actor,ConcurrentDatastoreRetrievable {
private let monsters: ConcurrentDatastore
init(_ monsters: ConcurrentDatastore) {
self.monsters = monsters
super.init()
}
private func _beRetrieveValue(_ key: String, _ value: Any?) {
if let value = value {
print("\(value)")
}
}
private func _bePrint(_ name: String) {
monsters.beGet(name, self)
}
}
class Scenario2: Actor {
private let monsters: ConcurrentDatastore
init(_ monsters: ConcurrentDatastore) {
self.monsters = monsters
super.init()
}
private func _bePrint(_ name: String) {
monsters.beGet(name, self) { value in
if let value = value {
print("\(value)")
}
}
}
}
How to use it:
let monsters = ConcurrentDatastore()
monsters.beSet("Goblin", Monster(name: "Goblin", hitpoints: 10, strength: 3, dexterity: 2, intelligence: 1))
monsters.beSet("Orc", Monster(name: "Orc", hitpoints: 15, strength: 6, dexterity: 4, intelligence: 2))
monsters.beSet("Troll", Monster(name: "Troll", hitpoints: 30, strength: 4, dexterity: 1, intelligence: 5))
let scenario1 = Scenario1(monsters)
let scenario2 = Scenario2(monsters)
print("-------Show-------")
scenario1.bePrint("Goblin")
scenario1.bePrint("Orc")
scenario1.bePrint("Troll")
sleep(1)
print("--------------")
scenario2.bePrint("Troll")
scenario2.bePrint("Orc")
scenario2.bePrint("Goblin")
View Model Actor:
class ViewModelActor: Actor,ObservableObject {
private var coutTextBlock: ((String) -> Void)?
private var count: Int = 0 {
willSet { objectWillChange() }
}
private var unsafeCount: String {
set {
if let value = Int(newValue) {
beSetCount(value)
}
}
get { return "\(count)" }
}
private func objectWillChange() {
DispatchQueue.main.async {
self.objectWillChange.send()
}
} private func _beSetCount(_ value: Int) {
count = value
}
private func _beAddCountTextBlock(_ block: @escaping (String?) -> Void) {
coutTextBlock = block
}
override init() {
super.init()
Flynn.Timer(timeInterval: 0.5, repeats: true, self) { (_) in
self.count += 1
if self.coutTextBlock != nil {
DispatchQueue.main.async {
self.coutTextBlock!("\(self.count)")
}
}
}
}
}
Use it:
model.beAddCountTextBlock { (title) in
self.button.setTitle(title, for: .normal)
It’s a simple sample, next time I’ll try RemoteActor
My practice project clone url: https://github.com/Cart00nHero/FlynnActorModel_iOS.git