Skip to content

Commit

Permalink
[primary-key-fix] Fixed crashing when using a primary key. Added a lo…
Browse files Browse the repository at this point in the history
…cal cache for mapped objects.
  • Loading branch information
rexmas committed Feb 13, 2016
1 parent 51e3c34 commit da6ef52
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 2 deletions.
4 changes: 4 additions & 0 deletions RealmCrust.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
173E0D821C6C27CB00A9E9B3 /* PrimaryKeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 173E0D811C6C27CB00A9E9B3 /* PrimaryKeyTests.swift */; };
175C9A611C36201F00B0D6E7 /* RealmCrust.h in Headers */ = {isa = PBXBuildFile; fileRef = 175C9A601C36201F00B0D6E7 /* RealmCrust.h */; settings = {ATTRIBUTES = (Public, ); }; };
175C9A681C36201F00B0D6E7 /* RealmCrust.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 175C9A5D1C36201F00B0D6E7 /* RealmCrust.framework */; };
175C9A781C36247700B0D6E7 /* RealmMappings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 175C9A771C36247700B0D6E7 /* RealmMappings.swift */; };
Expand Down Expand Up @@ -34,6 +35,7 @@

/* Begin PBXFileReference section */
0E234D4DFBBC77280B5A7F0C /* Pods-RealmCrustTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RealmCrustTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RealmCrustTests/Pods-RealmCrustTests.debug.xcconfig"; sourceTree = "<group>"; };
173E0D811C6C27CB00A9E9B3 /* PrimaryKeyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrimaryKeyTests.swift; sourceTree = "<group>"; };
175C9A5D1C36201F00B0D6E7 /* RealmCrust.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RealmCrust.framework; sourceTree = BUILT_PRODUCTS_DIR; };
175C9A601C36201F00B0D6E7 /* RealmCrust.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RealmCrust.h; sourceTree = "<group>"; };
175C9A621C36201F00B0D6E7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -117,6 +119,7 @@
175C9A7E1C38547700B0D6E7 /* EmployeeMappingTests.swift */,
175C9A7F1C38547700B0D6E7 /* EmployeeStub.swift */,
175C9A871C38548000B0D6E7 /* Operators.swift */,
173E0D811C6C27CB00A9E9B3 /* PrimaryKeyTests.swift */,
175C9A891C38548000B0D6E7 /* RealmMappingTest.swift */,
175C9A6E1C36201F00B0D6E7 /* Info.plist */,
);
Expand Down Expand Up @@ -346,6 +349,7 @@
175C9A8E1C38548000B0D6E7 /* RealmMappingTest.swift in Sources */,
175C9A841C38547700B0D6E7 /* Employee.swift in Sources */,
175C9A821C38547700B0D6E7 /* CompanyStub.swift in Sources */,
173E0D821C6C27CB00A9E9B3 /* PrimaryKeyTests.swift in Sources */,
175C9A861C38547700B0D6E7 /* EmployeeStub.swift in Sources */,
175C9A811C38547700B0D6E7 /* CompanyMappingTests.swift in Sources */,
175C9A851C38547700B0D6E7 /* EmployeeMappingTests.swift in Sources */,
Expand Down
16 changes: 14 additions & 2 deletions RealmCrust/RealmMappings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import RealmSwift
public class RealmAdaptor {

public typealias BaseType = Object
public typealias ResultsType = Results<Object>
public typealias ResultsType = Set<BaseType>

var realm: Realm
var cache: NSMutableSet

public init(realm: Realm) {
self.realm = realm
self.cache = []
}

public convenience init() throws {
Expand All @@ -22,22 +24,26 @@ public class RealmAdaptor {

public func mappingEnded() throws {
try self.realm.commitWrite()
self.cache.removeAllObjects()
}

public func mappingErrored(error: ErrorType) {
if self.realm.inWriteTransaction {
self.realm.cancelWrite()
}
self.cache.removeAllObjects()
}

public func createObject(objType: BaseType.Type) throws -> BaseType {
let obj = objType.init()
self.cache.addObject(obj)
return obj
}

public func saveObjects(objects: [BaseType]) throws {
let saveBlock = {
for obj in objects {
self.cache.removeObject(obj)
self.realm.add(objects, update: obj.dynamicType.primaryKey() != nil)
}
}
Expand All @@ -50,6 +56,7 @@ public class RealmAdaptor {

public func deleteObject(obj: BaseType) throws {
let deleteBlock = {
self.cache.removeObject(obj)
self.realm.delete(obj)
}
if self.realm.inWriteTransaction {
Expand All @@ -74,12 +81,17 @@ public class RealmAdaptor {

public func fetchObjectsWithType(type: BaseType.Type, predicate: NSPredicate) -> ResultsType? {

let objects = self.cache.filteredSetUsingPredicate(predicate)
if objects.count > 0 {
return objects as? ResultsType
}

if type.primaryKey() != nil {
// We're going to build an unstored object and update when saving based on the primary key.
return nil
}

return realm.objects(type).filter(predicate)
return Set(realm.objects(type).filter(predicate))
}
}

Expand Down
112 changes: 112 additions & 0 deletions RealmCrustTests/PrimaryKeyTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import XCTest
import Crust
import JSONValueRX
import RealmCrust
import RealmSwift

class PrimaryObj1: Object {
let class2s = List<PrimaryObj2>()
dynamic var uuid: String = ""

override class func primaryKey() -> String? {
return "uuid"
}
}

class PrimaryObj2: Object {
dynamic var uuid: String = ""
dynamic var class1: PrimaryObj1?

override class func primaryKey() -> String? {
return "uuid"
}
}

class PrimaryObj1Mapping : RealmMapping {

var adaptor: RealmAdaptor
var primaryKeys: Dictionary<String, CRMappingKey>? {
return [ "uuid" : "data.uuid" ]
}

required init(adaptor: RealmAdaptor) {
self.adaptor = adaptor
}

func mapping(inout tomap: PrimaryObj1, context: MappingContext) {
let obj2Mapping = PrimaryObj2Mapping(adaptor: self.adaptor)

tomap.class2s <- KeyExtensions.Mapping("class2s", obj2Mapping) >*<
tomap.uuid <- "data.uuid" >*<
context
}
}

// Until we support optional mappings, have to make a nested version.
class NestedPrimaryObj1Mapping : RealmMapping {

var adaptor: RealmAdaptor
var primaryKeys: Dictionary<String, CRMappingKey>? {
return [ "uuid" : "data.uuid" ]
}

required init(adaptor: RealmAdaptor) {
self.adaptor = adaptor
}

func mapping(inout tomap: PrimaryObj1, context: MappingContext) {
tomap.uuid <- "data.uuid" >*<
context
}
}

class PrimaryObj2Mapping : RealmMapping {

var adaptor: RealmAdaptor
var primaryKeys: Dictionary<String, CRMappingKey>? {
return [ "uuid" : "data.more_data.uuid" ]
}

required init(adaptor: RealmAdaptor) {
self.adaptor = adaptor
}

func mapping(inout tomap: PrimaryObj2, context: MappingContext) {
// TODO: Including this mapping fails. Need to support making some mappings as optional
// so when the recursive cycle of json between these two relationships runs out it doesn't error
// from expecting json.
//
// In the meantime, user can write separate Nested Mappings for the top level object and nested objects.
// let obj1Mapping = PrimaryObj1Mapping(adaptor: self.adaptor)

let obj1Mapping = NestedPrimaryObj1Mapping(adaptor: self.adaptor)

tomap.class1 <- KeyExtensions.Mapping("class1", obj1Mapping) >*<
tomap.uuid <- "data.more_data.uuid" >*<
context
}
}

class PrimaryKeyTests: RealmMappingTest {

func testMappingsWithPrimaryKeys() {

var json1Dict = [ "data" : [ "uuid" : "primary1" ] ] as [ String : AnyObject ]
let json2Dict1 = [ "data.more_data.uuid" : "primary2.1", "class1" : json1Dict ]
let json2Dict2 = [ "data.more_data.uuid" : "primary2.2", "class1" : json1Dict ]

json1Dict["class2s"] = [ json2Dict1, json2Dict2 ]

XCTAssertEqual(realm!.objects(PrimaryObj1).count, 0)
XCTAssertEqual(realm!.objects(PrimaryObj2).count, 0)

let json = try! JSONValue(object: json1Dict)
let mapper = CRMapper<PrimaryObj1, PrimaryObj1Mapping>()
let object = try! mapper.mapFromJSONToExistingObject(json, mapping: PrimaryObj1Mapping(adaptor: adaptor!))

XCTAssertEqual(realm!.objects(PrimaryObj1).count, 1)
XCTAssertEqual(realm!.objects(PrimaryObj2).count, 2)
XCTAssertEqual(object.uuid, "primary1")
XCTAssertEqual(object.class2s.count, 2)
}
}

0 comments on commit da6ef52

Please sign in to comment.