leanne leanne - 1 month ago 13
Swift Question

Getting NSNumber exception while converting data based on attribute type

Using the pattern shown here: identify coreData Attribute's type

// setting up entity and attributes list
let entity: NSManagedObject = NSEntityDescription.insertNewObject(forEntityName: entityName, into: backgroundContext)

let entityAttributes = entity.entity.attributesByName

...

// looping through provided property names and values
let propertyType: NSAttributeType = entityAttributes[propertyName]!.attributeType

switch propertyType {

// StringAttributeType, used in link example, isn't available in Swift 3, Xcode 8.0
//case StringAttributeType:

// using instead: NSAttributeType constants
case .stringAttributeType:
let propertyValue: String = propertyValues[idx]
entity.setValue(propertyValue, forKey: propertyName)

case .integer16AttributeType:
let propertyValue = Int16(propertyValues[idx])
entity.setValue(propertyValue, forKey: propertyName)

...


However, getting exception with Int16 value:


Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "this_prop_name"; desired type = NSNumber; given type = _SwiftValue; value = 3.'


I thought we no longer had to do NSNumber conversions in CoreData with Swift. Or, am I having a low-brain-oxygen moment?

What is the correct Swift 3 way to convert an Int16 (or any other number value) via an entity's attribute type?

Answer

Setting the Core Data attributes via Key-Value coding requires values which are instances of NSObject subclasses, such as NSString or NSNumber.

Up to Swift 3.0, only certain scalar types can be bridged to NSNumber and back, such as Int, UInt, Double, Float but not the fixed-size types like Int16. (This will change in a future Swift release, compare SE-0139 Bridge Numeric Types to NSNumber and Cocoa Structs to NSValue.)

Therefore in

entity.setValue(propertyValue, forKey: propertyName)

propertyValue needs to be an Int or NSNumber. In your case of an "Integer 16" attribute, Int would be large enough to hold all possible values:

let propertyValue = Int(propertyValues[idx])
Comments