realm-swift
realm-swift copied to clipboard
Flexible Sync V2 (Proposed 1)
This PR contains a complete new API for Flexible Sync, intended to make flexible sync less confusing to the developer.
let app = App(id: "my-app-id")
let user = try await app.login(credentials: .anonymous)
let realm = try user.realm(configuration: .defaultConfiguration)
// You can subscribe and get `QueryResults` from 1-12 queries at the same time, this subscriptions will be done within a batch write
let (persons, dogs, birds) = try await realm.subscriptions.subscribe(to:
QuerySubscription<Person> { $0.age >= 18},
QuerySubscription<Dog>(), //Intended to query all elements for this object
QuerySubscription<Bird> { $0.species == .magpie })
print(persons.count) //20
let newPerson = Person()
newPerson.age = 18
try realm.write {
realm.add(newPerson)
}
// QueryResults will be updated if the values are within the subscription query
print(persons.count) //21
// Unsubscribe will remove the values from the `QueryResult` and remove the subscription from the subscription set.
try await persons.unsubscribe()
// Reusing a `QueryResults` is as easy as calling again subscribe for the same query
let persons: QueryResults<Person>= try await realm.subscriptions.subscribe(to: { $0.age >= 18 })
// Or search in your subscription set
let foundedSubscription = realm.subscriptions.first(ofType: SwiftPerson.self, where: { { $0.age >= 18 })
// You can use unsubscribeAll to remove all subscriptions from the subscription set
try await realm.subscriptions.unsubscribeAll()
// Or you can remove them by type
try await realm.subscriptions.unsubscribeAll(ofType: Person.self)
SwiftUI
You can use @ObservedResults
to add a subscription and retrieve data from it
We are adding a new property to @ObservedResults
which includes a state value, this state will publish any subscription state changes to view.
public enum SubscriptionState {
case pending //Subscription has been added and waiting for data to bootstrap.
case error(Error) // An error has occurred while adding the subscription (client or server side)
case completed // Data has been bootstrapped and query results updated.
}
@available(macOS 12.0, *)
struct ObservedQueryResultsStateView: View {
@ObservedResults(Person.self, where: { $0.age > 18 && $0.firstName == ProcessInfo.processInfo.environment["firstName"]! })
var persons
var body: some View {
VStack {
switch $persons.state {
case .pending:
ProgressView()
case .completed:
List {
ForEach(persons) { person in
Text("\(person.firstName)")
}
}
case .error(let error):
ErrorView(error: error)
.background(Color.red)
.transition(AnyTransition.move(edge: .trailing)).animation(.default)
}
}
}
}
@available(macOS 12.0, *)
struct ObservedQueryResultsView: View {
@ObservedResults(Person.self, where: { $0.age >= 15 && $0.firstName == ProcessInfo.processInfo.environment["firstName"]! })
var persons
@State var searchFilter: String = ""
var body: some View {
VStack {
if persons.isEmpty {
ProgressView()
} else {
List {
ForEach(persons) { person in
HStack {
Text("\(person.firstName)")
Spacer()
Text("\(person.age)")
}
}
}
}
}
.onDisappear {
Task {
do {
try await $persons.unsubscribe()
}
}
}
}
}
Notes
- AnyQueryResults??, didn't add the implementation for using this as a collection, which can be added if needed but I don’t consider this to be a priority, which brings me to the issue if the user is really going to use this instead of doing a search for the query or subscribe again to the query which will retrieve the results without adding another subscription.