/websocket-actor-system

A client/server actor system for Swift distributed actors

Primary LanguageSwiftApache License 2.0Apache-2.0

WebSocketActors

Swift Compatibility Platform Compatibility

macOS tests Ubuntu tests

WebSocketActors is a client/server communications library that allows an iOS, macOS, tvOS, or watchOS app to communicate with a server on the internet using Swift's distributed actor system. It's a streamlined alternative to writing a server using Swagger/OpenAPI and then implementing your client app on top of that API. With WebSocketActors, you can make Swift function calls directly between your client and server.

This library is based on Apple's TicTacFish sample code, but adds features like:

  • Simultaneous connections to multiple servers & clients
  • Automatic reconnection after network failures
  • Server calls to client actors (server push)
  • Logging with SwiftLog

Installation

Add the package https://github.com/samalone/websocket-actor-system to your Xcode project, or add:

   .package(url: "https://github.com/samalone/websocket-actor-system.git", from: "1.0.0"),

to your package dependencies in your Package.swift file. Then add:

   .product(name: "WebSocketActors", package: "websocket-actor-system"),

to the target dependencies of your package target.

Quick start

In your shared library, import WebSocketActors and define your distributed actors.

import Distributed
import WebSocketActors

extension NodeIdentity {
   public static let server = NodeIdentity(id: "server")
}

extension ActorIdentity {
   public static let greeter = ActorIdentity(id: "greeter", node: .server)
}

public distributed actor Greeter {
   public typealias ActorSystem = WebSocketActorSystem

   public distributed func greet(name: String) -> String {
      return "Hello, \(name)!"
   }
}

In your server code, start the server in the background and make sure the server keeps running.

func main() async throws {
   let address = ServerAddress(scheme: .insecure, host: "localhost", port: 8888)
   let system = WebSocketActorSystem(id: .server)
   try await system.runServer(at: address)

   _ = system.makeLocalActor(id: .greeter) {
      Greeter(actorSystem: system)
   }

   while true {
      try await Task.sleep(for: .seconds(1_000_000))
   }
}

On the client, connect to the server and call the remote actor.

func receiveGreeting() async throws {
   let address = ServerAddress(scheme: .insecure, host: "localhost", port: 8888)
   let system = WebSocketActorSystem()
   try await system.connectClient(to: address)

   let greeter = try Greeter.resolve(id: .greeter, using: system)
   let greeting = try await greeter.greet(name: "Alice")
   print(greeting)
}

Documentation

The documentation for WebSocketActors includes both API documentation and getting-started articles:

Sample Code

For a basic example of writing a client/server app using WebSocketActors, see my Monotonic project. It implements a simple iOS app that displays a counter above an "Increment" button. Clicking the increment button bumps the counters on all client apps connected to the server in real time.

Additional reading