Skip to content

Commit

Permalink
Add support for initialSubscriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
dianaafanador3 committed Aug 4, 2022
1 parent ad40703 commit 882df3f
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 8 deletions.
152 changes: 146 additions & 6 deletions Realm/ObjectServerTests/SwiftFlexibleSyncServerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ extension SwiftFlexibleSyncTests {
XCTAssertEqual(persons.count, 3)

// This will trigger a client reset, which will result in the server not responding to any instruction, this is not removing the object from the database.
// let newPerson = SwiftPerson()
// newPerson.age = 10
// try realm.write {
// realm.add(newPerson)
// }
// XCTAssertEqual(persons.count, 3)
// let newPerson = SwiftPerson()
// newPerson.age = 10
// try realm.write {
// realm.add(newPerson)
// }
// XCTAssertEqual(persons.count, 3)

let newPerson = SwiftPerson(firstName: "\(#function)", lastName: "lastname_\(19)", age: 19)
try realm.write {
Expand Down Expand Up @@ -397,6 +397,146 @@ extension SwiftFlexibleSyncTests {
XCTAssertNotNil(error)
}
}

@MainActor
func testFlexibleSyncInitialSubscriptionsAsync() async throws {
try await populateFlexibleSyncDataForType(SwiftPerson.self) { realm in
for i in 1...20 {
let person = SwiftPerson(firstName: "\(#function)", lastName: "lastname_\(i)", age: i)
realm.add(person)
}
}

let user = try await logInUser(for: basicCredentials(app: self.flexibleSyncApp), app: self.flexibleSyncApp)
var config = user.flexibleSyncConfiguration(initialSubscriptions: { subscriptions in
subscriptions.append(ofType: SwiftPerson.self,
where: { $0.age > 10 && $0.firstName == "\(#function)" })
})

if config.objectTypes == nil {
config.objectTypes = [SwiftPerson.self]
}
let realm = try await Realm(configuration: config, downloadBeforeOpen: .once)
XCTAssertNotNil(realm)

XCTAssertEqual(realm.subscriptions.count, 1)
// Adding this sleep, because there seems to be a timing issue after this commit in baas
// https://github.com/10gen/baas/commit/64e75b3f1fe8a6f8704d1597de60f9dda401ccce,
// data take a little longer to be downloaded to the realm even though the
// sync client changed the subscription state to completed.
sleep(1)
checkCount(expected: 10, realm, SwiftPerson.self)
}

@MainActor
func testFlexibleSyncInitialSubscriptionsNotRerunOnOpen() async throws {
let user = try await logInUser(for: basicCredentials(app: self.flexibleSyncApp), app: self.flexibleSyncApp)
var config = user.flexibleSyncConfiguration(initialSubscriptions: { subscriptions in
subscriptions.append(ofType: SwiftPerson.self,
where: { $0.age > 10 && $0.firstName == "\(#function)" })
})

if config.objectTypes == nil {
config.objectTypes = [SwiftPerson.self]
}
let realm = try await Realm(configuration: config, downloadBeforeOpen: .once)
XCTAssertNotNil(realm)
XCTAssertEqual(realm.subscriptions.count, 1)

let realm2 = try await Realm(configuration: config, downloadBeforeOpen: .once)
XCTAssertNotNil(realm2)
XCTAssertEqual(realm.subscriptions.count, 1)
}

@MainActor
func testFlexibleSyncInitialSubscriptionsRerunOnOpen() async throws {
try await populateFlexibleSyncDataForType(SwiftTypesSyncObject.self) { realm in
for i in 1...30 {
let object = SwiftTypesSyncObject()
object.dateCol = Calendar.current.date(
byAdding: .hour,
value: -i,
to: Date())!
realm.add(object)
}
}

let user = try await logInUser(for: basicCredentials(app: self.flexibleSyncApp), app: self.flexibleSyncApp)
var isFirstOpen = true
var config = user.flexibleSyncConfiguration(initialSubscriptions: { subscriptions in
subscriptions.append(ofType: SwiftTypesSyncObject.self,
where: {
let date = isFirstOpen ? Calendar.current.date(
byAdding: .hour,
value: -10,
to: Date()) : Calendar.current.date(
byAdding: .hour,
value: -20,
to: Date())
isFirstOpen = false
return $0.dateCol < Date() && $0.dateCol > date! })
}, rerunOnOpen: true)

if config.objectTypes == nil {
config.objectTypes = [SwiftTypesSyncObject.self, SwiftPerson.self]
}
let c = config
_ = try await Task { @MainActor in
let realm = try await Realm(configuration: c, downloadBeforeOpen: .always)
XCTAssertNotNil(realm)
XCTAssertEqual(realm.subscriptions.count, 1)
// Adding this sleep, because there seems to be a timing issue after this commit in baas
// https://github.com/10gen/baas/commit/64e75b3f1fe8a6f8704d1597de60f9dda401ccce,
// data take a little longer to be downloaded to the realm even though the
// sync client changed the subscription state to completed.
sleep(1)
checkCount(expected: 9, realm, SwiftTypesSyncObject.self)
}.value

_ = try await Task { @MainActor in
let realm = try await Realm(configuration: c, downloadBeforeOpen: .always)
XCTAssertNotNil(realm)
XCTAssertEqual(realm.subscriptions.count, 2)
checkCount(expected: 19, realm, SwiftTypesSyncObject.self)
}.value
}

@MainActor
func testFlexibleSyncInitialSubscriptionsThrows() async throws {
let user = try await logInUser(for: basicCredentials(app: self.flexibleSyncApp), app: self.flexibleSyncApp)
var config = user.flexibleSyncConfiguration(initialSubscriptions: { subscriptions in
subscriptions.append(ofType: SwiftTypesSyncObject.self,
where: { $0.uuidCol == UUID() })
})

if config.objectTypes == nil {
config.objectTypes = [SwiftTypesSyncObject.self, SwiftPerson.self]
}
do {
_ = try await Realm(configuration: config, downloadBeforeOpen: .once)
} catch {
XCTAssertNotNil(error)
let nsError = error as NSError
XCTAssertEqual(nsError.code, 2)
XCTAssertEqual(nsError.domain, "io.realm.sync.flx")
}
}

@MainActor
func testFlexibleSyncInitialSubscriptionsDefaultConfiguration() async throws {
let user = try await logInUser(for: basicCredentials(app: self.flexibleSyncApp), app: self.flexibleSyncApp)
var config = user.flexibleSyncConfiguration(initialSubscriptions: { subscriptions in
subscriptions.append(ofType: SwiftTypesSyncObject.self)
})

if config.objectTypes == nil {
config.objectTypes = [SwiftTypesSyncObject.self, SwiftPerson.self]
}
Realm.Configuration.defaultConfiguration = config

let realm = try await Realm(downloadBeforeOpen: .once)
XCTAssertEqual(realm.subscriptions.count, 1)
}
}

#endif // canImport(_Concurrency)
Expand Down
6 changes: 4 additions & 2 deletions RealmSwift/SwiftUI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ extension Projection: _ObservedResultsValue { }
if let configuration = configuration,
configuration.syncConfiguration?.isFlexibleSync ?? false {
#if swift(>=5.6) && canImport(_Concurrency)
Task {
Task { @MainActor in
do {
let realm = try await Realm(configuration: configuration)
let filter = filter ?? `where` ?? NSPredicate(format: "TRUEPREDICATE")
Expand Down Expand Up @@ -1192,7 +1192,9 @@ private class ObservableAsyncOpenStorage: ObservableObject {
}

// Use the user configuration by default or set configuration with the current user `syncConfiguration`'s.
if var configuration = configuration {
if var configuration = configuration,
let syncConfiguration = configuration.syncConfiguration,
syncConfiguration.user.id == user.id {
let userSyncConfig = config.syncConfiguration
configuration.syncConfiguration = userSyncConfig
config = configuration
Expand Down
7 changes: 7 additions & 0 deletions RealmSwift/Sync.swift
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,13 @@ public enum ClientResetMode {
ObjectiveCSupport.convert(object: config.partitionValue)
}

/**
Returns true if the sync configuration corresponds to a flexible sync App.
*/
internal var isFlexibleSync: Bool {
return config.enableFlexibleSync
}

/**
An enum which determines file recovery behvaior in the event of a client reset.
- note: Defaults to `.manual`
Expand Down
46 changes: 46 additions & 0 deletions RealmSwift/SyncSubscription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,21 @@ struct QuerySubscription<T: RealmFetchable> {
rlmSyncSubscriptionSet.remove(subscription._rlmSyncSubscription)
}
}

/**
Appends a query to the subscription set.
- warning: This method may only be called on the `initialSubscription` block when initialising the flexible sync configuration.
- parameter type: The type of the object to be queried.
- parameter where: A query builder that generates a query which can be added to the
subscriptions set the user is subscribed to.
*/
public func `append`<T: RealmFetchable>(ofType type: T.Type, `where` query: ((Query<T>) -> Query<Bool>)? = nil) {
let query = query != nil ? QuerySubscription<T>(query!) : QuerySubscription<T>()
rlmSyncSubscriptionSet.addSubscription(withClassName: query.className,
predicate: query.predicate)
}
}

#if swift(>=5.6) && canImport(_Concurrency)
Expand Down Expand Up @@ -255,3 +270,34 @@ extension SyncSubscriptionSet {
}
}
#endif // swift(>=5.6)

extension User {
/**
Return a `Realm` for the given configuration and injects the sync configuration associated to a flexible sync session.
- parameter configuration: The configuration for the realm. This can be used if any custom configuration is needed.
- returns: A `Realm`.
*/
public func realm(configuration: Realm.Configuration = Realm.Configuration()) throws -> Realm {
return try Realm(configuration: flexibleSyncConfiguration(configuration))
}

/**
Create a flexible sync configuration instance, which can be used to open a realm which
supports flexible sync.
It won't be possible to combine flexible and partition sync in the same app, which means if you open
a realm with a flexible sync configuration, you won't be able to open a realm with a PBS configuration
and the other way around.
- parameter configuration: The configuration for the realm. This can be used if any custom configuration is needed.
- returns: A `Realm.Configuration` instance with a flexible sync configuration.
*/
public func flexibleSyncConfiguration(_ configuration: Realm.Configuration = Realm.Configuration()) -> Realm.Configuration {
let config = configuration.rlmConfiguration
config.syncConfiguration = self.__flexibleSyncConfiguration().syncConfiguration
return ObjectiveCSupport.convert(object: config)
}
}

0 comments on commit 882df3f

Please sign in to comment.