diff --git a/CHANGELOG.md b/CHANGELOG.md index b39a6403f8..0ba236d9b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ x.y.z Release notes (yyyy-MM-dd) * `UserPublisher` incorrectly bounced all notifications to the main thread instead of setting up the Combine publisher to correctly receive on the main thread. ([#8132](https://github.com/realm/realm-swift/issues/8132), since 10.21.0) +* Copy EmbeddedObject for embedded object property on initializing unmanaged object ([#6921](https://github.com/realm/realm-cocoa/issues/6921). +* Copying an embedded object to an unmanaged object will not throw ([#6921](https://github.com/realm/realm-cocoa/issues/6921). + ### Compatibility diff --git a/Realm/RLMObjectBase.mm b/Realm/RLMObjectBase.mm index 7565582660..c92c8e099b 100644 --- a/Realm/RLMObjectBase.mm +++ b/Realm/RLMObjectBase.mm @@ -93,7 +93,7 @@ - (void)dealloc { } static id coerceToObjectType(id obj, Class cls, RLMSchema *schema) { - if ([obj isKindOfClass:cls]) { + if ([obj isKindOfClass:cls] && (![(id)cls isEmbedded] || ![obj realm])) { return obj; } obj = RLMBridgeSwiftValue(obj) ?: obj; @@ -537,6 +537,9 @@ id RLMObjectThaw(RLMObjectBase *obj) { } id RLMValidatedValueForProperty(id object, NSString *key, NSString *className) { + if ([object isKindOfClass:[RLMObjectBase class]] && ![[[object objectSchema] className] isEqualToString:className]) { + @throw RLMException(@"Invalid value: cannot initialize '%@' with value '%@'", className, object); + } @try { return [object valueForKey:key]; } diff --git a/Realm/Tests/DictionaryPropertyTests.m b/Realm/Tests/DictionaryPropertyTests.m index fb2e39919f..940d96d8ce 100644 --- a/Realm/Tests/DictionaryPropertyTests.m +++ b/Realm/Tests/DictionaryPropertyTests.m @@ -265,7 +265,7 @@ - (void)testUnmanaged { XCTAssertThrows([intDictionary.intObjDictionary sortedResultsUsingKeyPath:@"intCol" ascending:YES], @"Should throw on unmanaged RLMDictionary"); // test unmanaged with literals - __unused DictionaryPropertyObject *obj = [[DictionaryPropertyObject alloc] initWithValue:@[@{}, @{}, @{}, @{@"one": [[IntObject alloc] initWithValue:@[@1]]}]]; + __unused DictionaryPropertyObject *obj = [[DictionaryPropertyObject alloc] initWithValue:@[@{}, @{}, @{}, @{@"one": [[EmbeddedIntObject alloc] initWithValue:@[@1]]}, @{@"one": [[IntObject alloc] initWithValue:@[@1]]}]]; } - (void)testUnmanagedComparision { diff --git a/Realm/Tests/ObjectCreationTests.mm b/Realm/Tests/ObjectCreationTests.mm index 6e8c166aae..20bfaf637b 100644 --- a/Realm/Tests/ObjectCreationTests.mm +++ b/Realm/Tests/ObjectCreationTests.mm @@ -529,6 +529,14 @@ - (void)testInitValidatesNumberTypes { XCTAssertNoThrow(([[NumberObject alloc] initWithValue:@{@"doubleObj": @DBL_MAX}])); } +- (void)testInitEmbeddedProperty { + NSArray *failVal = @[@{}, @{}, @{}, @{@"one": [[IntObject alloc] init]}]; + XCTAssertThrows([[DictionaryPropertyObject alloc] initWithValue:failVal]); + + NSArray *passVal = @[@{}, @{}, @{}, @{@"one": [[EmbeddedIntObject alloc] init]}]; + XCTAssertNoThrow([[DictionaryPropertyObject alloc] initWithValue:passVal]); +} + #pragma mark - Create - (void)testCreateWithArray { diff --git a/RealmSwift/Tests/ObjectCreationTests.swift b/RealmSwift/Tests/ObjectCreationTests.swift index cec6af61a2..15e57f167a 100644 --- a/RealmSwift/Tests/ObjectCreationTests.swift +++ b/RealmSwift/Tests/ObjectCreationTests.swift @@ -869,6 +869,51 @@ class ObjectCreationTests: TestCase { realm.cancelWrite() } + func testCopyEmbeddedObjectFromManagedObjectInSameRealm() { + let realm = try! Realm() + try! realm.write { + let parent = realm.create(EmbeddedParentObject.self, value: [ + "object": ["value": 1], + "array": [[2]], + "map": ["some": [3]] + ]) + // Copy managed object + let copyA = EmbeddedParentObject(value: parent) + realm.add(copyA) + + XCTAssertNotEqual(parent, copyA) + XCTAssertEqual(copyA.object!.value, 1) + XCTAssertEqual(copyA.array.count, 1) + XCTAssertEqual(copyA.map.values.count, 1) + + let copyB = EmbeddedParentObject() + // Explicit copy of object + copyB.object = EmbeddedTreeObject1(value: parent.object!) + realm.add(copyB) + + XCTAssertNotEqual(parent, copyB) + XCTAssertEqual(copyB.object!.value, 1) + + let copyC = EmbeddedParentObject() + // Assign of EmbeddedObject + copyC.object = parent.object + assertThrows(realm.add(copyC), "Cannot set a link to an existing managed embedded object") + + let parentUnmanaged = EmbeddedParentObject(value: [ + "object": ["value": 4], + "array": [[5]], + "map": ["some": [6]] + ]) + // Do not copy unmanaged object + let copyD = EmbeddedParentObject(value: parentUnmanaged) + XCTAssertTrue(copyD.object === parentUnmanaged.object) + realm.add(copyD) + assertThrows(realm.add(parentUnmanaged), "Cannot set a link to an existing managed embedded object") + + realm.cancelWrite() + } + } + func testCreateEmbeddedFromManagedObjectInDifferentRealm() { let realmA = realmWithTestPath() let realmB = try! Realm() @@ -893,6 +938,14 @@ class ObjectCreationTests: TestCase { realmB.cancelWrite() } + func testInitEmbeddedProperty() { + let failVal: [Any] = [[], ["one": SwiftIntObject()]] + assertThrows(SwiftDictionaryObject(value: failVal)) + + let passVal: [Any] = [[], ["one": EmbeddedSwiftIntObject()]] + XCTAssertNoThrow(SwiftDictionaryObject(value: passVal)) + } + // test null object // test null list diff --git a/RealmSwift/Tests/SwiftTestObjects.swift b/RealmSwift/Tests/SwiftTestObjects.swift index fffc9c8fb5..f9f6ed6384 100644 --- a/RealmSwift/Tests/SwiftTestObjects.swift +++ b/RealmSwift/Tests/SwiftTestObjects.swift @@ -856,3 +856,12 @@ class EmbeddedTreeObject3: EmbeddedObject, EmbeddedTreeObject { let parent3 = LinkingObjects(fromType: EmbeddedTreeObject2.self, property: "child") let parent4 = LinkingObjects(fromType: EmbeddedTreeObject2.self, property: "children") } + +class EmbeddedSwiftIntObject: EmbeddedObject { + @objc dynamic var intCol = 0 +} + +class SwiftDictionaryObject: Object { + let intDict = Map() + let embedIntDict = Map() +} diff --git a/examples/installation/build.sh b/examples/installation/build.sh index 985257e735..eda2b5bcab 100755 --- a/examples/installation/build.sh +++ b/examples/installation/build.sh @@ -200,6 +200,7 @@ case "$COMMAND" in ;; test-*-*-carthage) + export REALM_CARTHAGE_ARM_DISABLED='YES' xctest "$PLATFORM" "$LANGUAGE" CarthageExample ;;