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 creating notifiers inside write transactions before the first change #8439

Merged
merged 1 commit into from
Dec 19, 2023
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
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
x.y.z Release notes (yyyy-MM-dd)
=============================================================
### Enhancements
* None.

### Fixed
* Registering new notifications inside write transactions before actually
making any changes is now actually allowed. This was supposed to be allowed
in 10.39.1, but it did not actually work due to some redundant validation.

### Compatibility
* Realm Studio: 14.0.1 or later.
* APIs are backwards compatible with all previous releases in the 10.x.y series.
* Carthage release for Swift is built with Xcode 15.1.0.
* CocoaPods: 1.10 or later.
* Xcode: 14.1-15.1.0.

### Internal
* Upgraded realm-core from ? to ?

10.45.1 Release notes (2023-12-18)
=============================================================

Expand Down
7 changes: 6 additions & 1 deletion Realm/RLMCollection.mm
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,12 @@ - (bool)invalidate {
RLMClassInfo *info = collection.objectInfo;
if (!queue) {
[realm verifyNotificationsAreSupported:true];
token->_token = [collection addNotificationCallback:block keyPaths:info->keyPathArrayFromStringArray(keyPaths)];
try {
token->_token = [collection addNotificationCallback:block keyPaths:info->keyPathArrayFromStringArray(keyPaths)];
}
catch (const realm::Exception& e) {
@throw RLMException(e);
}
return token;
}

Expand Down
9 changes: 7 additions & 2 deletions Realm/RLMObjectBase.mm
Original file line number Diff line number Diff line change
Expand Up @@ -752,8 +752,13 @@ - (void)observe:(RLMObjectBase *)obj
completion();
}
};
_token = _object.add_notification_callback(ObjectChangeCallbackWrapper{block, obj, completion},
obj->_info->keyPathArrayFromStringArray(keyPaths));
try {
_token = _object.add_notification_callback(ObjectChangeCallbackWrapper{block, obj, completion},
obj->_info->keyPathArrayFromStringArray(keyPaths));
}
catch (const realm::Exception& e) {
@throw RLMException(e);
}
}

- (void)registrationComplete:(void (^)())completion {
Expand Down
3 changes: 0 additions & 3 deletions Realm/RLMRealm.mm
Original file line number Diff line number Diff line change
Expand Up @@ -550,9 +550,6 @@ - (void)verifyNotificationsAreSupported:(bool)isCollection {
if (_realm->config().automatic_change_notifications && !_realm->can_deliver_notifications()) {
@throw RLMException(@"Can only add notification blocks from within runloops.");
}
if (isCollection && _realm->is_in_transaction()) {
@throw RLMException(@"Cannot register notification blocks from within write transactions.");
}
}

- (RLMNotificationToken *)addNotificationBlock:(RLMNotificationBlock)block {
Expand Down
14 changes: 9 additions & 5 deletions Realm/Tests/AsyncTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -859,11 +859,15 @@ - (void)testAsyncNotSupportedForReadOnlyRealms {
}]);
}

- (void)testAsyncNotSupportedInWriteTransactions {
[RLMRealm.defaultRealm transactionWithBlock:^{
XCTAssertThrows([IntObject.allObjects addNotificationBlock:^(RLMResults *results, RLMCollectionChange *change, NSError *error) {
XCTFail(@"should not be called");
}]);
- (void)testAsyncNotSupportedAfterMakingChangesInWriteTransactions {
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
XCTAssertNoThrow([IntObject.allObjects addNotificationBlock:^(RLMResults *, RLMCollectionChange *, NSError *) {}]);
[IntObject createInRealm:realm withValue:@[@0]];
RLMAssertThrowsWithReason([IntObject.allObjects addNotificationBlock:^(RLMResults *, RLMCollectionChange *, NSError *) {}],
@"Cannot create asynchronous query after making changes in a write transaction.");
RLMAssertThrowsWithReason([IntObject.allObjects[0] addNotificationBlock:^(BOOL, NSArray *, NSError *) {}],
@"Cannot create asynchronous query after making changes in a write transaction.");
}];
}

Expand Down
2 changes: 0 additions & 2 deletions RealmSwift/Tests/ObjectTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import XCTest
import Realm
import RealmSwift

Check notice on line 21 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift_14.3.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L21

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

Check notice on line 21 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | tvos-swift_15.1 | Test - tvOS

RealmSwift/Tests/ObjectTests.swift#L21

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

Check notice on line 21 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L21

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

Check notice on line 21 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift_15.0.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L21

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

Check notice on line 21 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | tvos-swift-evolution_15.1 | Test - tvOS

RealmSwift/Tests/ObjectTests.swift#L21

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

Check notice on line 21 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | ios-swift-evolution_15.1 | Test - iOS

RealmSwift/Tests/ObjectTests.swift#L21

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

Check notice on line 21 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift-evolution_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L21

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

Check notice on line 21 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | ios-swift_15.1 | Test - iOS

RealmSwift/Tests/ObjectTests.swift#L21

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

Check notice on line 21 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | catalyst-swift_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L21

Add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'RealmSwift'
import Foundation
import os.lock

Expand Down Expand Up @@ -1580,7 +1580,6 @@

// This test consistently crashes inside the Swift runtime when building
// with SPM.
#if !SWIFT_PACKAGE
@MainActor
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
func testAsyncSequenceObserve() async throws {
Expand All @@ -1588,7 +1587,7 @@
let ex = Locked(expectation(description: "got value"))
let task = Task { @MainActor in
var value = 0
for try await object in valuePublisher(obj).values {

Check notice on line 1590 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | tvos-swift_15.1 | Test - tvOS

RealmSwift/Tests/ObjectTests.swift#L1590

Non-sendable type 'RealmPublishers.Value<SwiftObject>.Output?' (aka 'Optional<SwiftObject>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1590 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1590

Non-sendable type 'RealmPublishers.Value<SwiftObject>.Output?' (aka 'Optional<SwiftObject>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1590 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift_15.0.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1590

Non-sendable type 'RealmPublishers.Value<SwiftObject>.Output?' (aka 'Optional<SwiftObject>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1590 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | tvos-swift-evolution_15.1 | Test - tvOS

RealmSwift/Tests/ObjectTests.swift#L1590

Non-sendable type 'RealmPublishers.Value<SwiftObject>.Output?' (aka 'Optional<SwiftObject>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1590 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | ios-swift-evolution_15.1 | Test - iOS

RealmSwift/Tests/ObjectTests.swift#L1590

Non-sendable type 'RealmPublishers.Value<SwiftObject>.Output?' (aka 'Optional<SwiftObject>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1590 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift-evolution_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1590

Non-sendable type 'RealmPublishers.Value<SwiftObject>.Output?' (aka 'Optional<SwiftObject>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1590 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | ios-swift_15.1 | Test - iOS

RealmSwift/Tests/ObjectTests.swift#L1590

Non-sendable type 'RealmPublishers.Value<SwiftObject>.Output?' (aka 'Optional<SwiftObject>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1590 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | catalyst-swift_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1590

Non-sendable type 'RealmPublishers.Value<SwiftObject>.Output?' (aka 'Optional<SwiftObject>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
XCTAssertIdentical(object, obj)
value += 1
XCTAssertEqual(object.intCol, value)
Expand All @@ -1608,7 +1607,6 @@
task.cancel()
_ = try await task.value
}
#endif

@MainActor
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
Expand All @@ -1616,10 +1614,10 @@
let obj = try createObject()
let ex = Locked(expectation(description: "got next"))
let task = Task { @CustomGlobalActor in
let realm = try await Realm(actor: CustomGlobalActor.shared)

Check notice on line 1617 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | tvos-swift_15.1 | Test - tvOS

RealmSwift/Tests/ObjectTests.swift#L1617

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

Check notice on line 1617 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1617

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

Check notice on line 1617 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift_15.0.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1617

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

Check notice on line 1617 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | tvos-swift-evolution_15.1 | Test - tvOS

RealmSwift/Tests/ObjectTests.swift#L1617

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

Check notice on line 1617 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | ios-swift-evolution_15.1 | Test - iOS

RealmSwift/Tests/ObjectTests.swift#L1617

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

Check notice on line 1617 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift-evolution_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1617

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

Check notice on line 1617 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | ios-swift_15.1 | Test - iOS

RealmSwift/Tests/ObjectTests.swift#L1617

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

Check notice on line 1617 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | catalyst-swift_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1617

Non-sendable type 'Realm' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
let obj = realm.objects(SwiftObject.self).first!
var value = 0
for try await change in changesetPublisher(obj).values {

Check notice on line 1620 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | tvos-swift_15.1 | Test - tvOS

RealmSwift/Tests/ObjectTests.swift#L1620

Non-sendable type 'RealmPublishers.ObjectChangeset<SwiftObject>.Output?' (aka 'Optional<ObjectChange<SwiftObject>>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1620 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1620

Non-sendable type 'RealmPublishers.ObjectChangeset<SwiftObject>.Output?' (aka 'Optional<ObjectChange<SwiftObject>>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1620 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift_15.0.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1620

Non-sendable type 'RealmPublishers.ObjectChangeset<SwiftObject>.Output?' (aka 'Optional<ObjectChange<SwiftObject>>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1620 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | tvos-swift-evolution_15.1 | Test - tvOS

RealmSwift/Tests/ObjectTests.swift#L1620

Non-sendable type 'RealmPublishers.ObjectChangeset<SwiftObject>.Output?' (aka 'Optional<ObjectChange<SwiftObject>>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1620 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | ios-swift-evolution_15.1 | Test - iOS

RealmSwift/Tests/ObjectTests.swift#L1620

Non-sendable type 'RealmPublishers.ObjectChangeset<SwiftObject>.Output?' (aka 'Optional<ObjectChange<SwiftObject>>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1620 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift-evolution_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1620

Non-sendable type 'RealmPublishers.ObjectChangeset<SwiftObject>.Output?' (aka 'Optional<ObjectChange<SwiftObject>>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1620 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | ios-swift_15.1 | Test - iOS

RealmSwift/Tests/ObjectTests.swift#L1620

Non-sendable type 'RealmPublishers.ObjectChangeset<SwiftObject>.Output?' (aka 'Optional<ObjectChange<SwiftObject>>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary

Check notice on line 1620 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | catalyst-swift_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1620

Non-sendable type 'RealmPublishers.ObjectChangeset<SwiftObject>.Output?' (aka 'Optional<ObjectChange<SwiftObject>>') returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
guard case let .change(object, props) = change else {
return XCTFail("Expected .change, got \(change)")
}
Expand Down Expand Up @@ -1690,7 +1688,7 @@
group.addTask { @CustomGlobalActor in
waitingForRealm.withLock { $0 += 1 }
// can throw due to cancellation
guard let realm = try? await Realm(actor: CustomGlobalActor.shared) else {

Check notice on line 1691 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | tvos-swift_15.1 | Test - tvOS

RealmSwift/Tests/ObjectTests.swift#L1691

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

Check notice on line 1691 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1691

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

Check notice on line 1691 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift_15.0.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1691

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

Check notice on line 1691 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | tvos-swift-evolution_15.1 | Test - tvOS

RealmSwift/Tests/ObjectTests.swift#L1691

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

Check notice on line 1691 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | ios-swift-evolution_15.1 | Test - iOS

RealmSwift/Tests/ObjectTests.swift#L1691

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

Check notice on line 1691 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | osx-swift-evolution_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1691

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

Check notice on line 1691 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | ios-swift_15.1 | Test - iOS

RealmSwift/Tests/ObjectTests.swift#L1691

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

Check notice on line 1691 in RealmSwift/Tests/ObjectTests.swift

View check run for this annotation

Xcode Cloud / RealmSwift | catalyst-swift_15.1 | Test - macOS

RealmSwift/Tests/ObjectTests.swift#L1691

Non-sendable type 'Realm' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
waitingForRealm.withLock { $0 -= 1 }
return NotificationToken()
}
Expand Down