Add NSArray to CLBeaconRegion via setValue forKey causes Error - objective-c

Is it possible to add an Array to a CLBeaconRegion via a setValue forKey?
I've tried to add it:
[regionAdvert setValue:haveArray forKey:#"advertArray"];
But I receive just the following error:
> Terminating app due to uncaught exception 'NSUnknownKeyException',
> reason: '[<CLBeaconRegion 0x15379600> setValue:forUndefinedKey:]: this
> class is not key value coding-compliant for the key advertArray.'

setValue:forKey: is a KVC method which is basically available on every class - but that doesn't mean you can just pass anything to it. The key you use needs to be an existing property (well, really a method as that is what gets called, or a handled non-existent key). Anyway, the point is that you should know the key exists before trying to set it.
Technically you can use objc_setAssociatedObject to associated arbitrary objects with other objects but in this case I would encourage you to instead use the major and minor properties to check the purpose of the region before deciding how to display your alert.

No, you can't do this. If you want to associate other data with a CLBeaconRegion, then you can use other data structures like NSDictionary. Since each CLBeaconRegion is constructed with a unique "identifier" string, you can use this identifier as a key into a NSDictionary to store your other arbitrary data objects.
Also, you aren't supposed to change a CLBeaconRegion once constructed. The fields are immutable. This isn't really a big deal -- there are only three fields. Just create a new CLBeaconRegion, copying any of the identifiers from the old CLBeaconRegion that you wish to retain.

Related

Sorting the results of an NSFetchRequest of objects that implement compare:

I have a NSManagedObject subclass that implements the compare: message. I want to retrieve these objects and sort them using this compare method, but apparently I have to provide a key for the NSSortDescriptor.
I don't want to provide any key, as the object itself already knows how to compare itself.
If I provide nil as the key for the key:
r.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:nil
ascending:YES selector:#selector(compare:)]];
I get the following error:
failed: ((62) equal to ([[self.lib tags] count])) failed: throwing
"keypath #self not found in entity "
How can I make sure that NSSortDescriptor ignores any key and just uses compare: instead?
Did you try supplying the key #"self"?
The problem was that I was using a SQLite store, and you can only use modelled properties in a NSFetchRequest that relies on a SQLite store.
It was very confusing, as it worked on my tests (I was using an In Memory store) and failed miserably in the App itself. A better error description would have saved a lot of time.

Why must the key of an NSDictionary conform to NSCopying

I noticed that for an object to be a key for an NSDictionary it must conform to NSCopying.
Why is this so? I would understand that all keys must implement hash, but why NSCopying?
Because the keys are copied. You wouldn't want a key to be stored as a reference to the very same object you started with, would you? It would be terrible if d[myThing] = myValue retained a reference to a possibly mutable instance myThing. That would mean that the dictionary could get mutated behind its own back.
NSDictionary guaranties that if you store a value with some key x this key is fixed and you can retrieve this value with the equivalent key y (y.isEqual(x) == YES). There are only two possibilities to do so:
Copy keys to prevent them from changing.
Demand that keys are immutable.
Apple decided that for most cases coping keys is better.
In case you need a dictionary were keys are not copied (for example keys do not implement NSCopying or coping is too expensive) you can use NSMapTable.
For example you can use
[NSMapTable strongToStrongObjectsMapTable]
to store keys and values as a strong references.

Objective-C - aligning class properties with json dictionary data

I'm relatively new to Objective-C (mainly using cocoa/apple foundation framework), but a long-time C++ programmer, so I'll start by explaining what I'm trying to accomplish; It's quite possible my approach isn't the best one, so I'd love to hear any suggestions for a different approach.
I've got a text file in json format, just made this quick example:
"section1" : {
"director" : "Sample Name 1",
"writers" : {
"name" : "Example Name 1",
"name" : "Example Name 2",
},
},
And I've got a class with properties which I want to match with the data inside this file, since I'm planning to parse this file and store some of the values in an instance of this class. My class header would look roughly like this:
#interface SongData : NSObject
#property (nonatomic, copy) NSString *director;
#property (nonatomic, copy) NSArray *writers;
#end
So I'm trying to find the cleanest, somewhat "data-driven" way to store this json data into the correct properties. Basically I want a way to loop through the json "dictionary" of data, and somehow use the "keys" to match with the correct property in my class and assign the value to that property. My first crack at it, I created a dictionary where the "key" is the actual key to lookup the proper data inside the json data, and the "value" would be a pointer to the correct class property. Sort of like this:
NSDictionary *descriptionDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
director, #"name",
writers, #"writers",
nil];
Then the idea was to loop through the json data (which is stored in another NSDictionary), use each key from my "descriptionDictionary" to lookup the appropriate value in the json data, then use the matching "value" of the description dictionary based on the same key (which I was hoping is sort of a pointer to the actual class property?) and set that properties value to the value from the json data. Perhaps an example will make it a bit more clear :-). Lets assume "jsonDictionary" is the result of parsing my json data, and I'm already inside section1, so there should be a 1-1 match between keys that make up the jsonData NSDictionary and the keys that make up my descriptionDictionary NSDictionary:
[descriptionDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
value = [jsonDictionary objectForKey:propertyName];
}];
Now "value" would technically be pointing to one of my classes properties, and I want to assign relevant json data to it (ie director = #"Sample Name 1"). I know there's problems with what I'm doing (I realize that I probably can't just dereference a pointer to a random class property and assign an arbitrary object of unknown type to it, hoping it all matches up :-) but hoping my example at least illustrates what I'm trying to do so someone can tell me a better way.
Have a look at KVC (source: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueCoding/Articles/BasicPrinciples.html)
Setting Attribute Values Using Key-Value Coding
The method setValue:forKey: sets the value of the specified key,
relative to the receiver, to the provided value. The default
implementation of setValue:forKey: automatically unwraps NSValue
objects that represent scalars and structs and assigns them to the
property. See “Scalar and Structure Support” for details on the
wrapping and unwrapping semantics.
If the specified key does not exist, the receiver is sent a
setValue:forUndefinedKey: message. The default implementation of
setValue:forUndefinedKey: raises an NSUndefinedKeyException; however,
subclasses can override this method to handle the request in a custom
manner.
The method setValue:forKeyPath: behaves in a similar fashion, but it
is able to handle a key path as well as a single key.
Finally, setValuesForKeysWithDictionary: sets the properties of the
receiver with the values in the specified dictionary, using the
dictionary keys to identify the properties. The default implementation
invokes setValue:forKey: for each key-value pair, substituting nil for
NSNull objects as required.
One additional issue that you should consider is what happens when an
attempt is made to set a non-object property to a nil value. In this
case, the receiver sends itself a setNilValueForKey: message. The
default implementation of setNilValueForKey: raises an
NSInvalidArgumentException. Your application can override this method
to substitute a default value or a marker value, and then invoke
setValue:forKey: with the new value.
As long as your class properties have the same names as the JSON fields you can use the setValuesForKeysWithDictionary: and pass in the JSON root dictionary.
For keys/properties that are named differently you can simply override the setValue:forUndefinedKey: and set the appropriate property yourself.
Finally there is the case of a value being represented by a different type in the JSON than in the property. For example NSURL would be an NSString instead. Here you can simply check the class of the passed parameter and if it does not match the IVAR, do a conversion.

KVC for an entry in NSDictionary

I have an NSDictionary of strings mapping to numbers that contains properties of an object to be edited in the UI, e.g.
Length : 1
Height : 2
Now trying to bind text fields in the UI to entries in the dictionary using the key of items in the dictionary in the key path fails miserably, so is trying to simply access entries in the dictionary with valueForKey: like
NSLog(#"KVC: %#", [self valueForKey:#"keysAndValues"]);
--> OK, Dump shows that Length is stored in the dictionary.
NSLog(#"KVC: %#", [self valueForKey:#"keysAndValues.Length"]);
[< YourClass 0x114608de0> valueForUndefinedKey:]:
this class is not key value coding-compliant for the key keysAndValues.Length.
Any suggestions on how to set up UI bindings with a pretty generic model having all it's stuff stored in a dictionary? I'm pretty sure I've read that KVC/KVO with paths to access individual entries in a dictionary actually works - but can't find the topic any more..
You want to use valueForKeyPath:, which takes a list of keys, separated by periods. Just using valueForKey can't include a series of keys as in your code.

How to test property existence and type based on NSString typed key?

In my quest to update a Core Data model within my iOS project, I'm querying a server for JSON objects that correspond - to some extent - with the managed entities of my model. The end result I'm striving for is a reliable update solution from JSON output.
For the examples in this question, I'll name the core data managed object existingObj and the incoming JSON deserialized dictionary updateDict. The tricky part is dealing with these facts:
Not all properties of the existingObj are present in the updateDict
Not all properties of the updateDict are available in the extistingObj.
Not all types of existingObj's properties match the JSON deserialized properties. (some strings may need a custom Objective-C wrapper).
updateDict may contain values for keys that are uninitialized (nil) in existingObj.
This means that while iterating through the updated dictionaries, there has to be some testing of properties back and forth. First I have to test whether the properties of the updateDict exist in existingObj, then I set the value using KVC, like so:
// key is an NSString, e.g. #"displayName"
if ([existingObj respondsToSelector:NSSelectorFromString(key)) {
[existingObj setValue:[updateDict objectForKey:key] forKey:key];
}
Although this part works, I don't like the fact that I'm actually testing for displayName as a getter, while I'm about to call the setDisplayName: setter (indirectly via KVC). What I'd rather to is something like [existingObj hasWritablePropertyWithName:key], but something that does this I can't find.
This makes for subquestion A: How does one test for a property setter, if you only have the property's name?
The next part is where I'd like to automate the property identification based on their types. If both the updateDict and the existingObj have an NSString for key #"displayName", setting the new value is easy. However, if the updateDict contains an NSString for key #"color" that is #"niceShadeOfGreen", I'd like to transform this into the right UIColor instance. But how do I test the type of the receiving property in existingObj so I know when to convert values and when to simply assign? I was hoping for something along the lines of typeOfSelector:
if ([existingObj typeOfSelector:sel] == [[updateDict objectForKey:key] class]) {
// regular assignment
} else {
// perform custom assignment
}
Of course this is boguscode. I can't rely on testing the type of the existingObj-property's value, for it may be unitialized or nil.
Subquestion B: How does one test for the type of a property, if you only have the property's name?
I guess that's it. I figured this must be a dupe of something that's already on here, but I couldn't find it. Maybe you guys can?
Cheers, EP.
P.S. If you'd have a better way to synchronize custom Objective-C objects to deserialized JSON objects, please do share! In the end, the result is what counts.
If you want to query whether an object has a setter for a given KVC key called key which corresponds to a declared property, you need to check whether it responds to a selector method called setKey: (starts with set, capitalise the first character in key, add a trailing colon). For instance,
NSString *key = #"displayName";
NSString *setterStr = [NSString stringWithFormat:#"set%#%#:",
[[key substringToIndex:1] capitalizedString],
[key substringFromIndex:1]];
if ([obj respondsToSelector:NSSelectorFromString(setterStr)]) {
NSLog(#"found the setter!");
[obj setValue:someValue forKey:key];
}
Two remarks:
Even though properties can have setters with names that do not follow the pattern described above, they wouldn’t be KVC compliant, so it is safe to check for set<Key>: since you’re using KVC to set the corresponding value.
KVC doesn’t use the setter method only. If it doesn’t find a setter method, it checks whether the class allows direct access to instance variables and, if so, use the instance variable to set the value. Also, if no setter method or instance variable is found, it sends -setValue:forUndefinedKey: to the receiver, whose class might have overridden the standard implementation that throws an exception. This is described in the Key-Value Coding Programming Guide.That said, if you’re always using properties, checking for the setter method should be safe.
As for your second question, it is not possible to query the runtime to know the actual Objective-C class of a property. From the runtime perspective, there’s an implementation specific type encoding for properties and general types (such as method parameters/return types). This type encoding uses a single encoding (namely #) for any Objective-C object, so the type encoding of an NSString property is the same as the type encoding of a UIColor property since they’re both Objective-C classes.
If you do need this functionality, one alternative is to process your classes and add a class method that returns a dictionary with keys and corresponding types for every property (or the ones you’re interested in) declared in that class and superclasses, or maybe some sort of description language. You’d have to do this on your own and rely on information not available during runtime.