Swift + CoreData: Can not set a Bool on NSManagedObject subclass - Bug?
Solution 1
If you let Xcode 6 Beta 3 create the Swift files for your entities, it will create NSNumber
properties for CoreData
s Boolean
type.
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
Related videos on Youtube
iQ.
Updated on June 13, 2022Comments
-
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 almost 10 yearsThe answer is correct, you cannot treat false as an object, what you should be doing is entity.completed = NSNumber.numberWithBool(false)
-
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 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. almost 10 yearsI have filed a bug, let's see where it goes, haven't tested with Xcode Beta3 yet though.
-
-
iQ. almost 10 yearsthat 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 almost 10 yearsFile a bug (another post stackoverflow.com/questions/24112250/… also has same issue)
-
Binarian almost 10 yearsSwift 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 almost 10 yearsWith 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 almost 10 yearsYou've misread my answer and incorrectly downvoted. I say that swift can use BOOL anywhere, where objective cannot use it as an object.
-
Binarian almost 10 yearsNo 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 almost 10 yearsI 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 almost 10 yearsThe 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 almost 10 yearsNah, 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. almost 10 yearsIt works for the other types, just Bool is the problem.
-
iQ. almost 10 yearsI haven't had a chance to play with Beta 3 yet, will check it out soon.
-
Daij-Djan almost 10 yearsnice 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 over 9 yearsThanks. Your answer helped me to understand what the issue was.
-
Mark.ewd over 9 yearsI 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 over 9 yearsI got it working by just adding "as Bool" on each line where I'm access a value (the getter).
-
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 over 8 yearsYou 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 over 4 years2019, still they didn't do that... 'NSNumber?' is not convertible to 'Bool'; did you mean to use 'as!' to force downcast?