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-2238: Allow defining models with only computed properties #8484

Merged
merged 5 commits into from
Mar 5, 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
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.
* Lifted a limitation that would prevent declaring a model with only computed properties. ([#8414](https://github.com/realm/realm-swift/issues/8414))

### Fixed
* <How to hit and notice issue? what was the impact?> ([#????](https://github.com/realm/realm-swift/issues/????), since v?.?.?)
Expand Down
5 changes: 3 additions & 2 deletions Realm/RLMObjectSchema.mm
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,9 @@ + (instancetype)schemaForObjectClass:(Class)objectClass {

if ([objectClass shouldIncludeInDefaultSchema]
&& schema.isSwiftClass
&& schema.properties.count == 0) {
@throw RLMException(@"No properties are defined for '%@'. Did you remember to mark them with '@objc' in your model?", schema.className);
&& schema.properties.count == 0
dianaafanador3 marked this conversation as resolved.
Show resolved Hide resolved
&& schema.computedProperties.count == 0) {
@throw RLMException(@"No properties are defined for '%@'. Did you remember to mark them with '@objc' or '@Persisted' in your model?", schema.className);
}
return schema;
}
Expand Down
39 changes: 38 additions & 1 deletion Realm/Tests/Swift/SwiftSchemaTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,24 @@ class NoProps: FakeObject {
// no @objc properties
}

class OnlyComputedSource: RLMObject {
@objc dynamic var link: OnlyComputedTarget?
}

class OnlyComputedTarget: RLMObject {
@objc dynamic var backlinks: RLMLinkingObjects<OnlyComputedSource>?

override class func linkingObjectsProperties() -> [String : RLMPropertyDescriptor] {
return ["backlinks": RLMPropertyDescriptor(with: OnlyComputedSource.self, propertyName: "link")]
}
}

class OnlyComputedNoBacklinksProps: FakeObject {
var computedProperty: String {
return "Test_String"
}
}

@MainActor
class RequiresObjcName: RLMObject {
static var enable = false
Expand Down Expand Up @@ -170,7 +188,26 @@ class SwiftRLMSchemaTests: RLMMultiProcessTestCase {

func testShouldRaiseObjectWithoutProperties() {
assertThrowsWithReasonMatching(RLMObjectSchema(forObjectClass: NoProps.self),
"No properties are defined for 'NoProps'. Did you remember to mark them with '@objc' in your model?")
"No properties are defined for 'NoProps'. Did you remember to mark them with '@objc' or '@Persisted' in your model?")
}

func testShouldNotThrowForObjectWithOnlyBacklinksProps() {
let config = RLMRealmConfiguration.default()
config.objectClasses = [OnlyComputedTarget.self, OnlyComputedSource.self]
config.inMemoryIdentifier = #function
let r = try! RLMRealm(configuration: config)
try! r.transaction {
_ = OnlyComputedTarget.create(in: r, withValue: [])
}

let schema = OnlyComputedTarget().objectSchema
XCTAssertEqual(schema.computedProperties.count, 1)
XCTAssertEqual(schema.properties.count, 0)
}

func testShouldThrowForObjectWithOnlyComputedNoBacklinksProps() {
assertThrowsWithReasonMatching(RLMObjectSchema(forObjectClass: OnlyComputedNoBacklinksProps.self),
"No properties are defined for 'OnlyComputedNoBacklinksProps'. Did you remember to mark them with '@objc' or '@Persisted' in your model?")
}

func testSchemaInitWithLinkedToObjectUsingInitWithValue() {
Expand Down
40 changes: 40 additions & 0 deletions RealmSwift/Tests/SwiftLinkTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,44 @@ class SwiftLinkTests: TestCase {

XCTAssertEqual(0, owners.count)
}

func testLinkingObjectsWithNoPersistedProps() {
let realm = realmWithTestPath()

let target = OnlyComputedProps()

let source1 = LinkToOnlyComputed()
source1.value = 1
source1.link = target

XCTAssertEqual(target.backlinks.count, 0, "Linking objects are not available until the object is persisted")

try! realm.write {
realm.add(source1)
}

XCTAssertEqual(target.backlinks.count, 1)
XCTAssertEqual(target.backlinks.first!.value, source1.value)

let source2 = LinkToOnlyComputed()
source2.value = 2
source2.link = target

XCTAssertEqual(target.backlinks.count, 1, "Linking objects to an unpersisted object are not available")
try! realm.write {
realm.add(source2)
}

XCTAssertEqual(target.backlinks.count, 2)
XCTAssertTrue(target.backlinks.contains(where: { $0.value == 2 }))

let targetWithNoLinks = OnlyComputedProps()
try! realm.write {
// Implicitly verify we can persist a RealmObject with no persisted properties and
// no objects linking to it
realm.add(targetWithNoLinks)
}

XCTAssertEqual(targetWithNoLinks.backlinks.count, 0, "No object is linking to targetWithNoLinks")
}
}
9 changes: 9 additions & 0 deletions RealmSwift/Tests/SwiftTestObjects.swift
Original file line number Diff line number Diff line change
Expand Up @@ -868,3 +868,12 @@ class ObjectWithNestedEmbeddedObject: Object {
private class PrivateObjectSubclass: Object {
@objc dynamic var value = 0
}

class LinkToOnlyComputed: Object {
@Persisted var value: Int = 0
@Persisted var link: OnlyComputedProps?
}

class OnlyComputedProps: Object {
@Persisted(originProperty: "link") var backlinks: LinkingObjects<LinkToOnlyComputed>
}
Loading