Swift-chat: Showcase for distributed application

Work in Progress

swift-chat is a simple distributed application created to explore and showcase distributed actors.

After the announcement of distributed actors, I was excited and began to dive deeper into the topic. Unfortunately, couldn't find any examples that covered it comprehensively. However, this hasn't stopped me from learning. I started reading books about Akka and Erlang and created a small repository where porting Akka in action examples, but quickly realised that there were still some missing pieces.

:top: So, to fill those gaps, I've created a small example with several goals:

  1. To reduce the learning curve for new developers.
  2. To cover, at least partially, some concepts like event sourcing.

While it's based on Swift distributed actors, the project also incorporates:

As a learning project, it could also serve as a helpful example for other developers.

Currently, the app is in an early stage, though basic functionalities are working:

  1. You can spin several nodes and form a cluster, such as:
    • main, which orchestrates other actors and connections.
    • room, to spawn room actors.
    • database, for persistency and event sourcing. You can even play around and split it into two separate nodes.
  2. Basic fault tolerance, in case of room and database unavailability (time out, crash)—the main node can continue working and spawn all needed actors on its own.
  3. A basic iOS/macOS app client with chat, user/room creation, and room search.
22 Likes

Would be nice to hear some feedback. Want to be this example as actor model correct as possible, and as I'm only learning and don't have much of experience in writing concurrent and distributed apps using actor model—would be nice to hear some thoughts from people who have this experience.

Also would like to mention next steps I want to cover:

  • Debugging is hard. Like really hard sometimes, even on a single node (cluster can just crash :man_shrugging:). Wanna add swift distributed tracing
  • @Honza_Dvorsky pointed out that it would be nice to add swift openapi generator, which is a good point. Will try!
  • Maybe adding some simple authentication an spin application somewhere on my raspberry pi4 or VPS and have some more insights :slight_smile:
  • Add some basic node discovery.
3 Likes

Also, where I have 0 experience is event sourcing and consistent persistency across multiple nodes.
So far checking this articles:

Would be nice if someone can provide more knowledge, at least links to the articles they know/liked.

1 Like

This is a really awesome effort and I’ll try to make time soon to dive in! Thanks a lot for driving this! :star_struck:

3 Likes

Yeah, thx! It's fun! :upside_down_face: Looking forward for next steps.

Think event sourcing is a good candidate to be extracted into separate package sometime in (distant) future.

Another idea I have in mind—would be interesting to discover and try to create Phoenix LiveView-like framework out of websocket connection example.

2 Likes

Yes, event-sourcing would be a nice standalone package! There's various approaches but the overall idea should be quite abstractible, especially with macros and the flexibility you get from actorReady so you can store it and register it for "when events arrive we'll call you" :slight_smile:

I'm personally a huge fan of doing something like LiveView, that'd be really cool :slight_smile:

1 Like

Yeah, would be good. :blush:

(don't be excited much though, my current state of event sourcing is simple event logging :sweat_smile:)

Seen some interest in the community before, and maybe interesting for someone, decided to pack some stuff into Virtual Actor pattern, so here is some basic and naive implementation:

Idea is that you can have transparent actors in the system referenced by some custom id, e.g.

let actor = try await factory.get(id: "first_example", dependency: SomeDependency())

Would be nice to work and improve it further. :slightly_smiling_face:

1 Like

Just wanted to share a bit on progress of my small distributed actors showcase demo:

  • Improved a bit error and crash handling (Let it crash™️) to self recover the service if something happens.
  • Removed some stuff, like new swift foundation, for simplicity. And overall some parts of code look way better now.
  • First of all, already posted in different topic, but now we can do simple event sourcing. You can think about event sourcing as autosaves in games—whatever bad happens you can jump to last saved state. The way it works is you just add plugin to the system, conform desired actors to EventSourced protocol and update the logic to handle events. :sparkles:
  • Added Swift openapi generator.
  • Removed websockets. This is an interesting point, basically while moving to openapi @Honza_Dvorsky suggested that you can also remove ws and use streams. And this was suprising for me, as I've always thought that things like SSE works one way. But apparently you can do bidirectional streaming by initialising streams both way. Sounds obvious now, but :man_shrugging: And as I think it's not quite discoverable—ended up creating a PR with an example. Thx again @Honza_Dvorsky and @beaumont for input and help.

After all the updates really like the state of the code now—quite simple, but you have fully working chat example with db, event sourcing, recovery and crash handling. Will try to improve it further, some still missing (like postgresql journal for event sourcing), but overall think next steps would be finally to play with infrastructure, like real device (raspberry) or VPS. This will include deploying, node discoverability and etc.

Hope there will be a nice demo soon, with documentation and tutorial. :slightly_smiling_face:

6 Likes

This is how I am finding out about Swift distributed actors. This is super cool!

1 Like

Can you expand more on your thoughts of building something like LiveView? I was just playing around with LiveView and was thinking something like that but built in swift would be amazing.

1 Like

Thx, glad it makes someone curios! :slightly_smiling_face:

Technically it should be doable: have an actor with state on server side, update state and only send diffs to client to render with SwiftUI. Converting state to SwiftUI views would be hardest part though, there is LiveView native initiative, and if you check—it takes a lot of effort to port it SwiftUI and Compose.

2 Likes

Yea, super cool idea. I think even if you did something like just handled the state on the server (similar to this GitHub - hansihe/live_data) and all the UI was still just controlled on client based on the state being updated on the server would be good.

Sort of like a redux pattern but going client<>server.

2 Likes

This looks awesome I need to give it a try!

2 Likes

Thx! Constantly improving it, so looking forward for any feedback! :slightly_smiling_face:

1 Like

It's been a long time since I have posted here, and actually I continue to develop repo from time to time and improving it with small steps.

But recent changes to Discord, and the way how our Swift community is scattered across different platforms, lead me to think—maybe I should finish it in some form and let people actually communicate there? Sure, it's hard to compete with Slack and Discord, and there are lots of other open source solutions‚ but in the end it's also about trying to make something better, while keeping some fun in the process. Another point to work on it is that previous work pushed me to continue developing Cluster system before, so going further will eventually help finding some edge cases and help Migrating Cluster system to Swift structured concurrency.

So, what's the current state?

  • Chat is actually working. :blush:
  • While working on it I've also abstracted and created Event sourcing plugin—basically for persistence, and Virtual actors—where system guarantees one actor across nodes. When combining virtual actor + event sourcing—you have it's always there guarantees if something happens, e.g. node dies with some chat in it—it should quickly recover with latest state on other node.
  • There are some simple iOS, macOS and web clients (based on GitHub - elementary-swift/elementary-htmx: HTMX + Elementary: Hypertext web apps with Swift).
  • It has some basic login + jwt functionality, but needs to be improved.

What should be next steps for basic MVP?

  • Finishing UI to have list of channels where people can join, chat updates also should be streamed (new message and etc.)
  • Some small stuff here and there, e.g. finishing login functionality, or handling some error and recovery edge cases.
  • Proper node discoverability—while Cluster System should work with any, I need to check how this works with k8s or some other simpler orchestration.

Of course this will be a very simple chat, but looking at amount of code—it's actually interesting to see what could be achieved in terms of scalability and reliability with such little. Current plan is to deploy it to some VPSs from multiple providers + my Raspberry Pi at home to monitor how this would work and look like.

After that other features that could also be included, are:

  • Secure 1 to 1 chats, or overall security customisation for chats.
  • Threads inside chats.
  • Push notifications.
  • Video calls for community hours—never worked with WebRTC, would be nice to check, but not sure when.
  • ...

and other features to make this chat a proper place to communicate.


Now the most interesting, but also hard part—what would make this chat unique and competitive? Here I have some quick ideas, could be non feasible, but why not dreaming, right?

  • Having an ability for anyone to deploy it, including adding nodes for anyone to existing chats—to increase scalability and also keeping chat really part of communities.
  • Adding nodes could also work for extending chat possibility—could imagine one would want to add some API or MCP agents on the fly.
  • Maybe not related to chat, but would be nice to have some framework to wrap MCP agents and even run custom LLMs on dedicated devices, that could be easy accessible afterwards. Plus adding some WebAssembly on top to run those actors in a better isolation.
  • Also not quite related to chat itself, but again—this is a showcase for Swift technologies, wanna create some durable workflows on top of virtual actors + event sourcing, like writing in chat /plan Notify @some @another in 90 days which will send notifications in 90 days and this will be reliable if something happens/crashes in-between.
  • While Slack only gives you an option to keep a history for 90 days, and on other hand Discord keeps if forever, which is quite a problem if you have limited disk space—having an option to either keep history and scale, or ability to reduce history log and snapshot to keep memory size strict. Also then some documentation + search functionality could be implemented for that.

I know this is a ton of work to be done, considering also I'm trying to improve Cluster System in parallel, and I don't know where and when it will land. But:

  1. As I said earlier working on some real cases helps understand what should be done better.
  2. One of the Erlang/Elixir advantages that lot's of developers notice and describe is that you can do complicated things with little effort and human resources. Would be nice to prove that it's also a case for distributed actors in Swift.

I will most likely post updates to Swift Open Source Slack (before publishing a proper chat :face_with_tongue:) and maybe some bigger things here. As always if you have any thoughts, concerns, or objections, please let me know—I’d really appreciate the feedback.

10 Likes

Don't have timers yet, but this week made a small presentation at our Swift community here in Munich, it was about showcasing distributed actors with examples of what you can build. One of them was durable workflows. Sorry @FranzBusch, stole swift-temporal-sdk API :upside_down_face: but considering it's just a research project—happy with the result.

5 Likes