Circular dependencies in Cocoa bindings - cocoa

This is doing my head in...
Simplified version: I have two text fields - Field A and Field B. Field B can be derived from field A and similarly Field B can be derived from Field A.
(There's couple of other fields that in combination with either A or B produce data for multiple TextLabels)
What I want to do is: when user changes Field A, Field B is updated, and vice versa.
So I created two methods that do A to B and B to A. And defined dependencies like:
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:#"calculatedFieldA"]) {
NSSet *dependentKeys = [NSSet setWithObjects:#"valueOfFieldB", nil];
keyPaths = [keyPaths setByAddingObjectsFromSet:dependentKeys];
}
if ([key isEqualToString:#"calculatedFieldB"]) {
NSSet *dependentKeys = [NSSet setWithObjects:#"valueOfFieldA", nil];
keyPaths = [keyPaths setByAddingObjectsFromSet:dependentKeys];
}
return keyPaths;
}
Where calculatedFieldA and calculatedFieldB are the methods that do the conversion and valueOfFieldA and valueOfFieldB are NSString's that are bound to the two text fields.
It works (but only one way, B is updated whenever A changes) if I remove the second if statement. When the second if is defined, it just bombs out, because (I think) it sees A updated so goes and updates B, then because B is updated, goes and updates A again, etc, etc...
What is the best way to achieve this circular dependency? Is it a time to start reading about ValueTransformers?
PS. I'm a Cocoa newbie so please bear with me and don't punch too hard if this is a very trivial question...
EDIT:
I probably need to clarify few points:
calculatedFieldA accepts B value and returns A, also updates (via setter method) valueOfFieldA.
Similarly calculatedFieldB accepts A value and returns B, also updates (via setter method) valueOfFieldB.
This is on Lion, with Xcode 4.1.

Value transformers are almost certainly the correct answer here. Don't store "A" and "B". Just store one of them. Value transformers are exactly the way to display and accept input for the other one.

Related

How do I remove all map annotations in swift 2

I had working code to remove all map annotations with a button, but after my update to xcode 7 I am running into the error:
Type 'MKAnnotation' does not conform to protocol 'SequenceType'
if let annotations = (self.mapView.annotations as? MKAnnotation){
for _annotation in annotations {
if let annotation = _annotation as? MKAnnotation {
self.mapView.removeAnnotation(annotation)
}
}
}
In Swift 2 annotations is declared as non optional array [MKAnnotation] so you can easily write
let allAnnotations = self.mapView.annotations
self.mapView.removeAnnotations(allAnnotations)
without any type casting.
self.mapView.removeAnnotations(self.mapView.annotations)
If you don't want remove user location.
self.mapView.annotations.forEach {
if !($0 is MKUserLocation) {
self.mapView.removeAnnotation($0)
}
}
Note: Objective-C now have generics, it is no longer necessary cast the elements of 'annotations' array.
SWIFT 5
If you don't want to remove user location mark:
let annotations = mapView.annotations.filter({ !($0 is MKUserLocation) })
mapView.removeAnnotations(annotations)
The issue is that there are two methods. One is removeAnnotation which takes an MKAnnotation object, and the other is removeAnnotations which takes an array of MKAnnotations, note the "s" at the end of one and not the other. Attempting to cast from [MKAnnotation], an array to MKAnnotation a single object or visa versa will crash the program. The line of code self.mapView.annotations creates an array. Thus, if you are using the method removeAnnotation, you need to index the array for a single object within the array, as seen below:
let previousAnnotations = self.mapView.annotations
if !previousAnnotations.isEmpty{
self.mapView.removeAnnotation(previousAnnotations[0])
}
Thus, you can remove various annotations while keeping the users location. You should always test your array before attempting to remove objects from it, else there is the possibly getting an out of bounds or nil error.
Note: using the method removeAnnotations (with an s)removes all annotations.
If you are getting a nil, that implies that you have an empty array. You can verify that by adding an else statement after the if, like so;
else{print("empty array")}

keyPathsForValuesAffectingValueForKey not called for all attributes

I am trying to observe changes to a transient attribute in my entity that is dependent on other attributes in the same entity. I want to do this by implementing keyPathsForValuesAffectingValueForKey:. The problem is that this doesn't seem to be invoked for all the attributes in the entity.
My entity has 10 attributes and about 5-6 relationships, and the keyPathsForValuesAffectingValueForKey is called 5 or 6 times with some mix of attributes and relationships, but not all of them, including the transient attribute that I'm really interested in.
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:#"todoSectionTitle"])
{
NSSet *affectingKeys = [NSSet setWithObjects:#"todoStatus", #"todoStartDate", #"timeNow", nil];
keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKeys];
}
return keyPaths;
}
In this case, keyPathsForValuesAffectingValueForKey is called multiple times, but the key is never "todoSectionTitle" (a transient attribute). Neither is the key ever equal to "todoStatus" though that is a non-transient attribute. The key IS equal to "todoStartDate" in one of the times it is called. It can also be equal
What is the logic behind when keyPathsForValuesAffectingValueForKey: and which keys it is called for, and which keys it ignores? The docs don't seem to shed any light on this.
+keyPathsForValuesAffectingValueForKey: is part of Key-Value Observing (KVO). KVO only needs to call it if something observes a key on an instance of the class which implements it. (That includes any keys observed indirectly. For example, if something observes key "A", it will call the method for key "A". If it indicates that the value of "A" is affected by key "B", then it will also call it for key "B" to see if some other key(s) affect the value of "B".)
If nothing is observing todoSectionTitle or any key whose value is affected by todoSectionTitle, then KVO has no reason to call +keyPathsForValuesAffectingValueForKey: with "todoSectionTitle". In fact, KVO no way to know that there is such a key.
Bindings are built on top of KVO, so all of this applies to them, too.

Sort two-dimensional NSMutableArray using the last element the inner arrays

I have the following NSMutableArray:
(A|B|C|D|E|255,
F|G|H|I|J|122,
K|L|M|N|O|555)
I am trying to sort the objects in the array using the last component (255, 122, 555). Right now I have the following code:
[myArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
As expected, this method sorts the array by the first element (A, F, K).
I also read about NSSortDescriptor, for example:
NSSortDescriptor *sort = [[[NSSortDescriptor alloc] initWithKey:#"dateModified" ascending:YES] autorelease];
If I use it, it is not clear what I put as a parameter in initWithKey.
You can use a sort descriptor, which takes the last object of the "inner" arrays and sort by that.
Since sort descriptors use key-value coding (KVC), you need to be aware that arrays respond to valueForKey: in a special way - they pass a normal key on to each of the objects that they contain.
You also need to know that methods which do not take a parameter and return a value can be accessed through KVC is if they were normal properties.
All this adds up to the following:
Each of the objects contained in your array (i.e., the inner arrays) have a key that you want to sort by: lastObject
But since the objects are instances of NSArray they will normally pass the key on to the objects that they contain - which is not what you want.
You therefore need to use a special escape in the key name for that situation, which is #, making the actual key to use #lastObject
So to make a long story short, you can do what you want in this way:
NSMutableArray *array = ... // Your array
NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey: #"#lastObject"
ascending: YES
selector: #selector(localizedCaseInsensitiveCompare:)];
[array sortUsingDescriptors: #[sd]];
You'll notice the "#" in the key name, within the string.
This escape character also works for other collection classes, for instance if you want to access allKeys from a dictionary through KVC, the key you should use is #allKeys.
I would use sortUsingComparator:
[myArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2){
return [obj1.lastPropertyForComparison compare:obj2.lastPropertyForComparison];
}
This method allows you to manually compare the properties or members of the objects that you want to order by. I use it almost exclusively for complex sorts, and I haven't noticed any performance differences.
UPDATE:
If your NSMutableArray contains NSArrays with the last object being the number you're trying to rank by, your comparator would be as follows:
[(NSNumber *)[obj1 lastObject] compare:(NSNumber *)[obj2 lastObject]]
Basically, you are grabbing the last object out of each NSArray, which you know is an NSNumber. You use the compare function to return NSOrderedAscending, NSOrderedDescending, or NSOrderedSame. I hope that helps.

Collection Object operator for #firstObject Key-value coding KVC

I'm often required to retrieve the 1st object belonging to a Set. (Using that object as a representative of that set.)
I envision a Collection Object operator, akin to the
#unionOfObjects
BUT clearly
#firstObject
Is it possible to create such a Collection operator!
Currently there's no way to define custom collection operators. However, due to some internal magic there is a funny solution:
NSSet *testSet = [NSSet setWithArray:#[#"one", #(1)]];
id object = [testSet valueForKey:#"#anyObject"];
NSLog(#"anyObject (%#): %#", NSStringFromClass([object class]), object);
UPD: Forgot to mention another handy trick: you can use #lastObject on NSArray!

Walking Core Data graph to-many relationships always seems awkward, is there a better way?

I'm able to get things working fine with Core Data and to achieve my desired results, but I always feel it very awkward when walking to-many relationships because NSSet is, for my typical purposes, fairly useless.
An example is if I have obtained a NSManagedObject of Entity "Zoo" with attribute "nameOfZoo" and to-many relationship "animalCages", the to-many relationship pointing to Entity "AnimalCage" which has attribute "nameOfSpecies" and to-many relationship pointing to Entity "IndividualAnimal"
Zoo [nameOfZoo] ->> AnimalCage [nameOfSpecies] ->> Animals
So, getting the top level Zoo object, that's simple. But then I want to get the data for nameOfSpecies "Canus Lupus". The code I want to write is this:
// Normal NSEntityRequest or whichever it is, I have no gripe with this
NSManagedObject *zoo = ..the request to get the one Zoo..;
// I want to get the object where the key "nameOfSpecies" is set to "CanusLupus"
NSManagedObject *wolf = [[zoo animalCages] object:#"Canus Lupus" forKey:#"nameOfSpecies"];
Obviously, I can't obtain wolf in this manner. Instead, I have to write like 10 lines of code (feels like 100 lines of code) to first obtain the set, then set up a search predicate request, and declare an error variable, execute the request, then get an array of the results, then get the first element of that array.. and if I want to walk further down the tree, to find the animal named "Wolfy" for instance, then I have to do it all over again.
Am I doing things correctly or am I foolishly an easier way? I guess I can put a category on NSSet, maybe I will, but I feel like there should be a built in better way. If not, why?
If you have a data model like this:
Zoo{
name:string
animalCages<-->>AnimalCage.zoo
}
AnimalCage{
nameOfSpecies:string
zoo<<-->Zoo.animalCages
animals<-->>Animal.animalCage
}
Animal{
name:string
animalCage<<-->AnimalCage.animals
}
The to find a specific AnimalCage by name of species for a given Zoo object:
NSString *soughtSpecies=#"Canis Lupis"; // normally this variable would be passed in to the method
NSPredicate *p=[NSPredicate predicateWithFormat:#"nameOfSpecies==%#", soughtSpecies];
NSSet *wolves=[[zoo animalCages] filteredSetUsingPredicate:p];
Or you can use objectPassingTest: if you like blocks.
If you use custom NSManagedObject subclasses, then you get custom accessor methods and can use self.dot notation so the above would be:
Zoo *zoo= // fetch the appropriate zoo object
NSString *soughtSpecies=#"Canis Lupis";
NSPredicate *p=[NSPredicate predicateWithFormat:#"nameOfSpecies==%#", soughtSpecies];
NSSet *wolves=[zoo.animalCages filteredSetUsingPredicate:p];
If you know before hand that you are going to have to find cages a lot, you could wrap the above up in a method on your Zoo class e.g.
#implementation Zoo
//... other stuff
-(AnimalCage *) cageForSpecieNamed:(NSString *) specieName{
NSPredicate *p=[NSPredicate predicateWithFormat:#"nameOfSpecies==%#", specieName];
NSSet *wolves=[self.animalCages filteredSetUsingPredicate:p];
return [wolves anyObject]; // assuming one cage per species
}
Objective-c is intentionally verbose because it was supposed to be "self documenting" and the editor written for the language at the beginning had autocomplete. So, if your used to a more expressive language it might seem you are doing a lot of work but logically you aren't.

Resources