In realm swift what is the best practice for subscription management?
Say we have 3 classes:
class User: Object, ObjectKeyIdentifiable {
@Persisted(primaryKey: true) var _id: ObjectId
@Peristed(originProperty: "creator") var myEvents: LinkingObject<Event>
@Peristed(originProperty: "user") var myInvitations: LinkingObject<Invitation>
}
class Event: Object, ObjectKeyIdentifiable {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var mainHost: User?
@Persisted var coHosts: List<User>
@Peristed(originProperty: "event") var guestlist: LinkingObject<Invitation>
@Persisted var listener_ids: MutableSet<ObjectId> /// IDs of the hosts
}
class Invitation: Object, ObjectKeyIdentifiable {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var user: User?
@Persited var comingWith: List<User>
@Peristed var event: Event?
@Persisted var listener_ids: MutableSet<ObjectId> /// IDs of users and event
}
To sync “expand” the Event I have to do the following
@MainActor
func syncHosts(event: Event) async throws {
guard let subs = realm?.subscriptions else {
throw RealmError.missingSubscriptions
}
try await subs.update {
for userID in event.listener_ids {
if subs.first(name: "user:\(userID.stringValue)") == nil {
subs.append(QuerySubscription<User>(name: "user:\(user._id.stringValue)", query: {
$0._id = userID
}))
}
}
}
}
OR
@MainActor
func syncHosts(event: Event) async throws {
guard let subs = realm?.subscriptions else {
throw RealmError.missingSubscriptions
}
try await subs.update {
for userID in event.listener_ids {
let user = realm.object(ofType: User, forPrimaryKey: userID)
if (user == nil) || (user.isInvalidated == true) {
subs.append(QuerySubscription<User>({
$0._id == userID
}))
}
}
}
}
This already feels dirty and gets worse when we want to expand Invitations.
@Mainactor
func STEP1InvitationsForEvent(event: Event) async throws {
guard let subs = realm?.subscriptions else {
throw RealmError.missingSubscriptions
}
try await subs.update {
// first
if subs.first(name: "event:invitaitons\(event._id.stringValue)") == nil {
subs.append(QuerySubscription<Invitation>(name: "event:invitaitons\(event._id.stringValue)", query: {
$0.listener_ids.contains(event._id)
}))
}
}
}
@MainActor
func STEP2SyncGuests(invitation: Invitation, eventID: ObjectID) async throws {
guard let subs = realm?.subscriptions else {
throw RealmError.missingSubscriptions
}
try await subs.update {
for id in invitation.listener_ids {
if (id != eventID) {
if subs.first(name: "user:\(id.stringValue)") == nil {
subs.append(QuerySubscription<User>(name: "user:\(id.stringValue)", query: {
$0._id == id
}))
}
}
}
}
}
Is it enough to check QuerySubscription names? Or should we check of the realm object is invalidated? Or both?
Furthermore, say we have a list of rows for invitations. Should STEP2SyncGuests
be ran as a task
on the views? It seems like a lot of work on the main thread. What is the best practice here? Perhaps run the subscriptions in the background? Or a completely different approach?
Please not i’m not sure the code above compiles it’s only for getting the question out there.