iQ.
iQ.

Reputation: 3953

Swift + CoreData: Can not set a Bool on NSManagedObject subclass - Bug?

I have a little strange issue which I can't seem to figure out, I have a simple entity with a custom NSManagedObject subclass:

@objc(EntityTest) class EntityTest: NSManagedObject {

    @NSManaged var crDate: NSDate
    @NSManaged var name: String
    @NSManaged var completed: Bool
    @NSManaged var completedOn: NSDate
}

This is the problem, I can create the object fine and set the all the values and store in in an array. However late on, when I try to retrieve the same object, I can set all the values EXCEPT the "completed" field. I get a run-time error saying "EXC_BAD_ACCESS", I can read the value, just can not set it.

The debugger points to:

0x32d40ae:  je     0x32d4110                 ; objc_msgSend + 108
0x32d40b0:  movl   (%eax), %edx

Maybe some issues due to it being treated as an Objective-C class and trying to send a message to set boolean which I know is a bit funny with CoreData originally representing them as NSNumbers.

Any ideas? I created the class myself, it is not generated.

EDIT:

entity.crDate = NSDate() // succeeds
entity.completed = false // fails
entity.completed.setValue(false, forKey: "completed") //succeeds

So for setting the bool, using the setValue of NSManagedObject works but not the direct setters, though for the non-bool properties, I can set it using the setters.

UPDATE:

While checking this a bit more, it seems like the first time I set the value after getting from NSEntityDescription, it uses normal Swift accessor methods. Later on when I try to access the same object (which was stored in an array) it attempts to treat it as a Objective-C style object and sends a message for method named "setCompleted". I guess it makes sense since I use the dot notation to access it and I used the @objc directive.

I tested this by creating a "setCompleted" method, however in the method I set the value using "completed = newValue" which makes a recursive call back to "setCompleted" causing it to crash... Strange, so at this moment still can't don't have a proper fix. It seems to only happen with Bools.

Only workaround is use the "setValueForKey" method of NSManagedObject. Perhaps file this as a bug report?

Upvotes: 18

Views: 20800

Answers (6)

Aqib Mumtaz
Aqib Mumtaz

Reputation: 5074

In XCode 8 Just set :

user.isLoggedIn = true

its works like a charm

Upvotes: 1

waj
waj

Reputation: 1185

I found that it works fine if you specify the class name and module in the data model (instead of leaving the default NSManagedObject).

Once I set that, I can use Bool, Int16, Int32, etc., without any problems.

Upvotes: 0

Anvar Azizov
Anvar Azizov

Reputation: 585

You can downcast your property from NSNumber to Bool type like this:

var someBoolVariable = numberValue as Bool    

It works for me in this way:

self.twoFactorAuthEnabledSwitch.enabled = userProfile?.twoFactorEnabled as Bool

Upvotes: 1

Binarian
Binarian

Reputation: 12446

If you let Xcode 6 Beta 3 create the Swift files for your entities, it will create NSNumber properties for CoreDatas Boolean type.

enter image description here

You can however just use Bool as a Swift type instead of NSNumber, that worked for me without using the dot syntax though. It will set the Swift Bool with a NSNumber, that maybe leads to a bug in the dot syntax.


To make it explicit you should use the type NSNumber for attributes in the entity with the Boolean type. Then create a computed property (in iBook The Swift programming language under Language Guide -> Properties -> Computed Properties) to return you a Swift Bool. So would never really store a Bool.

Like so:

@NSManaged var snack: NSNumber
var isSnack: Bool {
    get {
        return Bool(snack)
    }
    set {
        snack = NSNumber(bool: newValue)
    }
}

Of course it would be cool to hide the other (NSNumber attribute), but be patient and Apple will implement private attributes in the future.


Edit:

If you check the box create skalar types it will even use the type Bool in the automatically created Swift file!

So I think it is a bug.

Upvotes: 19

Daniel Galasko
Daniel Galasko

Reputation: 24267

Following on from CH Buckingham who is entirely correct. You are attempting to store a primitive type in core data where it is expecting an NSNumber.

The correct usage would be entity.completed = NSNumber.numberWithBool(false)

This is also why you cannot retrieve this completed value as a bool directly and thus you would need to write:

var: Bool? = entity.completed.boolValue()

Upvotes: 2

Cooper Buckingham
Cooper Buckingham

Reputation: 2534

I haven't touched swift, but in Objective-C, a BOOL is not an object, and cannot be an object. It's a primitive, and it looks like you are attempting to tell an Objective-C class to treat a BOOL like an object. But I could be making that up, as I'm not familiar with what @NSManaged does under the hood.

https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/FoundationTypesandCollections/FoundationTypesandCollections.html

Upvotes: 0

Related Questions