Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RCOCOA-2334: Move initial subscription logic into Core #8549

Merged
merged 4 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ x.y.z Release notes (yyyy-MM-dd)
=============================================================
### Enhancements
* The default base url in `AppConfiguration` has been updated to point to `services.cloud.mongodb.com`. See https://www.mongodb.com/docs/atlas/app-services/domain-migration/ for more information. ([#8512](https://github.com/realm/realm-swift/issues/8512))
* 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 @@ -20,7 +20,7 @@

import Realm
import Realm.Private
import RealmSwift

Check notice on line 23 in Realm/ObjectServerTests/AsyncSyncTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | sync_15.3 | Test - macOS

Realm/ObjectServerTests/AsyncSyncTests.swift#L23

Add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'RealmSwift'
import XCTest

#if canImport(RealmTestSupport)
Expand Down Expand Up @@ -263,7 +263,7 @@

let configuration = try configuration()
func isolatedOpen(_ actor: isolated CustomExecutorActor) async throws {
_ = try await Realm(configuration: configuration, actor: actor, downloadBeforeOpen: .always)

Check notice on line 266 in Realm/ObjectServerTests/AsyncSyncTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | sync_15.3 | Test - macOS

Realm/ObjectServerTests/AsyncSyncTests.swift#L266

Non-sendable type 'Realm' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
}

// Try opening the Realm with the Task being cancelled at every possible
Expand Down Expand Up @@ -406,7 +406,7 @@
["favourite_colour": "green", "apples": 10]
])

try await user.refreshCustomData()

Check notice on line 409 in Realm/ObjectServerTests/AsyncSyncTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | sync_15.3 | Test - macOS

Realm/ObjectServerTests/AsyncSyncTests.swift#L409

Non-sendable type '[AnyHashable : Any]' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
XCTAssertEqual(user.customData["favourite_colour"], .string("green"))
XCTAssertEqual(user.customData["apples"], .int64(10))
}
Expand Down Expand Up @@ -611,6 +611,11 @@
})
})
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 @@
}
}, 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 Expand Up @@ -784,7 +794,7 @@
_ = try await results0.subscribe()
ex.fulfill()
}
await fulfillment(of: [ex], timeout: 1.0)

Check notice on line 797 in Realm/ObjectServerTests/AsyncSyncTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | sync_15.3 | Test - macOS

Realm/ObjectServerTests/AsyncSyncTests.swift#L797

Passing argument of non-sendable type 'XCTestCase' outside of main actor-isolated context may introduce data races
try await task.value
XCTAssertEqual(realm.subscriptions.count, 1)
}
Expand All @@ -807,7 +817,7 @@
XCTAssertEqual(realm.subscriptions.count, 1)
ex.fulfill()
}
await fulfillment(of: [ex], timeout: 5.0)

Check notice on line 820 in Realm/ObjectServerTests/AsyncSyncTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | sync_15.3 | Test - macOS

Realm/ObjectServerTests/AsyncSyncTests.swift#L820

Passing argument of non-sendable type 'XCTestCase' outside of main actor-isolated context may introduce data races
XCTAssertEqual(realm.subscriptions.count, 1)
}

Expand Down Expand Up @@ -849,7 +859,7 @@
let user = try await createUser()
var config = user.flexibleSyncConfiguration()
config.objectTypes = [SwiftPerson.self]
let realm = try await Realm(configuration: config, actor: MainActor.shared)

Check notice on line 862 in Realm/ObjectServerTests/AsyncSyncTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | sync_15.3 | Test - macOS

Realm/ObjectServerTests/AsyncSyncTests.swift#L862

Non-sendable type 'Realm' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
let results1 = try await realm.objects(SwiftPerson.self)
.where { $0.firstName == name && $0.age > 8 }.subscribe(waitForSync: .onCreation)
XCTAssertEqual(results1.count, 2)
Expand All @@ -869,7 +879,7 @@
let user = try await createUser()
var config = user.flexibleSyncConfiguration()
config.objectTypes = [SwiftPerson.self]
let realm = try await Realm(configuration: config, actor: CustomGlobalActor.shared)

Check notice on line 882 in Realm/ObjectServerTests/AsyncSyncTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | sync_15.3 | Test - macOS

Realm/ObjectServerTests/AsyncSyncTests.swift#L882

Non-sendable type 'Realm' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
let results1 = try await realm.objects(SwiftPerson.self)
.where { $0.firstName == name && $0.age > 8 }.subscribe(waitForSync: .onCreation)
XCTAssertEqual(results1.count, 2)
Expand Down Expand Up @@ -1037,7 +1047,7 @@
XCTAssertEqual(realm.subscriptions.count, 1)
expectation.fulfill()
}
await fulfillment(of: [expectation], timeout: 2.0)

Check notice on line 1050 in Realm/ObjectServerTests/AsyncSyncTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | sync_15.3 | Test - macOS

Realm/ObjectServerTests/AsyncSyncTests.swift#L1050

Passing argument of non-sendable type 'XCTestCase' outside of main actor-isolated context may introduce data races
try await task.value
}

Expand Down Expand Up @@ -1084,7 +1094,7 @@
XCTAssertEqual(realm.subscriptions.count, 1)
expectation.fulfill()
}
await fulfillment(of: [expectation], timeout: 1)

Check notice on line 1097 in Realm/ObjectServerTests/AsyncSyncTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | sync_15.3 | Test - macOS

Realm/ObjectServerTests/AsyncSyncTests.swift#L1097

Passing argument of non-sendable type 'XCTestCase' outside of main actor-isolated context may introduce data races
}

@MainActor
Expand Down
70 changes: 70 additions & 0 deletions Realm/RLMInitialSubscriptionsConfiguration.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
////////////////////////////////////////////////////////////////////////////
//
// 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`
*/
@interface RLMInitialSubscriptionsConfiguration : NSObject
nirinchev marked this conversation as resolved.
Show resolved Hide resolved

/**
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No space after the colon in obj-c.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a bunch of spots with extra spaces between the colon and the argument and not just here.

(I think the historical reason behind this style is that it makes word-wrapping work much better and obj-c is prone to very long message sends that need wrapping).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry, will go over those!



/**
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
Loading