Cocoa input validation for duplicate name how? - cocoa

How can I implement this validation in Cocoa?
My Situation is:
Model: An object names Person, with a name property. And an NSArray of Person objects,
View: NSTableView, it uses data-binding to bind with the Person object array. The NSTableView has in-place editing function enabled.
When user finish editing the name in NSTableView, I need check if the Person's name already exists in the Array.
I read the key-value Validation document. It looks KVC will help call validate:error: method on the bound object. So in my case, it should be the Person object. But the Person Object cannot access the whole array, I cannot check for duplication in the method.
Can anyone point me how to use key-value Validation to handle this case?
Thanks!
-Jonny

Take a look at this page in documentation particularly section "User Updates a Value in the User Interface". Hope this helps.

Related

Core Data: "-add<RelationshipKey>Object:" not called

Let's presume I have an entity BikeRider with a relationship property called helmets.
I have an array controller bound to the app's managed object context, with entity set to BikeRider. There's a tableview that lists all bike riders.
Then, I have a second array controller, bound to the app's managed object context, with entity set to Helmet. Additionally, it's bound to bikeRiderArrayController.selection. There's a second tableview that lists all helmets for the selected bike rider.
I also have two buttons for adding and removing helmets. The setup works apparently flawlessly.
Except, of course for one small thing: it looks like -addHelmetsObject:, -removeHelmetsObject:, -addHelmets: and -removeHelmets: never get called. This means some code for setting up observation of each helmet's color property never gets called.
What am I missing? Isn't overriding addHelmets: et al (with proper willChangeValueForKey: et al notifications) the right way to get notified of additions?
Do I really have to [self observeValueForKey:#"helmets". . .] and then [oldValue minusSet:newValue] and vice versa to figure out which objects were added or removed? I could swear the methods were being correctly called in the past. Maybe some key element of the setup is now different.
This has never worked properly through NSArrayController. From Apple's docs:
Custom relationship set mutator methods are not invoked by an arraycontroller
Problem: You have implemented set mutator methods for a relationship as described in “Custom To-Many Relationship Accessor
Methods,” and have bound the contentSet binding of an
NSArrayController instance to a relationship, but the set mutator
methods are not invoked when you add objects to and remove objects
from the array controller.
Cause: This is a bug.
Remedy: You can work around this by adding self to the contentSet binding's key path. For example, instead of binding to [Department
Object Controller].selection.employees, you would bind to [Department
Object Controller].selection.self.employees.

implementing custom accessor methods

I am reading "Core Data Programming Guide". It contains this text:
You must, however, change attribute values in a KVC-compliant fashion.
For example, the following typically represents a programming error:
NSMutableString *mutableString = [NSMutableString stringWithString:#"Stig"];
[newEmployee setFirstName:mutableString];
[mutableString setString:#"Laura"];
For mutable values, you should either transfer ownership of the value
to Core Data, or implement custom accessor methods to always perform a
copy. The previous example may not represent an error if the class
representing the Employee entity declared the firstName property
(copy) (or implemented a custom setFirstName: method that copied the
new value). In this case, after the invocation of setString: (in the
third code line) the value of firstName would then still be “Stig” and
not “Laura”.
Question regarding text: "In this case" is which case--the one where property is declared as "copy" or when its not?
Question regarding copy and programming practice:
From what I have read here:
NSString property: copy or retain?
I understand
that using copy will ensure that firstName is "Stig", not Laura
it is wise to do so because "in almost all cases you want to prevent mutating an object's attributes behind its back"
I would really like to know what is the above quoted text trying to tell us in the context of Core Data. We have to use "copy" anyway whether using Core Data or not. Also, I would be glad if someone could throw more light on point "2" (it is wise to...) above as in what will be the consequences of mutating an object's attributes behind its back?
your "Question regarding text: "In this case" is which case--the one where property is declared as "copy" or when its not?"
mis-matched the point that Apple document wants to explain, I believe.
As Apple document points out, if custom-accessor-method is implemented normally, 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).
Here is a copying setter snippet
#interface Department : NSManagedObject
{
}
#property(nonatomic, copy) NSString *name;
#end
#implementation Department
#dynamic name;
- (void)setName:(NSString *)newName
{
[self willChangeValueForKey:#"name"];
// NSString implements NSCopying, so copy the attribute value
NSString *newNameCopy = [newName copy];
[self setPrimitiveName:newNameCopy];
[self didChangeValueForKey:#"name"];
} #end
The issue is when to use (and how) immutable values.
Since core data use KVO heavily when detecting changes done to objects, if you use a mutable property that is changed directly through it object and not through the property, CoreData will not detect the change to the object and your changes might not persist to the store.
If you use mutable NSManagedObject attributes, override the setter/getter method and use only them to mutate the underlying object (this mean that you are responsible to let CoreData know that a change did happen to the object, and it must be persisted to the store.
Also, if you use transformable properties for complex objects, you must trigger the change notifications yourself in order for CoreData to realise that a change has occurred, and the object should be re-transformed and saved when the context saves.
I would highly recommend that when it comes to simple objects like strings, you use immutable property values which will force you to go through the object properties and trigger the default KVO notification (copy attributes will also force the KVO notifications).

Core Data: observing nested properties

I have an entity Category in my Core Data model. Category has a to-many relation to Article. Article has a property read, which is a boolean.
I want to observe the number of unread articles (so I can display it in the title).
A first approach would be something like:
[self.category addObserver:self forKeyPath:#"articles.#sum.read" options:NSKeyValueObservingOptionNew context:nil];
But this doesn't work. I can observe the articles collection to see if something is added, and observe all of the elements individually. I can get this working, but I wonder if there is an easier way. Any hints?
(This might be a duplicate of Using KVO to observe changes to a property on an object inside a collection in Objective-C, but I still think there should be a better way).
How precisely do you need to know what changed and when it changed? If your requirements are very simple, you can listen to the NSManagedObjectContextObjectsDidChangeNotification notification and check the userInfo for NSUpdatedObjectsKey. Those objects will return the changes through their -changedValues method.
How do things get added to the articles collection? It probably easiest to simply hook in there directly in stead of using KVO.

Binding an NSArrayController to NSDictionary allValues

I've encountered a few scenarios where I'd like to show some entries in an NSDictionary in an NSTableView. (My instincts to user an NSDictionaryController are always foiled by the need to make every object implement copyWithZone, which I find to be an absurd requirement that is fatal to usability...)
Instead, I'm trying to bind an NSArrayController to the allValues property of an NSDictionary, with the intent of binding respective columns of the table various properties of the objects in the array. However, I can't find a correct way to specify this binding within Cocoa, via the Controller Key and Model Key Path properties of the array controller.
When I attempt to bind the array directly to allValues, the array controller's arrangedObjects property is constantly empty - even when objects are added to the dictionary, when the addition is enclosed by [dictionary willChangeValueForKey: #"allValues"] and [dictionary didChangeValueForKey: #"allValues"] calls, even after calling setContent: on the array controller after adding the object.
Of course, I found this Stack Overflow thread:
Binding to an NSDictionary's "allValues" array
...and tried various versions of #"#allValues" in the Model Key Path field, but none produced the desired results.
So here's my question: If I have ArrayControllerOne bound to an object, and I'd like to bind ArrayControllerTwo to the allValues property of an NSDictionary property (called "instances") within ArrayControllerOne.selection, what should I enter in the Controller Key and Model Key Path fields?
Thanks in advance...
You must have missed one, because I found that you can bind the content array of an array controller to a dictionary's allValues by using dict.#allValues (no quotes), where dict is the property name of the dictionary. Why this works, I don't know -- I can't find any documentation for this.
BTW, if I add a new key-value pair to the dictionary, I use self.dict = _dict; to get the table view to update.

Why would you bind the value of a NSProgressIndicator?

What's the point of binding the value of a NSProgressIndicator to your controller? It never seems to ask the controller for the value, except on startup. The only way to move the NSProgressIndicator seems to be by sending it #increaseBy:, which bypasses my binding. So, why would I bind?!
If your UI's bound value not updating, that means you either bungled the binding or your controller code is not modifying the bound value in a key-value-observing–compliant way. The most common problem is doing fooIvar = val rather than [self setFooIvar:val] or self.fooIvar = val.
Apple's answer to your problem:
[What to do if] Changing the value of a model property programmatically is not reflected in the user interface
If changes made to a model value programmatically are not being reflected in the user interface, this typically indicates that the model object is not key-value-observing compliant for the property, or that you are modifying the value in a manner that is bypassing key-value observing. You should ensure that:
The model class has automatic key-value observing enabled or implements manual key-value observing for the property.
That you are changing the value using an accessor method, or using a key-value-coding compliant method. Changing the value of an instance variable directly does not provide key-value observing change notifications.
If your model property is a collection, that you're modifying the content in a key-value-observing compliant manner. See “My collection controller isn’t displaying the current data” for more information.
For that answer and answers other common problems, see "Troubleshooting Cocoa Bindings."
You should also look at the examples provided by mmalc. They are a valuable resource.

Resources