iPhone - Core Data Bool Values? - iphone

I have a BOOL in my core data.
I want the value to be null unless i set it to either YES, or NO.
Is it possible to do this?
in my code how can i check to see if the value has not been set yet or not?
In my nsmanagedObject i use NSNumber for my BOOL, in my core data mdoel i have BOOL.

CoreData allows you to set default values.
A Bool can contain "YES", "NO" , "None".
Setting the default value to "None" makes the returned value "null" which is exactly what i needed.

If you set Boolean as the attribute type in your model, that means it will be implemented using NSNumber.
I'm not sure why you'd want to return no value instead of NO, I'd actually recommend you don't. You'll have a much easier time with data integrity if you just set a false default value.
However, if you absolutely insist, you can just check if the attribute is nil or not to see if it was assigned a value yet.

The correct answer is yes, and there is real meaning to use 'yes' 'no' and a different 'none', which means the data is intact. In Core Data there is good reason sometimes to check against 'none', especially if you'd like to debug a database. In Core Data you can use:
[myManagedObject setValue: nil forKey:#"attributeOfMyManagedObject"];
and if check your SQL database behind your Core Data you will see 'NULL' if you use default settings.

Related

SwiftUI NSData comparing non-optional value which is actually optional [duplicate]

I have a core data entity named Film which has properties title and date. I noticed that the generated NSManagedObject subclass contains optional NSManaged properties even though I marked the properties as non optional in the core data inspector.
Can I can manually change it as non-optional property or is it a better choice to leave it as optional? Why?
"Optional" means something different to Core Data than it does to Swift.
If a Core Data attribute is not optional, it must have a non-nil value when you save changes. At other times Core Data doesn't care if the attribute is nil.
If a Swift property is not optional, it must have a non-nil value at all times after initialization is complete.
Making a Core Data attribute non-optional does not imply that it's non-optional in the Swift sense of the term. That's why generated code makes these properties optional-- as far as Core Data is concerned, it's legal to have nil values except when saving changes.
Update: After writing this answer I wrote a deep dive blog post explaining things in more detail: https://www.atomicbird.com/blog/clash-of-the-optionals/
This is a known issue. Some people change it to non-optional with no adverse effects, I keep it the way it was generated and hope for early fix.
It always helps if you submit a bug to Apple to increase visibility and priority.
Create managedobject class and change the entity class type to manual and add these classes to your project scope.
Edit your managedObject to make them non-optional. This means you need to maintain this class yourself and do any changes both in the core data model and the class
If your data model is stable and won't be changed then you can use this.
The Optional checkbox in the data model inspector has nothing to do with Swift optionals. The checkbox determines whether or not the attribute is required to have a value.
If you deselect the Optional checkbox for an attribute, you must give that attribute a value or you will get an error when saving. By selecting the Optional checkbox you can save without giving the attribute a value. Suppose you have a description attribute that's a string. If you select the Optional checkbox you could leave the description blank and still save the entity.
Here's another example. Suppose you have text fields to let a person enter their home, work, and cell phone numbers. These phone numbers should be optional attributes. You wouldn't want to require someone to have a home phone number, a work phone number, and a cell phone number just to save the person's data.

FMDB: Retrieval of NULL values for value types in Swift

Using FMDB, bridged for use in Swift, I retrieved long integer values for a SQLite column definition like this
myColumn BigInt NULL UNIQUE
with a line of Swift code for an FMResultSet (based on a straightforward select query left out here) like this
let value = resultSet.longForColumnName("myColumn")
This worked fine. Yet, when I retrieved and then updated multiple records involving this column, I ran into a Unique Key Index violation. As it turned out, for NULL values, the above line of Swift code returned a value of 0, and I couldn't see a quick way to detect NULL values properly.
When searching for a proper way to handle this, the only related question I could find is this one concerning empty strings being returned for text columns with Null values. The answer didn't apply here. So, I'm adding the results of my research here, should they be useful to somebody else.
(The underlying problem turns out to not be specific to having the Unique constraint.)
The FMDB API, when bridged from the Objective-C version, seemingly has no direct way to check for NULL values (correct me, if I'm wrong, please). So, for a database column defined as, for example,
myColumn BigInt NULL
a NULL value will appear as value 0 in any FMResultSet involving this column with Swift code as shown in the question.
(This will have particularly surprising results when there happens to be a UNIQUE constraint on top. The NULL value will be retrieved as 0 from the database to be potentially updated as such with the next save operation, violating the Unique constraint when multiple entities are involved, as in my case. However, the underlyling problem is independent of the constraint. So, I'll focus on the problem of NULL values in general.)
To avoid this problem, we have to retrieve the column's value as an object from a respective FMResultSet first like so:
let objectValue = resultSet.objectForColumn("myColumn")
If objectValue happens to be of type/value NSNull(), then we have a NULL value and can handle it accordingly. Otherwise, we can then use the normal longForColumnName method. (For object types such as Strings, however, the FMDB implementation naturally returns an optional, which will be nil for database values of NULL!)
To make this easier, I use an extension of the FMResultSet class (for retrieval by index, which I prefer) like so:
extension FMResultSet {
func isNullForColumnIndex(columnIdx: Int32) -> Bool {
let value = self.objectForColumnIndex(columnIdx)
if let nullValue = value as? NSNull {
return true
} else {
return (value == nil)
}
}
}
This reduces the value extraction for a number type like in the example above to a line like this, assuming "myColumn" would appear at index 0 in the result set:
let num: Int64? = (result.isNullForColumnIndex(0) ? nil : Int64(result.longForColumnIndex(0)))
Surely, I could as well have added a method such as, for example, optionalLongForColumnIndex(columnIndex: Int32) -> Int64? that would include both the NULL-check and value retrieval. That would just require one such method for every value-type, which I have avoided so far.
Marco, your own answer is excellent: it describes exactly what is happening. Yet there are type-safer alternatives to ccgus/fmdb when coding in Swift. You could try https://github.com/stephencelis/SQLite.swift (pretty popular) or https://github.com/groue/GRDB.swift (closer to fmdb in the spirit).
I had a similar problem in Objective-C with FMDB.
Also getting 0 instead of nil when using longForColumnIndex.
However using:
NSNumber* object = [resultSet objectForColumnIndex:0];
worked as expected.
Gives a perfectly valid NSNumber object if a Value is stored, NSNull otherwise.

Attempting to work with Booleans in core data

Started working with booleans trying to save them within core data and figured I could do a simple true/false save object but after reading I am unsure of whether or not they added the ability to do this in swift or not. I read another post Swift + CoreData: Can not set a Bool on NSManagedObject subclass - Bug?. Reading through it led me to believe that they changed this so that it should be able to save simple booleans based on the edit from the first answer.
So I created the class with Bool instead of NSNumber, and tried to set the object true and save the object. It doesn't seem to work. So my question is do you have to set the object with NSNumber still?
The issue I am facing is that when I try to recall the data using a fetch request after saving the Bool as true, with the default value set to No which should = false if I understand correctly, I should be able to recall which attribute is true through the getter that I created as a var that loops to check what the object is and returns it as a string. This however isn't working.

NSUserDefaults: What's the +resetStandardUserDefaults method good for? How can I provide "system settings"?

I want the user to be able to make some preferences like colors, prefered images, etc.
When I use NSUserDefaults for this, on first start of the app there will be no preferences, right? So, every time I want to get a preference like
NSInteger avatarID = (NSInteger)[[NSUserDefaults standardUserDefaults] objectForKey:#"avatar"];
I have to check if it's null, and then use my system preference. But then I've seen this in the docs: +resetStandardUserDefaults
Are there two branches of defaults saved somewhere? The ones from the user, and the ones from the developer?
Yes, but it's a little different than what you're doing. NSUserDefaults has the registerDefaults method, which lets you populate the "blank" defaults from an NSDictionary with values you provide. Put this in the +initialize method in your app controller, and you'll know the default values are sensible. If the user defaults object finds a "real" value for a key, either one you set during the current application launch or loaded from disk, it will always take precedent over what you provided in registerDefaults. resetStandardUserDefaults will remove the shared user defaults object from memory and undo any customization you've made to it, it's not something you'll need to provide default values.
Also, keep in mind you can't just cast an object to a primitive NSInteger value as you're doing, you'll just end up with a number representing the memory location of the object's pointer. Either use NSUserDefault's integerForKey: to get a primitive directly, or use objectForKey: to get an NSNumber and use integerValue to get the primitive value.

Replacement for use of nil in dictionaries in objective-C

I'm working in the IPhone SDK, and am pretty new to objective-c. Right now I'm working with NSUserDefaults to save and restore setting on my IPhone app. In order to save the classes that have been created, I encode them into dictionary form, and then save the NSdictionary.
My problem is that I can't find a reasonable way to store a non-value (I.E. a class variable that is nil) in the dictionary in a reasonable way. To be more specific, lets say I have a class "Dog" and it's got NSString *tail-color. Lets say I'm trying to save a class instance of a dog without a tail, so tail-color for that instance is nil. What is a reasonable way of saving dog as a dictionary? It won't let me save nil into the NSdictionary. #"" isn't good, because if I do if(#""), #"" is true. I would like it to be false like nil.
I hope my question makes sense, and thanks for your help!
If you don't store anything for that key, nil will be returned when you call objectForKey:. If you check for nil when reading the data in, would that be enough? Optionally, you can use objectsForKeys:notFoundMarker: that will return a default value instead of nil.
So, store nothing at all in the dictionary for a value you don't have, and use a strategy for handling that value missing when reading.
You could use NSNull, but that doesn't feel standard, IMO.
You can use NSNull. Instantiate it like this:
[NSNull null]
I would recommend Archiving to save and restore your objects, however.
You should use NSNull to represent nil objects in collections
The best solution is to not save the values which are 'nil' in your case. While reading if the value is not present for your given key the dictionary will return you 'nil'.