diff --git a/CHANGELOG.md b/CHANGELOG.md index fdf78fc812..377e1c3e02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ x.y.z Release notes (yyyy-MM-dd) ### Fixed * ([#????](https://github.com/realm/realm-swift/issues/????), since v?.?.?) -* None. +* Client reset callbacks no longer attempt to open a realm without an initialized schema ([PR #8125](https://github.com/realm/realm-swift/pull/8125)) diff --git a/Realm/ObjectServerTests/RLMObjectServerTests.mm b/Realm/ObjectServerTests/RLMObjectServerTests.mm index 4657e45d0d..7c3d71f265 100644 --- a/Realm/ObjectServerTests/RLMObjectServerTests.mm +++ b/Realm/ObjectServerTests/RLMObjectServerTests.mm @@ -26,12 +26,12 @@ #import "RLMCredentials.h" #import "RLMObjectSchema_Private.hpp" #import "RLMRealm+Sync.h" -#import "RLMRealmConfiguration_Private.h" +#import "RLMRealmConfiguration_Private.hpp" #import "RLMRealmUtil.hpp" #import "RLMRealm_Dynamic.h" #import "RLMRealm_Private.hpp" #import "RLMSchema_Private.h" -#import "RLMSyncConfiguration_Private.h" +#import "RLMSyncConfiguration_Private.hpp" #import "RLMSyncManager_Private.hpp" #import "RLMSyncUtil_Private.h" #import "RLMUser_Private.hpp" @@ -39,6 +39,7 @@ #import #import +#import #import #import @@ -1510,6 +1511,66 @@ - (void)testSetClientResetCallbacks { } +// TODO: Consider testing with sync_config->on_sync_client_event_hook or a client reset +- (void)testBeforeClientResetCallbackNotVersioned { + // Setup sync config + RLMSyncConfiguration *syncConfig = [[RLMSyncConfiguration alloc] initWithRawConfig:{} path:""]; + XCTestExpectation *beforeExpectation = [self expectationWithDescription:@"block called once"]; + syncConfig.clientResetMode = RLMClientResetModeRecoverUnsyncedChanges; + syncConfig.beforeClientReset = ^(RLMRealm *beforeFrozen) { + + XCTAssertNotEqual(RLMNotVersioned, beforeFrozen->_realm->schema_version()); + [beforeExpectation fulfill]; + }; + auto& beforeWrapper = syncConfig.rawConfiguration.notify_before_client_reset; + + // Setup a realm with a versioned schema + RLMRealmConfiguration *configVersioned = [RLMRealmConfiguration defaultConfiguration]; + configVersioned.fileURL = RLMTestRealmURL(); + @autoreleasepool { + RLMRealm *versioned = [RLMRealm realmWithConfiguration:configVersioned error:nil]; + XCTAssertEqual(0U, versioned->_realm->schema_version()); + } + std::shared_ptr versioned = realm::Realm::get_shared_realm(configVersioned.config); + + // Create a config that's not versioned. + RLMRealmConfiguration *configUnversioned = [RLMRealmConfiguration defaultConfiguration]; + configUnversioned.configRef.schema_version = RLMNotVersioned; + std::shared_ptr unversioned = realm::Realm::get_shared_realm(configUnversioned.config); + + XCTAssertNotEqual(versioned->schema_version(), RLMNotVersioned); + XCTAssertEqual(unversioned->schema_version(), RLMNotVersioned); + beforeWrapper(versioned); // one realm should invoke the block + beforeWrapper(unversioned); // while the other should not invoke the block + + [self waitForExpectationsWithTimeout:5 handler:nil]; +} + +// TODO: Consider testing with sync_config->on_sync_client_event_hook or a client reset +- (void)testAfterClientResetCallbackNotVersioned { + // Setup sync config + RLMSyncConfiguration *syncConfig = [[RLMSyncConfiguration alloc] initWithRawConfig:{} path:""]; + XCTestExpectation *afterExpectation = [self expectationWithDescription:@"block should not be called"]; + afterExpectation.inverted = true; + + syncConfig.clientResetMode = RLMClientResetModeRecoverUnsyncedChanges; + syncConfig.afterClientReset = ^(RLMRealm * _Nonnull, RLMRealm * _Nonnull) { + [afterExpectation fulfill]; + }; + auto& afterWrapper = syncConfig.rawConfiguration.notify_after_client_reset; + + // Create a config that's not versioned. + RLMRealmConfiguration *configUnversioned = [RLMRealmConfiguration defaultConfiguration]; + configUnversioned.configRef.schema_version = RLMNotVersioned; + std::shared_ptr unversioned = realm::Realm::get_shared_realm(configUnversioned.config); + + auto unversionedTsr = realm::ThreadSafeReference(unversioned); + XCTAssertEqual(unversioned->schema_version(), RLMNotVersioned); + afterWrapper(unversioned, std::move(unversionedTsr), false); + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + #pragma mark - Progress Notifications static const NSInteger NUMBER_OF_BIG_OBJECTS = 2; diff --git a/Realm/RLMSyncConfiguration.h b/Realm/RLMSyncConfiguration.h index d2af02e077..54ea2ecfce 100644 --- a/Realm/RLMSyncConfiguration.h +++ b/Realm/RLMSyncConfiguration.h @@ -120,7 +120,7 @@ typedef NS_ENUM(NSUInteger, RLMClientResetMode) { A block type used to report before a client reset will occur. The `beforeFrozen` is a frozen copy of the local state prior to client reset. */ -RLM_SWIFT_SENDABLE // invoked on a backgroun thread +RLM_SWIFT_SENDABLE // invoked on a background thread typedef void(^RLMClientResetBeforeBlock)(RLMRealm * _Nonnull beforeFrozen); /** diff --git a/Realm/RLMSyncConfiguration.mm b/Realm/RLMSyncConfiguration.mm index 50b60a8b97..acbd9bfafc 100644 --- a/Realm/RLMSyncConfiguration.mm +++ b/Realm/RLMSyncConfiguration.mm @@ -82,7 +82,9 @@ RLMSyncError errorKindForSyncError(SyncError error) { RLMClientResetBeforeBlock block; void operator()(std::shared_ptr local) { @autoreleasepool { - block([RLMRealm realmWithSharedRealm:local schema:getSchema(*local) dynamic:false]); + if (local->schema_version() != RLMNotVersioned) { + block([RLMRealm realmWithSharedRealm:local schema:getSchema(*local) dynamic:false]); + } } } }; @@ -91,15 +93,17 @@ void operator()(std::shared_ptr local) { RLMClientResetAfterBlock block; void operator()(std::shared_ptr local, ThreadSafeReference remote, bool) { @autoreleasepool { - RLMSchema *schema = getSchema(*local); - RLMRealm *localRealm = [RLMRealm realmWithSharedRealm:local - schema:schema - dynamic:false]; - - RLMRealm *remoteRealm = [RLMRealm realmWithSharedRealm:Realm::get_shared_realm(std::move(remote)) - schema:schema - dynamic:false]; - block(localRealm, remoteRealm); + if (local->schema_version() != RLMNotVersioned) { + RLMSchema *schema = getSchema(*local); + RLMRealm *localRealm = [RLMRealm realmWithSharedRealm:local + schema:schema + dynamic:false]; + + RLMRealm *remoteRealm = [RLMRealm realmWithSharedRealm:Realm::get_shared_realm(std::move(remote)) + schema:schema + dynamic:false]; + block(localRealm, remoteRealm); + } } } };