Swift-erlang-actor-system: The Actor Model Gets a Swift Makeover

Swift-erlang-actor-system: The Actor Model Gets a Swift Makeover

Introduction

The actor model—a foundation of reliable, massively concurrent services—has long thrived in Erlang and Elixir. Now, with swift-erlang-actor-system, the richness of Erlang’s actor system is available directly in Swift, unleashing new potential for distributed programming and interoperability. This system bridges the gap, allowing Swift programs to join an Erlang cluster, exchange messages, and participate as first-class nodes in distributed computations.
This post explains how it works, where it shines, and how you can start using it today.

What is the Swift-Erlang-Actor-System?

At its core, swift-erlang-actor-system is a runtime for Swift’s new distributed actors, which uses Erlang’s erl_interface C library to connect natively to Erlang, Elixir, and other BEAM languages. Swift processes (actors) appear as nodes inside an Erlang cluster, and can communicate bi-directionally with other BEAM or C-node participants.
The system wraps Erlang’s C node API, enabling Swift's distributed actors to send and receive messages, participate in process registries, and implement robust communication patterns found in scalable, fault-tolerant systems.

How Does It Work?

  • Each Swift node creates an ErlangActorSystem instance with a unique name and cookie (for secure node authentication).
  • The actor system connects to other nodes via the Erlang Port Mapper Daemon (epmd), or directly by IP and port.
  • Distributed actor instances are created in Swift and registered on the system, optionally with stable ("well-known") names.
  • Actors exchange messages using Erlang's External Term Format, with serialization handled by Swift-side encoders/decoders compatible with the BEAM ecosystem.
  • Swift methods marked with @StableName macros can be invoked from the Erlang side by their unmangled names.

Sample Workflow: Connecting Swift and Elixir

  1. Install Elixir and start epmd.
  2. Start an Elixir node:
    iex --sname elixir_node
  3. Get the authentication cookie:
    iex(elixir_node@YOUR_HOSTNAME)> Node.get_cookie()
  4. In Swift, set up your ErlangActorSystem with the same cookie:
import ErlangActorSystem
import Distributed

@StableNames
distributed actor Counter {
    typealias ActorSystem = ErlangActorSystem
    private(set) var count: Int = 0

    @StableName("increment")
    distributed func increment() {
        count += 1
        print(count)
    }

    @StableName("decrement")
    distributed func decrement() {
        count -= 1
        print(count)
    }
}

let actorSystem = try await ErlangActorSystem(name: "swift_node", cookie: "YOUR_COOKIE")
try await actorSystem.connect(to: "elixir_node@YOUR_HOSTNAME")
let counter = Counter(actorSystem: actorSystem)
actorSystem.register(counter, name: "counter")
while true {}

Next, you can send messages to the Swift actor from Elixir using familiar GenServer semantics:

iex(elixir_node@YOUR_HOSTNAME)> GenServer.cast({:counter, :"swift_node@YOUR_HOSTNAME"}, :increment)
iex(elixir_node@YOUR_HOSTNAME)> GenServer.cast({:counter, :"swift_node@YOUR_HOSTNAME"}, :decrement)

Key Features & Design

  • Interoperability: Swift actors can interact seamlessly with BEAM actors (in Elixir/Erlang), opening hybrid system designs where each language is used to its strengths.
  • Stable Names: The @StableNames and @StableName macros allow exporting Swift methods with human-friendly names for remote invocation, avoiding Swift-specific name mangling pains.
  • External Actor Protocols: For BEAM actors defined only on the Erlang side, you can create Swift protocols (@Resolvable + HasStableNames) that let Swift code talk to remote GenServers as if they were native distributed actors.
  • Flexible Networking: Swap out transports (e.g., WebSockets instead of TCP), as the system is modular under the hood.
  • Message Serialization: Full compatibility with Erlang External Term Format, enabling arbitrary Codable values to travel between worlds.

When and Why Use It?

  • Bring Swift’s safety and modern syntax to distributed systems that need to interact with BEAM-based services.
  • Gradually migrate or extend legacy Erlang/Elixir clusters with new Swift microservices.
  • Prototype actor-based concurrency patterns in Swift, then orchestrate or scale them in classic Erlang clusters.

Common Use Cases

  • Real-time multiplayer games needing both performance (Swift) and reliability (Erlang supervisor trees).
  • Telecom or messaging clouds already running BEAM services, but also requiring rich client agents implemented in Swift.
  • Experimental distributed applications leveraging actor models beyond the current Swift runtime’s reach.

Caveats and Limitations

  • Complexity: Distributed actor systems require careful design around message formats, error handling, and process supervision.
  • Performance tuning: Serialization and network latency must be considered for very high-throughput or low-latency use cases.
  • Language differences: Not all constructs map perfectly between Swift and BEAM; protocol and macro design can help, but diligence is needed.

How to Get Started

  1. Install required dependencies: Elixir/Erlang on your cluster, Swift on your development machine.
  2. Add the otp-interop/swift-erlang-actor-system package to your Swift project.
  3. Follow the official README or the overview above to set up your actor system, connect nodes, and try out distributed actors.

Conclusion

The swift-erlang-actor-system is a major step towards language-agnostic, actor-based distributed systems. For teams looking to harness the best of Swift and Erlang/Elixir together, it’s an exciting new foundation to explore, with huge possibilities in reliability, concurrency, and seamless integration.

References

Post a Comment

0 Comments