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

19,617

Solution 1

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.

Solution 2

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()

Solution 3

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

Solution 4

In XCode 8 Just set :

user.isLoggedIn = true

its works like a charm

Share:
19,617

Related videos on Youtube

iQ.
Author by

iQ.

Updated on June 13, 2022

Comments

  • iQ.
    iQ. almost 2 years

    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?

    • Daniel Galasko
      Daniel Galasko almost 10 years
      The answer is correct, you cannot treat false as an object, what you should be doing is entity.completed = NSNumber.numberWithBool(false)
    • holex
      holex almost 10 years
      CoreData can store objects only, not primitives. it has been the same in Obj-C since iOS4, that should not be a new surprise.
    • Daij-Djan
      Daij-Djan almost 10 years
      @holex -- but it supports scalars since ios7 - im aware it boxes them for you - but thats besides the point. you shouldn't have to deal with NSNumbers either way: swift should get that -- i call it a bug
    • iQ.
      iQ. almost 10 years
      I have filed a bug, let's see where it goes, haven't tested with Xcode Beta3 yet though.
  • iQ.
    iQ. almost 10 years
    that actually gives me an error as it is actually represented as a Bool. Thing is, I can set this value in the beginning fine when I first create it from NSEntityDescription.
  • Daniel Galasko
    Daniel Galasko almost 10 years
    File a bug (another post stackoverflow.com/questions/24112250/… also has same issue)
  • Binarian
    Binarian almost 10 years
    Swift may use Bool everywhere, where BOOL is wanted. And internal the NSManagedObject translated SQLite's Boolean to NSNumber. NSManaged does nothing, it is like @dynamic. That said the CoreData Framework creates the getter/setter at runtime.
  • Binarian
    Binarian almost 10 years
    With Objective-C I could use BOOL on SQLite's Boolean, and that is not a NSNumber either (it is not even an object, char). That is what the setter and getter should do in the first place, like it Objective-C. I think it is a bug.
  • Cooper Buckingham
    Cooper Buckingham almost 10 years
    You've misread my answer and incorrectly downvoted. I say that swift can use BOOL anywhere, where objective cannot use it as an object.
  • Binarian
    Binarian almost 10 years
    No you don't understand. BOOL in Objective-C for a NSManagedObject subclass converts to NSNumber (an object!) in the generated setter and getter automatically to synchronize with SQLite's Boolean. So the statement you have is wrong about CoreData.
  • Cooper Buckingham
    Cooper Buckingham almost 10 years
    I didn't say it wasn't doing that. I suggested a common usage of bool in objective c. You better get out there and downvote all the other comments and answers then on the same track. And it's absurd to vote down an answer when the only other explanation is a bug in the framework.
  • Binarian
    Binarian almost 10 years
    The answer from Daniel is working, even though setValue is the best option for now, like in the question said. Your text is simply wrong tell an Objective-C class to treat a BOOL like an object. If you get wrong answers you will get downvoted, so nobody thinks it is correct. If you don't like it, you can delete it and get your points back.
  • Cooper Buckingham
    Cooper Buckingham almost 10 years
    Nah, who cares about points. It's actually your English failing you, and I'd rather you didn't do it to others, or further confuse things. :-)
  • iQ.
    iQ. almost 10 years
    It works for the other types, just Bool is the problem.
  • iQ.
    iQ. almost 10 years
    I haven't had a chance to play with Beta 3 yet, will check it out soon.
  • Daij-Djan
    Daij-Djan almost 10 years
    nice swift gets scalars now! still: you shouldn't have to deal with NSNumbers either way: swift should get that -- i call it a bug
  • Mark.ewd
    Mark.ewd over 9 years
    Thanks. Your answer helped me to understand what the issue was.
  • Mark.ewd
    Mark.ewd over 9 years
    I upgrade xcode to 6.1 and now this does not work! It seems to ignore the code that maps the get. How frustrating!
  • Mark.ewd
    Mark.ewd over 9 years
    I got it working by just adding "as Bool" on each line where I'm access a value (the getter).
  • Binarian
    Binarian over 9 years
    @Mark.ewd yes, it is a good idea to just cast the type in the getter, instead of create a new Bool like I did with the Bool(...) syntax.
  • Zia
    Zia over 8 years
    You should modify the answer so others who look at it don't have to come down to comments to find this out. Great answer though :D
  • JBarros35
    JBarros35 over 4 years
    2019, still they didn't do that... 'NSNumber?' is not convertible to 'Bool'; did you mean to use 'as!' to force downcast?