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

Allow opening synced realm in-memory. #8421

Merged
merged 2 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ x.y.z Release notes (yyyy-MM-dd)
* The `baseURL` field of `AppConfiguration` can now be updated, rather than the
value being persisted between runs of the application in the metadata
storage. ([Core #7201](https://github.com/realm/realm-core/issues/7201))
* Allow in-memory synced Realms. This will allow setting an in-memory identifier on
a flexible sync realm.

### Fixed
* `@Persisted`'s Encodable implementation did not allow the encoder to
Expand Down Expand Up @@ -9393,7 +9395,7 @@ Prebuilt frameworks are now built with Xcode 7.1.
the properties in a `RLMObject` subclass.
* Fix crash on IN query with several thousand items.
* Fix crash when querying indexed `NSString` properties.
* Fixed an issue which prevented in-memory Realms from being used accross multiple threads.
* Fixed an issue which prevented in-memory Realms from being used across multiple threads.
* Preserve the sort order when querying a sorted `RLMResults`.
* Fixed an issue with migrations where if a Realm file is deleted after a Realm is initialized,
the newly created Realm can be initialized with an incorrect schema version.
Expand Down
46 changes: 46 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.2 | 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.2 | 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.2 | 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 @@ -514,6 +514,52 @@
checkCount(expected: 10, realm, SwiftPerson.self)
}

@MainActor
func testFlexibleSyncInitInMemory() async throws {
try await populateSwiftPerson(5)

let user = try await createUser()
try await Task {
var config = user.flexibleSyncConfiguration(initialSubscriptions: { subs in
subs.append(QuerySubscription<SwiftPerson> {
$0.age > 0 && $0.firstName == self.name
})
})
config.objectTypes = [SwiftPerson.self]
config.inMemoryIdentifier = "identifier"
let inMemoryRealm = try await Realm(configuration: config, downloadBeforeOpen: .always)
XCTAssertEqual(inMemoryRealm.objects(SwiftPerson.self).count, 5)
try! inMemoryRealm.write {
let person = SwiftPerson(firstName: self.name,
lastName: "lastname_10",
age: 10)
inMemoryRealm.add(person)
}
XCTAssertEqual(inMemoryRealm.objects(SwiftPerson.self).count, 6)
try await inMemoryRealm.syncSession?.wait(for: .upload)
}.value

var config = user.flexibleSyncConfiguration(initialSubscriptions: { subs in
subs.append(QuerySubscription<SwiftPerson> {
$0.age > 5 && $0.firstName == self.name
})
})
config.objectTypes = [SwiftPerson.self]
config.inMemoryIdentifier = "identifier"
let inMemoryRealm = try await Realm(configuration: config, downloadBeforeOpen: .always)
XCTAssertEqual(inMemoryRealm.objects(SwiftPerson.self).count, 1)

var config2 = user.flexibleSyncConfiguration(initialSubscriptions: { subs in
subs.append(QuerySubscription<SwiftPerson> {
$0.age > 0 && $0.firstName == self.name
})
})
config2.objectTypes = [SwiftPerson.self]
config2.inMemoryIdentifier = "identifier2"
let inMemoryRealm2 = try await Realm(configuration: config2, downloadBeforeOpen: .always)
XCTAssertEqual(inMemoryRealm2.objects(SwiftPerson.self).count, 6)
}

@MainActor
func testStates() async throws {
let realm = try await openRealm()
Expand Down Expand Up @@ -803,7 +849,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 852 in Realm/ObjectServerTests/AsyncSyncTests.swift

View check run for this annotation

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

Realm/ObjectServerTests/AsyncSyncTests.swift#L852

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 @@ -823,7 +869,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 872 in Realm/ObjectServerTests/AsyncSyncTests.swift

View check run for this annotation

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

Realm/ObjectServerTests/AsyncSyncTests.swift#L872

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
4 changes: 2 additions & 2 deletions Realm/RLMRealmConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ typedef void(^RLMFlexibleSyncInitialSubscriptionsBlock)(RLMSyncSubscriptionSet *
@property (nonatomic, copy, nullable) NSURL *fileURL;

/// A string used to identify a particular in-memory Realm. Mutually exclusive with `fileURL`,
/// `seedFilePath`and `syncConfiguration`;
/// setting any one of the three properties will automatically nil out the other two.
/// `seedFilePath`.
/// setting an in-memory identifier will automatically nil out the other two.
@property (nonatomic, copy, nullable) NSString *inMemoryIdentifier;

/// A 64-byte key to use to encrypt the data, or `nil` if encryption is not enabled.
Expand Down
2 changes: 0 additions & 2 deletions Realm/RLMRealmConfiguration.mm
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,6 @@ - (void)setInMemoryIdentifier:(NSString *)inMemoryIdentifier {
if (inMemoryIdentifier.length == 0) {
@throw RLMException(@"In-memory identifier must not be empty");
}
_config.sync_config = nullptr;
_seedFilePath = nil;

RLMNSStringToStdString(_config.path, [NSTemporaryDirectory() stringByAppendingPathComponent:inMemoryIdentifier]);
Expand Down Expand Up @@ -367,7 +366,6 @@ - (void)setSyncConfiguration:(RLMSyncConfiguration *)syncConfiguration {
}

NSAssert(user.identifier, @"Cannot call this method on a user that doesn't have an identifier.");
_config.in_memory = false;
_config.sync_config = std::make_shared<realm::SyncConfig>(syncConfiguration.rawConfiguration);
_config.path = syncConfiguration.path;

Expand Down
11 changes: 4 additions & 7 deletions RealmSwift/RealmConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ extension Realm {
/**
Creates a `Configuration` which can be used to create new `Realm` instances.

- note: The `fileURL`, `inMemoryIdentifier`, and `syncConfiguration` parameters are mutually exclusive. Only
- note: The `fileURL`, and `inMemoryIdentifier`, parameters are mutually exclusive. Only
set one of them, or none if you wish to use the default file URL.
Synced Realms will set a unique file path unless is an in-memory realm.

- parameter fileURL: The local URL to the Realm file.
- parameter inMemoryIdentifier: A string used to identify a particular in-memory Realm.
Expand Down Expand Up @@ -106,15 +107,13 @@ extension Realm {
// MARK: Configuration Properties

/**
A configuration value used to configure a Realm for synchronization with Atlas App Services. Mutually
exclusive with `inMemoryIdentifier`.
A configuration value used to configure a Realm for synchronization with Atlas App Services.
*/
public var syncConfiguration: SyncConfiguration? {
get {
return _syncConfiguration
}
set {
_inMemoryIdentifier = nil
_syncConfiguration = newValue
}
}
Expand All @@ -128,15 +127,13 @@ extension Realm {
}
}

/// A string used to identify a particular in-memory Realm. Mutually exclusive with `fileURL` and
/// `syncConfiguration`.
/// A string used to identify a particular in-memory Realm. Mutually exclusive with `fileURL`.
public var inMemoryIdentifier: String? {
get {
return _inMemoryIdentifier
}
set {
fileURL = nil
_syncConfiguration = nil
_inMemoryIdentifier = newValue
}
}
Expand Down