Cocoa bindings only working one way (NSMutableDictionary) - swift

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!

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.

How can I persist NSPredicateEditor NSPredicateEditorRowTemplates data

I've been struggling with getting the information entered in an NSPredicatEditor from session to session in a Swift project.
Any guidance or samples would be greatly appreciated.
What I've got so far.
A predicate editor that is functional implemented in an Action Sheet.
Bound predicate to an NSArrayController also bound to a CoreData Table.
A method to generate Row Templates based on CoreData Table Attributes.
What is not working.
A bound predict variable to the predicateditor getting error on not
being able to match the predicate to a row template also at the
awakefromnib seems to be where this is checked the row templates are
not initialised (nil )at this point from the InterfaceBuilder
What I want to be able to do
Using the NSUSerDefaults persist the contents of the predicate editor so that when the application is re-started this editor has the data last entered.
Load the previously saved predicate by setting the editor's objectValue property.
predicateEditor.objectValue = predicate
After setting up the predicate editor, you typically send it a objectValue message to restore a saved predicate. source
It sounds like you may have a complicated setup involving Interface Builder, array controllers, Core Data, bindings, etc. Try it through code instead: load the row templates, then load the predicate.

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.

dynamic Instance naming

I'm developing an iPad App and need some help.
Through a button within my App I want to create one object at a time.
So every time the button is touched one object should be created.
The problem I have is: I want to assign each object a dynamic name to identify this object.
This would be something like: form0, form1, form2, ..., formN.
This Name corresponds to an instance variable within every object.
So the form1 instance has a number attribute which is 1.
But how do I assign this form1, form2, etc. to a new instance?
I tried to initialize a new instance with the return of a method which creates the formX-String:
-(NSString*)giveMeName{
NSString* simpleName = #"form";
NSString* newName = [simpleName stringByAppendingString:[NSString stringWithFormat:#"%d", questionCounter]];
return newName;
}
where questionCounter is a variable which holds the int identifier for both formX and the instance number attribute.
But when I want to initialize a new instance with this function as name it's not working:
TSForm* [self giveMeName] = [[TSForm alloc] initWithInt:questionCounter headline:headlineText intro:introText];
Obviously I got something wrong with the inner working of Objective-C.
Please help me out.
what you're trying to do isn't really possible. One way that you could achieve the affect you're looking for is using an NSDictionary. For every TSForm object you create, you add that object to the dictionary with the key of the giveMeName return value.
So you start by creating your dictionary:
NSMutableDictionary *formDict = [[NSMutableDictionary alloc] init];
Then, every time you create an object, add it to the dictionary:
id *newTSForm = [[TSForm alloc] init]; // Or however you create a TSForm
[formDict setObject:newTSForm forKey:[newTSForm giveMeName]];
Then when you want to pull out the form you're looking for, you just ask the dictionary based on the name you provided:
[formDict valueForKey:nameOfForm]; // nameOfForm is the name provided by giveMeName
Hope this helps!
use NSMutableArray and keep adding your items there.
Even if what you are trying to do is technically possible, that's using tricsk in low-level objective-C runtime and KVC stuff and so on for nothing.
Using a simple NSMutableArray to keep track of all you instances (and using the index in the array to know which form you are dealing with) is the way to go.
I don't think you really need your unique identifier stuff for that (if so, you are probably thinking about your project the wrong way), as long as you have a way in your code to differentiate each form and manipulate them (the first form created will then be at index 0, the second at index 1… of your NSMutableDictionary)
If you really need this special unique identifier anyway for some strange reason, you can still use an NSMutableDictionary and use the unique identifier as your key of your dict and the form as the associated value. But you should probably think twice about your architecture ad the real need for this before, as it seems quite strange app architecture/design to do so based on your description of your needs in your question.
What you are looking for is some kind of variable variable, which don't really exist in objective-C.
This question (Objective C Equivalent of PHP's “Variable Variables”) has some different suggestions for getting similar results.

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'.