18

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?

iQ.
  • 3,811
  • 6
  • 38
  • 57
  • 1
    The answer is correct, you cannot treat false as an object, what you should be doing is entity.completed = NSNumber.numberWithBool(false) – Daniel Galasko Jun 22 '14 at 08:01
  • `CoreData` can store objects only, not primitives. it has been the same in Obj-C since iOS4, that should not be a new surprise. – holex Jun 22 '14 at 08:51
  • 1
    @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 – Daij-Djan Jul 13 '14 at 17:18
  • I have filed a bug, let's see where it goes, haven't tested with Xcode Beta3 yet though. – iQ. Jul 13 '14 at 23:31

6 Answers6

19

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.

Community
  • 1
  • 1
Binarian
  • 12,296
  • 8
  • 53
  • 84
  • I haven't had a chance to play with Beta 3 yet, will check it out soon. – iQ. Jul 13 '14 at 14:11
  • 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 – Daij-Djan Jul 13 '14 at 17:19
  • Thanks. Your answer helped me to understand what the issue was. – Mark.ewd Oct 14 '14 at 15:17
  • 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 Oct 28 '14 at 17:12
  • 1
    I got it working by just adding "as Bool" on each line where I'm access a value (the getter). – Mark.ewd Oct 28 '14 at 21:22
  • @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. – Binarian Nov 01 '14 at 11:00
  • 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 – Zia Aug 20 '15 at 20:43
  • 2019, still they didn't do that... 'NSNumber?' is not convertible to 'Bool'; did you mean to use 'as!' to force downcast? – JBarros35 Dec 13 '19 at 09:05
  • If you are wondering where the checkbox for scalar types is: https://stackoverflow.com/a/42677943/2107610 – bbjay Jul 04 '22 at 11:03
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()
Daniel Galasko
  • 23,617
  • 8
  • 77
  • 97
  • 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. – iQ. Jun 22 '14 at 10:24
  • File a bug (another post http://stackoverflow.com/questions/24112250/exc-bad-access-error-when-trying-to-change-bool-property also has same issue) – Daniel Galasko Jun 23 '14 at 11:33
  • 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. – Binarian Jun 25 '14 at 14:17
  • It works for the other types, just Bool is the problem. – iQ. Jun 25 '14 at 18:36
1

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
Anvar Azizov
  • 545
  • 1
  • 6
  • 9
1

In XCode 8 Just set :

user.isLoggedIn = true

its works like a charm

Aqib Mumtaz
  • 4,936
  • 1
  • 36
  • 33
0

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

Cooper Buckingham
  • 2,503
  • 2
  • 15
  • 23
  • 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 Jun 25 '14 at 14:15
  • You've misread my answer and incorrectly downvoted. I say that swift can use BOOL anywhere, where objective cannot use it as an object. – Cooper Buckingham Jun 25 '14 at 14:20
  • 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. – Binarian Jun 25 '14 at 16:05
  • 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. – Cooper Buckingham Jun 25 '14 at 16:08
  • 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. – Binarian Jun 25 '14 at 16:36
  • 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. :-) – Cooper Buckingham Jun 25 '14 at 18:30
0

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.

waj
  • 1,185
  • 10
  • 14