Skip to content

Commit

Permalink
RCOCOA-2334: Move initial subscription logic into Core (#8549)
Browse files Browse the repository at this point in the history
* Clean up API docs, add changelog (+1 squashed commit)
Squashed commits:
[04969a5] Move initial subscription logic into Core

* Address CR comments

* Remove spaces after colons
  • Loading branch information
nirinchev authored Apr 22, 2024
1 parent 309003b commit 3a9586b
Show file tree
Hide file tree
Showing 19 changed files with 206 additions and 56 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
x.y.z Release notes (yyyy-MM-dd)
=============================================================
### Enhancements
* None.
* Added `SyncConfiguration.initialSubscriptions` which describes the initial subscription configuration that was passed when constructing the `SyncConfiguration`. ([#8548](https://github.com/realm/realm-swift/issues/8548))

### Fixed
* <How to hit and notice issue? what was the impact?> ([#????](https://github.com/realm/realm-swift/issues/????), since v?.?.?)
Expand Down
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ let package = Package(
"Realm/RLMEmailPasswordAuth.mm",
"Realm/RLMFindOneAndModifyOptions.mm",
"Realm/RLMFindOptions.mm",
"Realm/RLMInitialSubscriptionsConfiguration.m",
"Realm/RLMMongoClient.mm",
"Realm/RLMMongoCollection.mm",
"Realm/RLMNetworkTransport.mm",
Expand Down
1 change: 1 addition & 0 deletions Realm.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Pod::Spec.new do |s|
'include/RLMApp.h',
'include/RLMAppCredentials.h',
'include/RLMBSON.h',
'include/RLMInitialSubscriptionsConfiguration.h',
'include/RLMNetworkTransport.h',
'include/RLMPushClient.h',
'include/RLMProviderClient.h',
Expand Down
10 changes: 10 additions & 0 deletions Realm.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,12 @@
0C3BD4B325C1BDF1007CFDD3 /* RLMDictionary.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0C3BD4B125C1BDF1007CFDD3 /* RLMDictionary.mm */; };
0C3BD4D325C1C5AB007CFDD3 /* Map.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C3BD4D225C1C5AB007CFDD3 /* Map.swift */; };
0C5796A225643D7500744CAE /* RLMUUID.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0C57969F25643D7500744CAE /* RLMUUID.mm */; };
0C63BB902BCD787300E25C3A /* RLMInitialSubscriptionsConfiguration.h in Sources */ = {isa = PBXBuildFile; fileRef = 0CC270AB2BCD665800788EE1 /* RLMInitialSubscriptionsConfiguration.h */; };
0C86B33925E15B6000775FED /* PrimitiveDictionaryPropertyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C86B33825E15B6000775FED /* PrimitiveDictionaryPropertyTests.m */; };
0C9758BF264974660097B48D /* SwiftRLMDictionaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9758BE264974660097B48D /* SwiftRLMDictionaryTests.swift */; };
0CBF2DB927286FFD00635902 /* ProjectedCollectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CBF2DB827286FFD00635902 /* ProjectedCollectTests.swift */; };
0CC270AA2BCD664200788EE1 /* RLMInitialSubscriptionsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CC270A92BCD664200788EE1 /* RLMInitialSubscriptionsConfiguration.m */; };
0CC270AC2BCD665800788EE1 /* RLMInitialSubscriptionsConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 0CC270AB2BCD665800788EE1 /* RLMInitialSubscriptionsConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
0CD1632826D3DF1D0027C49B /* ProjectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD1632726D3DF1C0027C49B /* ProjectionTests.swift */; };
0CED6DB82655087200B80277 /* RLMDictionary_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 0C3BD50125C1DE6F007CFDD3 /* RLMDictionary_Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
1A0512771D8746CD00806AEC /* RLMSyncConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A3623651D8384BA00945A54 /* RLMSyncConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -628,6 +631,8 @@
0C86B33825E15B6000775FED /* PrimitiveDictionaryPropertyTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PrimitiveDictionaryPropertyTests.m; sourceTree = "<group>"; };
0C9758BE264974660097B48D /* SwiftRLMDictionaryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftRLMDictionaryTests.swift; sourceTree = "<group>"; };
0CBF2DB827286FFD00635902 /* ProjectedCollectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectedCollectTests.swift; sourceTree = "<group>"; };
0CC270A92BCD664200788EE1 /* RLMInitialSubscriptionsConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RLMInitialSubscriptionsConfiguration.m; sourceTree = "<group>"; };
0CC270AB2BCD665800788EE1 /* RLMInitialSubscriptionsConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLMInitialSubscriptionsConfiguration.h; sourceTree = "<group>"; };
0CD1632526D3DD7B0027C49B /* Projection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Projection.swift; sourceTree = "<group>"; };
0CD1632726D3DF1C0027C49B /* ProjectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProjectionTests.swift; sourceTree = "<group>"; };
1A0512731D87413000806AEC /* RLMSyncUtil_Private.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RLMSyncUtil_Private.hpp; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1200,6 +1205,8 @@
4993220324129DCD00A0EC8E /* RLMCredentials.h */,
4993220424129DCD00A0EC8E /* RLMCredentials.mm */,
4993220224129DCD00A0EC8E /* RLMCredentials_Private.hpp */,
0CC270AB2BCD665800788EE1 /* RLMInitialSubscriptionsConfiguration.h */,
0CC270A92BCD664200788EE1 /* RLMInitialSubscriptionsConfiguration.m */,
4993221524129E6600A0EC8E /* RLMNetworkTransport.h */,
4993221424129E6500A0EC8E /* RLMNetworkTransport.mm */,
CF330BBB24E56E3A00F07EE2 /* RLMNetworkTransport_Private.hpp */,
Expand Down Expand Up @@ -1906,6 +1913,7 @@
6807E64F2487F9210096066F /* RLMPushClient_Private.hpp in Headers */,
1AD397CF1F72FFC7002AA897 /* RLMRealm+Sync.h in Headers */,
5D659EBF1BE04556006515A0 /* RLMRealm.h in Headers */,
0CC270AC2BCD665800788EE1 /* RLMInitialSubscriptionsConfiguration.h in Headers */,
5D659EC01BE04556006515A0 /* RLMRealm_Dynamic.h in Headers */,
5D659EC11BE04556006515A0 /* RLMRealm_Private.h in Headers */,
5D659EC21BE04556006515A0 /* RLMRealmConfiguration.h in Headers */,
Expand Down Expand Up @@ -2454,6 +2462,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0C63BB902BCD787300E25C3A /* RLMInitialSubscriptionsConfiguration.h in Sources */,
3F73BC961E3A878500FE80B6 /* NSError+RLMSync.m in Sources */,
5D659E851BE04556006515A0 /* RLMAccessor.mm in Sources */,
5D659E861BE04556006515A0 /* RLMAnalytics.mm in Sources */,
Expand Down Expand Up @@ -2513,6 +2522,7 @@
531F956C27906F7600E497F1 /* RLMSyncSubscription.mm in Sources */,
1A84132F1D4BCCE600C5326F /* RLMSyncUtil.mm in Sources */,
3F67DB3E1E26D69C0024533D /* RLMThreadSafeReference.mm in Sources */,
0CC270AA2BCD664200788EE1 /* RLMInitialSubscriptionsConfiguration.m in Sources */,
5D659E9A1BE04556006515A0 /* RLMUpdateChecker.mm in Sources */,
CF76F7DD24816AAB00890DD2 /* RLMUpdateResult.mm in Sources */,
1ABDCDB01D793008003489E3 /* RLMUser.mm in Sources */,
Expand Down
10 changes: 10 additions & 0 deletions Realm/ObjectServerTests/AsyncSyncTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,11 @@ class AsyncFlexibleSyncTests: SwiftSyncTestCase {
})
})
config.objectTypes = [SwiftPerson.self]

XCTAssertNotNil(config.syncConfiguration?.initialSubscriptions)
XCTAssertNotNil(config.syncConfiguration?.initialSubscriptions?.callback)
XCTAssertEqual(config.syncConfiguration?.initialSubscriptions?.rerunOnOpen, false)

let realm = try await Realm(configuration: config, downloadBeforeOpen: .once)
XCTAssertEqual(realm.subscriptions.count, 1)
checkCount(expected: 10, realm, SwiftPerson.self)
Expand Down Expand Up @@ -644,6 +649,11 @@ class AsyncFlexibleSyncTests: SwiftSyncTestCase {
}
}, rerunOnOpen: true)
config.objectTypes = [SwiftPerson.self]

XCTAssertNotNil(config.syncConfiguration?.initialSubscriptions)
XCTAssertNotNil(config.syncConfiguration?.initialSubscriptions?.callback)
XCTAssertEqual(config.syncConfiguration?.initialSubscriptions?.rerunOnOpen, false)

try await Task {
let realm = try await Realm(configuration: config, downloadBeforeOpen: .once)
XCTAssertEqual(realm.subscriptions.count, 1)
Expand Down
71 changes: 71 additions & 0 deletions Realm/RLMInitialSubscriptionsConfiguration.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2024 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////


#import <Foundation/Foundation.h>
#import <Realm/RLMRealm.h>

RLM_HEADER_AUDIT_BEGIN(nullability, sendability)

/**
A block which receives a subscription set instance, that can be used to add an initial set of subscriptions which will be executed
when the Realm is first opened.
*/
RLM_SWIFT_SENDABLE
typedef void(^RLMFlexibleSyncInitialSubscriptionsBlock)(RLMSyncSubscriptionSet * _Nonnull subscriptions);

/**
A configuration controlling how the initial subscriptions are populated when a Realm file is first opened.
@see `RLMSubscriptionSet`
*/
RLM_SWIFT_SENDABLE RLM_FINAL // immutable final class
@interface RLMInitialSubscriptionsConfiguration : NSObject

/**
A callback that's executed in an update block to populate the initial subscriptions for that Realm.
This callback will only be executed when the Realm is first created, unless `rerunOnOpen` is `true`, in which case it will be executed every time
the Realm is opened.
*/
@property (nonatomic, readonly) RLMFlexibleSyncInitialSubscriptionsBlock callback;

/**
Controls whether to re-run the `callback` every time the Realm is opened.
*/
@property (nonatomic, readonly) BOOL rerunOnOpen;

/**
Create a new initial subscriptions configuration.
@param callback Callback that will be invoked to update the subscriptions for this Realm file when it's first created or every time it's opened if `rerunOnOpen` is `true`.
@param rerunOnOpen A flag controlling whether to run the subscription callback every time the Realm is opened or only the first time.
*/
- (instancetype)initWithCallback:(RLMFlexibleSyncInitialSubscriptionsBlock)callback rerunOnOpen:(BOOL)rerunOnOpen;


/**
Create a new initial subscriptions configuration.
@param callback Callback that will be invoked to update the subscriptions for this Realm file when it's first created.
*/
- (instancetype)initWithCallback:(RLMFlexibleSyncInitialSubscriptionsBlock)callback;

@end

RLM_HEADER_AUDIT_END(nullability, sendability)
36 changes: 36 additions & 0 deletions Realm/RLMInitialSubscriptionsConfiguration.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2024 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////


#import "RLMInitialSubscriptionsConfiguration.h"

@implementation RLMInitialSubscriptionsConfiguration

-(id)initWithCallback:(RLMFlexibleSyncInitialSubscriptionsBlock)callback
{
return [self initWithCallback:callback rerunOnOpen:false];
}

-(id)initWithCallback:(RLMFlexibleSyncInitialSubscriptionsBlock)callback rerunOnOpen:(BOOL)rerunOnOpen
{
_callback = callback;
_rerunOnOpen = rerunOnOpen;
return self;
}

@end
19 changes: 1 addition & 18 deletions Realm/RLMRealm.mm
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,6 @@ + (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration
}
}

bool isFirstOpen = false;
if (realm->_schema) { }
else if (dynamic) {
realm->_schema = [RLMSchema dynamicSchemaFromObjectStoreSchema:realm->_realm->schema()];
Expand Down Expand Up @@ -482,16 +481,9 @@ + (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration
};
}

DataInitializationFunction initializationFunction;
if (!configuration.rerunOnOpen && configuration.initialSubscriptions) {
initializationFunction = [&isFirstOpen](SharedRealm) {
isFirstOpen = true;
};
}

try {
realm->_realm->update_schema(schema.objectStoreCopy, config.schema_version,
std::move(migrationFunction), std::move(initializationFunction));
std::move(migrationFunction));
}
catch (...) {
RLMRealmTranslateException(error);
Expand Down Expand Up @@ -522,15 +514,6 @@ + (instancetype)realmWithConfiguration:(RLMRealmConfiguration *)configuration
realm->_realm->m_binding_context->realm = realm->_realm;
}

#if REALM_ENABLE_SYNC
if (isFirstOpen || (configuration.rerunOnOpen && !realmIsCached)) {
RLMSyncSubscriptionSet *subscriptions = realm.subscriptions;
[subscriptions update:^{
configuration.initialSubscriptions(subscriptions);
}];
}
#endif

// Run Analytics and Update checker, this will be run only the first any realm open
[self runFirstCheckForConfiguration:configuration schema:realm.schema];

Expand Down
7 changes: 0 additions & 7 deletions Realm/RLMRealmConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,6 @@ RLM_HEADER_AUDIT_BEGIN(nullability, sendability)
RLM_SWIFT_SENDABLE
typedef BOOL (^RLMShouldCompactOnLaunchBlock)(NSUInteger totalBytes, NSUInteger bytesUsed);

/**
A block which receives a subscription set instance, that can be used to add an initial set of subscriptions which will be executed
when the Realm is first opened.
*/
RLM_SWIFT_SENDABLE
typedef void(^RLMFlexibleSyncInitialSubscriptionsBlock)(RLMSyncSubscriptionSet * _Nonnull subscriptions);

/**
An `RLMRealmConfiguration` instance describes the different options used to
create an instance of a Realm.
Expand Down
2 changes: 0 additions & 2 deletions Realm/RLMRealmConfiguration.mm
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,6 @@ - (instancetype)copyWithZone:(NSZone *)zone {
configuration->_customSchema = _customSchema;
configuration->_eventConfiguration = _eventConfiguration;
configuration->_migrationObjectClass = _migrationObjectClass;
configuration->_initialSubscriptions = _initialSubscriptions;
configuration->_rerunOnOpen = _rerunOnOpen;
return configuration;
}

Expand Down
4 changes: 0 additions & 4 deletions Realm/RLMRealmConfiguration_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ RLM_HEADER_AUDIT_BEGIN(nullability)
@property (nonatomic, nullable) Class migrationObjectClass;
@property (nonatomic) bool disableAutomaticChangeNotifications;

// Flexible Sync
@property (nonatomic, readwrite, nullable) RLMFlexibleSyncInitialSubscriptionsBlock initialSubscriptions;
@property (nonatomic, readwrite) BOOL rerunOnOpen;

// Get the default configuration without copying it
+ (RLMRealmConfiguration *)rawDefaultConfiguration;

Expand Down
7 changes: 7 additions & 0 deletions Realm/RLMSyncConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
#import <Foundation/Foundation.h>

#import <Realm/RLMSyncManager.h>
#import <Realm/RLMInitialSubscriptionsConfiguration.h>

@class RLMApp;
@class RLMRealm;
@class RLMRealmConfiguration;
@class RLMUser;
@class RLMInitialSubscriptionsConfiguration;
@protocol RLMBSON;

RLM_HEADER_AUDIT_BEGIN(nullability, sendability)
Expand Down Expand Up @@ -175,6 +177,11 @@ typedef void(^RLMClientResetAfterBlock)(RLMRealm * _Nonnull beforeFrozen, RLMRea
*/
@property (nonatomic, nullable) RLMSyncErrorReportingBlock manualClientResetHandler;

/**
A configuration that controls how initial subscriptions are populated when the Realm is opened.
@see `RLMInitialSubscriptionsConfiguration`
*/
@property (nonatomic, readwrite, nullable) RLMInitialSubscriptionsConfiguration *initialSubscriptions;

/**
Whether nonfatal connection errors should cancel async opens.
Expand Down
44 changes: 44 additions & 0 deletions Realm/RLMSyncConfiguration.mm
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
#import "RLMApp_Private.hpp"
#import "RLMBSON_Private.hpp"
#import "RLMError_Private.hpp"
#import "RLMInitialSubscriptionsConfiguration.h"
#import "RLMRealm_Private.hpp"
#import "RLMRealmConfiguration_Private.h"
#import "RLMRealmConfiguration_Private.hpp"
#import "RLMRealmUtil.hpp"
#import "RLMSchema_Private.hpp"
#import "RLMSyncManager_Private.hpp"
#import "RLMSyncSession_Private.hpp"
#import "RLMSyncSubscription.h"
#import "RLMSyncUtil_Private.hpp"
#import "RLMUser_Private.hpp"
#import "RLMUtil.hpp"
Expand Down Expand Up @@ -80,6 +82,23 @@ void operator()(std::shared_ptr<Realm> local, ThreadSafeReference remote, bool)
}
}
};

struct InitialSubscriptionsWrapper : CallbackSchema {
RLMFlexibleSyncInitialSubscriptionsBlock block;
void operator()(std::shared_ptr<Realm> local) {
@autoreleasepool {
RLMRealm *realm = [RLMRealm realmWithSharedRealm:local
schema:customSchema
dynamic:dynamic
freeze:false];

RLMSyncSubscriptionSet* subscriptions = realm.subscriptions;
[subscriptions update:^{
block(subscriptions);
}];
}
}
};
} // anonymous namespace

@interface RLMSyncConfiguration () {
Expand Down Expand Up @@ -190,6 +209,26 @@ - (void)setManualClientResetHandler:(RLMSyncErrorReportingBlock)manualClientRese
[self assignConfigErrorHandler:self.user];
}

- (RLMInitialSubscriptionsConfiguration *)initialSubscriptions {
if (_config->subscription_initializer) {
auto wrapper = _config->subscription_initializer.target<InitialSubscriptionsWrapper>();

return [[RLMInitialSubscriptionsConfiguration alloc] initWithCallback:wrapper->block
rerunOnOpen:_config->rerun_init_subscription_on_open];
}

return nil;
}

- (void)setInitialSubscriptions:(RLMInitialSubscriptionsConfiguration *)initialSubscriptions {
if (initialSubscriptions) {
_config->subscription_initializer = InitialSubscriptionsWrapper{.block = initialSubscriptions.callback};
_config->rerun_init_subscription_on_open = initialSubscriptions.rerunOnOpen;
} else {
_config->subscription_initializer = nil;
}
}

void RLMSetConfigInfoForClientResetCallbacks(realm::SyncConfig& syncConfig, RLMRealmConfiguration *config) {
if (syncConfig.notify_before_client_reset) {
auto before = syncConfig.notify_before_client_reset.target<BeforeClientResetWrapper>();
Expand All @@ -201,6 +240,11 @@ void RLMSetConfigInfoForClientResetCallbacks(realm::SyncConfig& syncConfig, RLMR
after->dynamic = config.dynamic;
after->customSchema = config.customSchema;
}
if (syncConfig.subscription_initializer) {
auto initializer = syncConfig.subscription_initializer.target<InitialSubscriptionsWrapper>();
initializer->dynamic = config.dynamic;
initializer->customSchema = config.customSchema;
}
}

- (id<RLMBSON>)partitionValue {
Expand Down
Loading

0 comments on commit 3a9586b

Please sign in to comment.