Skip to content

nerdsupremacist/Sync

Repository files navigation

Sync

Swift Package Manager Twitter: @nerdsupremacist

Sync

Sync is a proof of concept for expanding on the Magic of ObservableObject, and making it work over the network. This let's you create real-time Apps in which your Observable Objects are in sync across multiple devices. This has a lot of applications just as:

  • IoT Apps
  • Multi-Player Mini Games
  • etc.

As of right now Sync works out of the box using WebSockets, however, it's not limited to web sockets and it allows for multiple kinds of connections. Some possible connections could be:

  • Bluetooth
  • Multi-Peer
  • MQTT
  • etc.

The sky is the limit!

Warning: This is only a proof of concept that I'm using for experimentation. I assume there's lots and lots of bugs in there...

Installation

Swift Package Manager

You can install Sync via Swift Package Manager by adding the following line to your Package.swift:

import PackageDescription

let package = Package(
    [...]
    dependencies: [
        .package(url: "https://github.com/nerdsupremacist/Sync.git", from: "1.0.0")
    ]
)

Usage

If you have ever used Observable Object, then Sync will be extremely easy to use. For this example we will create an app with a Switch that everyone can flip on or off as they like. We will build this using SwiftUI, WebSockets and a Vapor Server. Final code available here.

Sync

For this we will need a few additional packages:

Let's start by building our shared ViewModel. This is easy, instead of using ObservableObject we use SyncableObject. And instead of Published we use Synced:

class ViewModel: SyncableObject {
    @Synced
    var toggle: Bool = false

    init() { }
}

This ViewModel needs to be both on your App codebase as well as on the Server codebase. I recommend putting it in a shared Swift Package, if you're feeling fancy.

Next stop is to create our server. In this example every client will be using the exact same ViewModel. So we're creating a Vapor application, and using syncObjectOverWebSocket to provide the object:

import Vapor
import SyncWebSocketVapor

let app = Application(try .detect())

let viewModel = ViewModel()
app.syncObjectOverWebSocket("view_model") { _ in
    return viewModel
}

try app.run()

For our SwiftUI App, we need to use two things:

  • @SyncedObject: Like ObservedObject, but for Syncable Objects. It's a property wrapper that will dynamically tell SwiftUI when to update the UI
  • Sync: A little wrapper view to start the remote session

Our actual view then uses SyncedObservedObject with our ViewModel

struct ContentView: View {
    @SyncedObject
    var viewModel: ViewModel

    var body: some View {
        Toggle("A toggle", isOn: $viewModel.toggle)
            .animation(.easeIn, value: viewModel.toggle)
            .padding(64)
    }
}

And in order to display it we use Sync, and pass along the Web Socket Connection:

struct RootView: View {
    var body: some View {
        Sync(ViewModel.self, using: .webSocket(url: url)) { viewModel in
            ContentView(viewModel: viewModel)
        }
    }
}

Developing for Web?

No problem. You can scale this solution to the web using Tokamak, and use the same UI on the Web thanks to Web Assembly. Here are the Web Assembly specific packages for Sync:

Here's a small demo of that working:

Sync

Contributions

Contributions are welcome and encouraged!

License

Sync is available under the MIT license. See the LICENSE file for more info.