Skip to content

Commit

Permalink
Merge pull request #3717 from realm/az-prohibit-lazy
Browse files Browse the repository at this point in the history
Prohibiting non-ignored lazy properties in models
  • Loading branch information
austinzheng committed Jun 7, 2016
2 parents 49f6a0b + 2554c3c commit c3855dd
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ x.x.x Release notes (yyyy-MM-dd)
transactions have been committed.
* Fix a "Destruction of mutex in use" assertion failure after an error while
opening a file.
* Realm now throws an exception if an `Object` subclass is defined with a managed Swift `lazy` property.
Objects with ignored `lazy` properties should now work correctly.
* Update the LLDB script to work with recent changes to the implementation of `RLMResults`.

1.0.0 Release notes (2016-05-25)
Expand Down
22 changes: 22 additions & 0 deletions Realm/RLMObjectSchema.mm
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,16 @@ + (instancetype)schemaForObjectClass:(Class)objectClass {
return schema;
}

+ (nullable NSString *)baseNameForLazySwiftProperty:(NSString *)propertyName {
// A Swift lazy var shows up as two separate children on the reflection tree: one named 'x', and another that is
// optional and is named 'x.storage'. Note that '.' is illegal in either a Swift or Objective-C property name.
NSString *const storageSuffix = @".storage";
if ([propertyName hasSuffix:storageSuffix]) {
return [propertyName substringToIndex:propertyName.length - storageSuffix.length];
}
return nil;
}

+ (NSArray *)propertiesForClass:(Class)objectClass isSwift:(bool)isSwiftClass {
Class objectUtil = [objectClass objectUtilClass:isSwiftClass];
NSArray *ignoredProperties = [objectUtil ignoredPropertiesForClass:objectClass];
Expand Down Expand Up @@ -267,6 +277,18 @@ + (NSArray *)propertiesForClass:(Class)objectClass isSwift:(bool)isSwiftClass {
}
if (auto type = RLMCoerceToNil(propertyType)) {
if (existing == NSNotFound) {
// Check to see if this optional property is an underlying storage property for a Swift lazy var.
// Managed lazy vars are't allowed.
// NOTE: Revisit this once property behaviors are implemented in Swift.
if (NSString *lazyPropertyBaseName = [self baseNameForLazySwiftProperty:propertyName]) {
if ([ignoredProperties containsObject:lazyPropertyBaseName]) {
// This property is the storage property for a ignored lazy Swift property. Just continue.
return;
} else {
@throw RLMException(@"Lazy managed property '%@' is not allowed on a Realm Swift object class. Either add the property to the ignored properties list or make it non-lazy.", lazyPropertyBaseName);
}
}
// The current property isn't a storage property for a lazy Swift property.
property = [[RLMProperty alloc] initSwiftOptionalPropertyWithName:propertyName
indexed:[indexed containsObject:propertyName]
ivar:class_getInstanceVariable(objectClass, propertyName.UTF8String)
Expand Down
16 changes: 16 additions & 0 deletions Realm/Tests/Swift2.2/SwiftPropertyTypeTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,20 @@ class SwiftPropertyTypeTest: RLMTestCase {
XCTAssertEqual(obj.int32, v32)
XCTAssertEqual(obj.int64, v64)
}

func testLazyVarProperties() {
let realm = realmWithTestPath()
let succeeded : Void? = try? realm.transactionWithBlock() {
realm.addObject(SwiftLazyVarObject())
}
XCTAssertNotNil(succeeded, "Writing an NSObject-based object with an lazy property should work.")
}

func testIgnoredLazyVarProperties() {
let realm = realmWithTestPath()
let succeeded : Void? = try? realm.transactionWithBlock() {
realm.addObject(SwiftIgnoredLazyVarObject())
}
XCTAssertNotNil(succeeded, "Writing an object with an ignored lazy property should work.")
}
}
9 changes: 9 additions & 0 deletions Realm/Tests/Swift2.2/SwiftTestObjects.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,12 @@ class SwiftLinkTargetObject: RLMObject {
return ["backlinks": RLMPropertyDescriptor(withClass: SwiftLinkSourceObject.self, propertyName: "link")]
}
}

class SwiftLazyVarObject : RLMObject {
dynamic lazy var lazyProperty : String = "hello world"
}

class SwiftIgnoredLazyVarObject : RLMObject {
dynamic lazy var ignoredVar : String = "hello world"
override class func ignoredProperties() -> [String] { return ["ignoredVar"] }
}
15 changes: 15 additions & 0 deletions RealmSwift-swift2.2/Tests/ObjectSchemaInitializationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ class ObjectSchemaInitializationTests: TestCase {
"Should throw when not ignoring a property of a type we can't persist")
assertThrows(RLMObjectSchema(forObjectClass: SwiftObjectWithOptionalStringArray.self),
"Should throw when not ignoring a property of a type we can't persist")
assertThrows(RLMObjectSchema(forObjectClass: RealmObjectWithLazyVar.self),
"Should throw when not ignoring a property marked as 'dynamic lazy var'")

// Shouldn't throw when not ignoring a property of a type we can't persist if it's not dynamic
_ = RLMObjectSchema(forObjectClass: SwiftObjectWithEnum.self)
Expand All @@ -147,6 +149,10 @@ class ObjectSchemaInitializationTests: TestCase {
XCTAssertNil(schema["runtimeProperty"], "The object schema shouldn't contain ignored properties")
XCTAssertNil(schema["runtimeDefaultProperty"], "The object schema shouldn't contain ignored properties")
XCTAssertNil(schema["readOnlyProperty"], "The object schema shouldn't contain read-only properties")

assertSucceeds("Should not throw when ignoring a lazy property.") {
_ = RLMObjectSchema(forObjectClass: RealmObjectWithIgnoredLazyVar.self)
}
}

func testIndexedProperties() {
Expand Down Expand Up @@ -272,3 +278,12 @@ extension Set: RealmOptionalType { }
class SwiftObjectWithNonRealmOptionalType: SwiftFakeObject {
let set = RealmOptional<Set<Int>>()
}

class RealmObjectWithLazyVar: SwiftFakeObject {
dynamic lazy var someVar = ""
}

class RealmObjectWithIgnoredLazyVar: Object {
dynamic lazy var ignoredVar = ""
dynamic override class func ignoredProperties() -> [String] { return ["ignoredVar"] }
}

0 comments on commit c3855dd

Please sign in to comment.