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

Fixed boxing of optional collections #12

Merged
merged 3 commits into from Oct 17, 2016
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion CoreValue.xcodeproj/project.pbxproj
Expand Up @@ -157,8 +157,8 @@
4D59125A1B4989FB0022506E /* CoreValue */ = {
isa = PBXGroup;
children = (
4D5912721B498A170022506E /* CoreValue.swift */,
4D6AEB981B4DEAE100DDD5E1 /* curry.swift */,
4D5912721B498A170022506E /* CoreValue.swift */,
4D391F291B5404FE00C66C90 /* Frameworks */,
);
path = CoreValue;
Expand Down
51 changes: 33 additions & 18 deletions CoreValue/CoreValue.swift
Expand Up @@ -642,26 +642,22 @@ private func internalToObject<T: BoxingStruct>(context: NSManagedObjectContext?,
result.setValue(nil, forKey: label)
// Optional with Value
case (.Optional?, let child?):
result.setValue(child.value as? AnyObject, forKey: label)
// A collection of objects
case (.Collection?, _):
var objects: [NSManagedObject] = []
for (_, value) in valueMirror.children {
if let boxedValue = value as? BoxingStruct {
objects.append(try boxedValue.toObject(context))
}
}

let orderedSet = NSOrderedSet(array: objects)
let optionalMirror: Mirror = Mirror(reflecting: child.value)

let mutableValue = result.mutableOrderedSetValueForKey(label)
if objects.count == 0 {
mutableValue.removeAllObjects()
} else {
mutableValue.intersectOrderedSet(orderedSet) // removes objects that are not in new array
mutableValue.unionOrderedSet(orderedSet) // adds new objects
switch (optionalMirror.displayStyle, optionalMirror.children.first) {
case (.Collection?, _):
try internalCollectionToSet(context, result: result, label: label, mirror: optionalMirror)
default:
if let value = child.value as? Boxing {
try value.box(result, withKey: label)
}else {
result.setValue(child.value as? AnyObject, forKey: label)
}
break
}

// A collection of objects
case (.Collection?, _):
try internalCollectionToSet(context, result: result, label: label, mirror: valueMirror)
default:
// If we end up here, we were unable to decode it
throw CVManagedStructError.StructValueError(message: "Could not decode value for field '\(label)' obj \(valueMaybe)")
Expand All @@ -674,3 +670,22 @@ private func internalToObject<T: BoxingStruct>(context: NSManagedObjectContext?,
throw CVManagedStructError.StructConversionError(message: "Object is not a struct: \(entity)")
}

private func internalCollectionToSet(context: NSManagedObjectContext?, result: NSManagedObject, label: String, mirror: Mirror) throws {
var objects: [NSManagedObject] = []
for (_, value) in mirror.children {
if let boxedValue = value as? BoxingStruct {
objects.append(try boxedValue.toObject(context))
}
}

let orderedSet = NSOrderedSet(array: objects)

let mutableValue = result.mutableOrderedSetValueForKey(label)
if objects.count == 0 {
mutableValue.removeAllObjects()
} else {
mutableValue.intersectOrderedSet(orderedSet) // removes objects that are not in new array
mutableValue.unionOrderedSet(orderedSet) // adds new objects
}
}

40 changes: 39 additions & 1 deletion CoreValueMacTests/CoreValueTests.swift
Expand Up @@ -58,11 +58,26 @@ struct Shop: CVManagedStruct {

var name: String
var owner: Employee
var products: Array<Product>?

static func fromObject(o: NSManagedObject) throws -> Shop {
return try curry(self.init)
<^> o <| "name"
<^> o <| "owner"
<^> o <|| "products"
}
}

struct Product: CVManagedStruct {
static let EntityName = "Product"

var name: String
var color: String

static func fromObject(o: NSManagedObject) throws -> Product {
return try curry(self.init)
<^> o <| "name"
<^> o <| "color"
}
}

Expand Down Expand Up @@ -200,7 +215,7 @@ class CoreValueMacTests: XCTestCase {
var nsEmployee2: NSManagedObject!

let shop = {
return Shop(name: "Carl's Household Items", owner: Employee(name: "Carl", age: 66, position: nil, department: "Register", job: "Owner"))
return Shop(name: "Carl's Household Items", owner: Employee(name: "Carl", age: 66, position: nil, department: "Register", job: "Owner"), products: nil)
}()

var nsShop: NSManagedObject!
Expand Down Expand Up @@ -460,6 +475,29 @@ class CoreValueMacTests: XCTestCase {
XCTAssert(false, "\(e)")
}
}

func testOptionalCollectionToCoreData() {
// create two shops
let s1 = Shop(name: "shop_with_products1", owner: Employee(name: "a", age: 4, position: nil, department: "", job: ""), products: [Product(name: "Pancake", color:"golden")])
do {
try s1.toObject(self.context)
} catch let e {
XCTAssert(false, "\(e)")
}

let s2 = Shop(name: "shop_with_products2", owner: Employee(name: "a", age: 4, position: nil, department: "", job: ""), products: [Product(name: "Unicorn", color: "sparkling")])
testTry {
try s2.toObject(self.context)
}

// And query the count
testTry {
let predicate = NSPredicate(format: "self.name=='shop_with_products1'", argumentArray: [])
let results: [Shop] = try Shop.query(self.context, predicate: predicate)
XCTAssert(results.count == 1, "Wrong amount of objects, update did insert: \(results.count)")
XCTAssert(results.first?.products?.count == 1, "Wrong amount of products, actual amount: \(results.first?.products?.count ?? 0)")
}
}
}


Expand Down
11 changes: 9 additions & 2 deletions CoreValueMacTests/CoreValueTests.xcdatamodel/contents
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="9525" systemVersion="15B42" minimumToolsVersion="Automatic">
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="10174" systemVersion="15B42" minimumToolsVersion="Automatic">
<entity name="Car" syncable="YES">
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="type" optional="YES" attributeType="String" syncable="YES"/>
Expand Down Expand Up @@ -27,16 +27,23 @@
<attribute name="float" optional="YES" attributeType="Float" defaultValueString="0.0" syncable="YES"/>
<attribute name="transformable" optional="YES" attributeType="Transformable" syncable="YES"/>
</entity>
<entity name="Product" syncable="YES">
<attribute name="color" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="shop" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Shop" inverseName="products" inverseEntity="Shop" syncable="YES"/>
</entity>
<entity name="Shop" syncable="YES">
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="employees" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="Employee" inverseName="work" inverseEntity="Employee" syncable="YES"/>
<relationship name="owner" optional="YES" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="Employee" inverseName="shop" inverseEntity="Employee" syncable="YES"/>
<relationship name="products" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="Product" inverseName="shop" inverseEntity="Product" syncable="YES"/>
</entity>
<elements>
<element name="Car" positionX="-45" positionY="54" width="128" height="75"/>
<element name="Company" positionX="-54" positionY="36" width="128" height="75"/>
<element name="Employee" positionX="-63" positionY="-18" width="128" height="165"/>
<element name="Other" positionX="-54" positionY="36" width="128" height="30"/>
<element name="Shop" positionX="-54" positionY="27" width="128" height="90"/>
<element name="Shop" positionX="-54" positionY="27" width="128" height="105"/>
<element name="Product" positionX="-45" positionY="63" width="128" height="90"/>
</elements>
</model>