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.
Related
I have a Core Data Entity with a Date attribute (e.g. current_date. Technically, in the Class which Core Data generates for that Entity, this attribute is optional (#NSManaged public var current_date: Date?). However, in my app this Date is always provided, which is why in the View I am displaying this fetched Entity via a list, I am force unwrapping it with current_date!. This all works fine so far. However, I have added an onDelete to that list to be able to delete single elements and now I am getting a bug Fatal error: Unexpectedly found nil while unwrapping an Optional value. Seems to be some problem related to the deletion process - as said, that value in the data is actually never empty. Does anyone know what is the problem here?
Your code may not set current_date to nil. But if you
Delete a managed object object
Save changes
Try to use that same managed object reference
...then current_date is nil, because that's something Core Data does. This error message suggests that you are attempting to use the object after deleting it and saving changes. Ideally you should fix that, because you won't have valid data anyway. You should also avoid force-unwrapping anyway, because using it is a way of specifically requesting app crashes any time you use it.
I have a ViewController with a 'Note' object as an instance variable. This variable is set before the ViewController appears.
The Note object contains a customFields NSMutableDictionary<String, String>.
I bind each value of the dictionary to a NSTextField of the dictionary in viewWillAppear :
newTextField.bind(.value, to: self, withKeyPath: "note.customFields.\(key)", options: nil)
The textField correctly displays the value from note.customFields.\(key).
BUT, if the user edits that value and saves, the change is not reflected in the dictionary.
The save() (basically a managedobjectcontext.save()) method has been tested with other fields (bound to other non-dictionary instance variables) and works properly.
Is there a reason why the dictionary does not receive the updated value?
Thanks for the help :-)
EDIT: here is the definition of customFields in the model:
The issue here is the combination of mutable type (here NSMutableDictionary) and Core Data.
This post helped: Core Data not saving transformable NSMutableDictionary
And this article helped as well: https://medium.com/#rohanbhale/hazards-of-using-mutable-types-as-transformable-attributes-in-core-data-2c95cdc27088
Solution summary
As proposed in the first link up-mentioned, I tried informing Core Data of changes in my NSMutableDictionary using note.valueDidChange(forKey: "customFields") but it did not work properly.
So I ended up using the second proposition in the first link, which is the same as the solution proposed in the second linked:
Use a NSDictionary as transformable type
When you need to modify this dictionary, use mutableDict = dictionary.mutableCopy(), and then save back to the nsdictionary : dictionary = mutableDict.copy()
For the specific case of NSTextField binding, the bindings will work for displaying the value, but for updating the value, you have to do it without bindings (whenever your view will disappear or when textfield ends editing)
Hope that helps!
I have a swift UIViewController in which there is an array of data type ( e.g. countries/person/ bank details) fetched from network service asynchronously in viewDidLoad. This array remains constant once fetched. Hence I want to enforce it using let keyword.
I don't have any value when UIViewController is initialized. Hence it gives me compile time error for not initializing it.
If I declare it as optional with '?' , I have to use if-let/ guard let or optional chaining to use it. I don't want to clutter the code with unwrapping.
What are my options to declare a variable as constant but initialized later in execution without making it an optional variable?
"Constant once fetched" is not constant. There is some period of time when it's not set, and some point later when it is. The fact that it's fetched from the network means that it may fail, so your code has to deal with that (i.e. it may never be set). The right tool to use here is an Optional var.
Since there must be some view state that handles "no data yet" and a different view state that handles "data received," you can improve your design by breaking those into two view controllers, and having your container view controller switch between them when the data becomes available. In that case, you can pass the available data to the "data received" view controller in init, and so it can be let.
What are my options to declare a variable as constant but initialized later
There are no such options. If you do not have the real value of a property at instantiation time, it must be declared with var so that you can set it when you do have the real value.
And use of an Optional is a common pattern to help your code distinguish between before you have the real value (nil) and after (not nil).
I don't want to clutter the code with unwrapping.
Then declare the property as an implicitly unwrapped Optional! This use case is exactly what it is good for.
(It would be nice to be able to “lock” the property somehow after assigning its final value, but that is not a Swift language feature. I have often wished it were. lazy has the same issue.)
How can we validate the context before saving it to core data?. My idea is i should have some validation before saving it into core data, if the values doesn't satisfy the validation the the coredata should not save the values. Say for example i have attributes like name, class, age, etc for entity candidate. I should have a validation that the values shouldn't be nil. If it is nil then the other values should not be saved.
Can anybody help me in this regard
EDITED:
I need to check them only at the time of saving and that should be done with core data
I like to do catchall validation in the save routine. Before you actually do the call to save the context, loop through its insertedObjects array and make sure they are as you require. If they aren't, you can either delete them or alert the user that they need to complete something (if the latter, return out of the method; give the user a chance to fix the problem).
Other validation should be at the point of entry, when you are getting values from, say, a textfield or checkbox to assign to your managed objects. Let the user know right away if there's a problem.
Also check out NSNumberFormatter, which can be applied to fields, preventing user from making incorrect entries to begin with.
Coredata validate itself when inserting its values. In managedObject class we can write our custom validation so that coredata will check that validation before saving the values. If the value is not valid then those values in the context will not be saved to coredata.
Here i added
#interface
-(BOOL) validateForInsert:(NSError **)error;
#implementation
-(BOOL) validateForInsert:(NSError **)error {
// check the value of the field with validation
if(condition == true) {
return Yes;
}
return NO;
}
(NSError **) is a special parameter that makes the coredata to call this method as if like a delegate method
Sorry, I hadn’t read your question carefully enough when I made that first answer. You’re not validating that individual entries for individual attrs are correct, rather, that no changes should be saved unless all attrs are filled for that object.
Looking at Apple doc “Model Object Validation”, you are concerned with inter-property validation, not property validation, and you are on the right track to be thinking of using validateForInsert: for this purpose.
That doc also supplies examples. Here’s a possible implementation for the particular entity you describe:
- (BOOL)validateForInsert:(NSError **)error {
if (self.name && self.class && self.age)
return [super validateForInsert:error];
else
return NO;
}
However, this method happens at the insertion stage, not at the save stage.
If you are gathering entries for a new entity all at once, validating at the insertion stage would make sense — don’t add a new object to the context if that object is doomed to be discarded as incomplete.
If you are gathering entries for changes to an existing object, and you want to make sure that all those changes work together before accepting any of them, validateForUpdate: would make sense — but there would be no way to restore the object to its original state other than by reopening the context without saving, unless you had cached its original values elsewhere.
If you want to gather attrs individually and wait to check that they are all complete before saving the object, I think you would do as I first suggested: Loop through the context’s insertedObjects and take care of validation before actually saving the context. There’s no existing validateForSave: method to override, but you could add one.
You could also combine these techniques: Gather entries and make new objects without inserting them, but cache all these objects in an array. When it comes time to save, loop through the cache and insert the objects into the context only if they pass validateForInsert:; then save the context.
Obviously I’m learning along with you, so the above might not be quite the cookie. Hopefully that Apple doc will be enough to get you started.
The docs say:
The default implementation does not
copy attribute values. If the
attribute value may be mutable and
implements the NSCopying protocol (as
is the case with NSString, for
example), you can copy the value in a
custom accessor to help preserve
encapsulation (for example, in the
case where an instance of
NSMutableString is passed as a value).
So instead of getting into trouble and inconvenience with overwriting accessors in my NSManagedObject subclass, couldn't I simply do something like this?
myManagedObject.firstName = [[firstNameMutableStr copy] autorelease];
This would have the exact same effect, or not? The dynamic implementation would retain that anyways ... so.... why not the easy way?
It's an open question whether having to remember to copy the mutable string every where in code you set the attribute is "the easy way."
With a custom accessor, you just write the copy once then forget about. It copies automatically from that point on.
Just imagine that in thousands of lines of code you forgot to copy just once and developed a subtle bug because that one attribute of the managed object sporadically changed because some other totally unrelated code subsequently changed the mutable string you held only by reference.
I could tell you some stories of weekends lost to debugging because someone took "the easy way."