Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/nerdsupremacist/sync
Real-time Apps the SwiftUI way
https://github.com/nerdsupremacist/sync
real-time state swift swiftui sync websocket
Last synced: about 2 months ago
JSON representation
Real-time Apps the SwiftUI way
- Host: GitHub
- URL: https://github.com/nerdsupremacist/sync
- Owner: nerdsupremacist
- License: mit
- Created: 2022-01-11T21:02:24.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2022-06-19T18:21:27.000Z (over 2 years ago)
- Last Synced: 2024-05-01T18:58:45.781Z (8 months ago)
- Topics: real-time, state, swift, swiftui, sync, websocket
- Language: Swift
- Homepage:
- Size: 640 KB
- Stars: 155
- Watchers: 3
- Forks: 3
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: Changelog.md
- License: LICENSE
Awesome Lists containing this project
README
# 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 ManagerYou can install Sync via [Swift Package Manager](https://swift.org/package-manager/) by adding the following line to your `Package.swift`:
```swift
import PackageDescriptionlet 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](https://github.com/nerdsupremacist/SyncExampleApp).
For this we will need a few additional packages:
- [Vapor](https://vapor.codes): To create a Web Server that will sync our devices
- [SyncWebSocketClient](https://github.com/nerdsupremacist/SyncWebSocketClient): The client code for using WebSockets
- [SyncWebSocketVapor](https://github.com/nerdsupremacist/SyncWebSocketVapor): A utility to make it easy to serve our object via a WebSocketLet's start by building our shared ViewModel. This is easy, instead of using `ObservableObject` we use `SyncableObject`. And instead of `Published` we use `Synced`:
```swift
class ViewModel: SyncableObject {
@Synced
var toggle: Bool = falseinit() { }
}
```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:
```swift
import Vapor
import SyncWebSocketVaporlet 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](https://developer.apple.com/documentation/swiftui/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 sessionOur actual view then uses SyncedObservedObject with our ViewModel
```swift
struct ContentView: View {
@SyncedObject
var viewModel: ViewModelvar 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:
```swift
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](https://github.com/TokamakUI/Tokamak), and use the same UI on the Web thanks to Web Assembly.
Here are the Web Assembly specific packages for Sync:
- [SyncTokamak](https://github.com/nerdsupremacist/SyncTokamak): Compatibility Layer so that Tokamak reacts to updates
- [SyncWebSocketWebAssemblyClient](https://github.com/nerdsupremacist/SyncWebSocketWebAssemblyClient): Web Assembly compatible version of [SyncWebSocketClient](https://github.com/nerdsupremacist/SyncWebSocketClient)Here's a small demo of that working:
## Contributions
Contributions are welcome and encouraged!## License
Sync is available under the MIT license. See the LICENSE file for more info.