NSDictionary add -objectAtIndex by category - objective-c

I'm writing custom patch for Quartz Composer and I work with structures as input. Structure are NSDictionary in Obj-C so I use NSDictionary's methods to deal with these structures. Depending on how I build these structures key can be NSString type or NSNumber.
Usually QC doesn't give named structures and keys are "0"..."n" or 0...n so I've decided to add -objectAtIndex in a NSDictionary category :
NSDictionary+Utils.h
#import <Foundation/Foundation.h>
#interface NSDictionary (Utils)
- (id)objectAtIndex:(NSInteger)index;
#end
NSDictionary+Utils.m
#import "NSDictionary+Utils.h"
#implementation NSDictionary (Utils)
- (id)objectAtIndex:(NSInteger)index
{
NSString* key = [NSString stringWithFormat:#"%d", index];
return [self objectForKey:key];
}
#end
It works perfectly for one of my project in which I build a structure using Kineme Struc Maker patch.
In another project I use the Queue patch provided by apple. Keys are not NSString type but NSNumber type. I've rewritten the code to be compliant with NSNumber keys :
NSDictionary+Utils.m
#implementation NSDictionary (Utils)
- (id)objectAtIndex:(NSInteger)index
{
NSNumber* key = [NSNumber numberWithInteger:index];
return [self objectForKey:key];
}
#end
Unfortunately it always gives a null value but using :
[self.inputSoundStruct objectForKey:[NSNumber numberWithInteger:0]]
Why does this line of code works but not objectAtIndex ? I must be missing something but what ?

Related

Objective C - Category No Visible selector #removeWhiteSpaceStringWithString

so im trying to add new method for testing using Category from NSString, but some how i must declared like this with following step:
Create Category from NSString with name StringExtension so it will be NSString+StringExtension, after that i declared my own methos that return type is String
so after i define in NSString+StringExtension #interface and #implementation, i tried in my viewController to called it, but first i import the class NSString+StringExtension
after that i do like this
NSString *testString = #"as d a s d";
NSLog(#"===== %#", [testString removeWhiteSpaceStringWithString:testString]);
and it says
No visible #interface for 'NSString' declares the selector 'removeWhiteSpaceStringWithString:'
the question is, why it cannot use like that? i already search and see tutorial doing like that and its possible, but why i'm not able to do that?
so i found this way, but i don't know is this the correct code to use?
NSLog(#"===== %#", [[testString class] removeWhiteSpaceStringWithString:testString]);
anyone have the same case like i am?
Based upon what you have shared with us, it would appear that you defined a class method (with +). It should be an instance method (with -) and then you don’t need the parameter, either. You can simply reference self.
For example:
// NSString+Whitespace.h
#import Foundation;
NS_ASSUME_NONNULL_BEGIN
#interface NSString (Whitespace)
- (NSString *)stringByRemovingWhitespace;
#end
NS_ASSUME_NONNULL_END
And
// NSString+Whitespace.m
#import "NSString+Whitespace.h"
#implementation NSString (Whitespace)
- (NSString *)stringByRemovingWhitespace {
return [self stringByReplacingOccurrencesOfString:#"\\s+"
withString:#""
options:NSRegularExpressionSearch
range:NSMakeRange(0, self.length)];
}
#end
Then you can do:
NSString *testString = #"as d a s d";
NSLog(#"===== %#", [testString stringByRemovingWhitespace]); // ===== asdasd
Obviously, do whatever you want in your implementation, but it illustrates the idea, that you want an instance method and you do not need to pass the string again as a parameter.

NSDictionary or custom NSObject for JSON parsing

I've seen a lot of people use NSDictionary for JSON parsing:
//ViewController.m
NSString* forename = [jsonDict valueForKey:#"forename"];
NSString* surname = [jsonDict valueForKey:#"surname"];
But I've also people creating custom NSObjects from a NSDictionary.
//JSONObject.h
#interface JSONObject : NSObject
#property (nonatomic) NSString* forename;
#property (nonatomic) NSString* surname;
#end
//JSONObject.m
#implementation JSONObect
#synthesize forename = _forename;
#synthesize surname = _surname;
#end
//ViewController.m
JSONObject* jsonObject = [[JSONObject alloc] init];
[jsonObject setForename:[jsonDict valueForKey:#"forename"]];
[jsonObject setSurname:[jsonDict valueForKey:#"surname"]];
And then store these in a NSMutableArray:
NSMutableArray* jsonObjectsArray = [NSMutableArray arrayWithCapacity:20];
[jsonObjectsArray addObject:jsonObject];
Which can be accessed later if needed.
In my case, I have a UITableView that gets it's data from JSON. The data is used at least once but most likely will be used more (eg. on device rotation). The JSON data shouldn't be permanently stored to file as it is updated regularly and is downloaded every time the app launches.
Should I use a custom NSObject or a NSDictionary in my scenario?
One argument for using a custom Object is that it is a few short steps away from using a NSManagedObject, which would let you leverage Core Data to manage your object graph.
The argument for using the NSDictionary is that it's simpler and easier to understand, and you define fewer "minor" classes (and associated h/m files), so less to manage in a project. Also a lot easier to edit/extend in a project "in flux".

iOS JSON serialization for NSObject-based classes

I'd like to JSON-serialize my own custom classes. I'm working in Objective-C / iOS5.
I'd like something to do the following:
Person* person = [self getPerson ]; // Any custom object, NOT based on NSDictionary
NSString* jsonRepresentation = [JsonWriter stringWithObject:person ];
Person* clone = [JsonReader objectFromJson: jsonRepresentation withClass:[Person Class]];
It seems that NSJSONSerialization (and several other libraries) require the 'person' class to be based on NSDictionary etc. I want something that will serialize any custom object that I care to define (within reason).
Let's imagine Person.h looks like this:
#import <Foundation/Foundation.h>
#interface Person : NSObject
#property NSString* firstname;
#property NSString* surname;
#end
I'd like the generated JSON for an instance to look similar to the following:
{"firstname":"Jenson","surname":"Button"}
My app uses ARC. I need something that will both serialise and deserialize using objects.
Many thanks.
This is a tricky one because the only data you can put into JSON are straight up simple objects (think NSString, NSArray, NSNumber…) but not custom classes or scalar types. Why? Without building all sorts of conditional statements to wrap all of those data types into those type of objects, a solution would be something like:
//at the top…
#import <objC/runtime.h>
NSMutableDictionary *muteDictionary = [NSMutableDictionary dictionary];
id YourClass = objc_getClass("YOURCLASSNAME");
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(YourClass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
SEL propertySelector = NSSelectorFromString(propertyName);
if ([classInstance respondsToSelector:propertySelector]) {
[muteDictionary setValue:[classInstance performSelector:propertySelector] forKey:propertyName];
}
}
NSError *jsonError = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:muteDictionary options:0 error:&jsonError];
This is tricky, though because of what I stated before. If you have any scalar types or custom objects, the whole thing comes tumbling down. If it's really critical to get something like this going, I'd suggest looking into investing the time and looking at Ricard's links which allow you to see property types which would assist on the conditional statements needed to wrap the values into NSDictionary-safe objects.
Now you can solve this problem easily using JSONModel. JSONModel is a library that generically serialize/deserialize your object based on Class. You can even use non-nsobject based for property like int, short and float. It can also cater nested-complex JSON.
Deserialize example. By referring to your example, in header file:
#import "JSONModel.h"
#interface Person : JSONModel
#property (nonatomic, strong) NSString* firstname;
#property (nonatomic, strong) NSString* surname;
#end
in implementation file:
#import "JSONModelLib.h"
#import "yourPersonClass.h"
NSString *responseJSON = /*from somewhere*/;
Person *person = [[Person alloc] initWithString:responseJSON error:&err];
if (!err)
{
NSLog(#"%# %#", person.firstname, person.surname):
}
Serialize Example. In implementation file:
#import "JSONModelLib.h"
#import "yourPersonClass.h"
Person *person = [[Person alloc] init];
person.firstname = #"Jenson";
person.surname = #"Uee";
NSLog(#"%#", [person toJSONString]);
maybe this can help JLObjectStrip.
its the same as what jacob said but it iterates even to the property of the class. this will give you dictionary/array then just use sbjson/jsonkit or what ever you prefer to construct your json string.
Try this one BWJSONMatcher
It's really simple as well as convenient.
...
NSString *jsonString = #"{your-json-string}";
YourValueObject *dataModel = [YourValueObject fromJSONString:jsonString];
NSDictionary *jsonObject = #{your-json-object};
YourValueObject *dataModel = [YourValueObject fromJSONObject:jsonObject];
...
YourValueObject *dataModel = instance-of-your-value-object;
NSString *jsonString = [dataModel toJSONString];
NSDictionary *jsonObject = [dataModel toJSONObject];
...
What i do for my objects is i have a method called "toDict" that return a nsdictionary. IN this method i set all attributes i need/want into the dictionary for example
[user setObject:self.first_name forKey:#"first_name"];
[user setObject:self.last_name forKey:#"last_name"];
[user setObject:self.email forKey:#"email"];

What is an import statement where filename contains "+"?

I have seen in some source code (by other developers) something like this:
#import "SomeClass+SomeOtherClass.h"
What is the + for? What does this mean?
Let's say you want to add functionality to an existing class (exp: NSString). You can do that by creating a subclass or you can use a category. And it is common to name the file where the category is defined using the pattern : MyClass+MyCategory.h.
For example, we can add a method reverseString to the class NSString in a category:
// File NSString+reversable.h
- (NSString *)reverseString;
// File NSString+reversable.m
- (NSString *)reverseString
{
// Implementation
}
Have a look at this documentation for more information about categories.
Then you can use that category in another class:
#import "NSString+reversable.h"
// ...
NSString *aString = #"Hello!";
NSString *reversedString = [aString reverseString];
The "+" in header/source filenames is - by convention - used to describe Category implementations.
Example :
Let's say you want to add some functionality to an existing class (e.g.the NSString class). (NSString+Utilities.h)
// NSString+Utilities.h
#interface NSString (Utilities)
-(NSString *) doSthWithThisString;
#end
// NSString+Utilities.m
#implementation NSString (Utilities)
-(NSString *) doSthWithThisString
{
NSMutableString *transformedStr = [self copy];
// Do sth
return transformedStr;
}
#end
Using it :
// in another file
#import "NSString+Utilities.h"
- (void)awakeFromNib
{
NSString* myString = #"This is a string";
// you may use our new NSString method as much as any already-existing one
NSString* newString = [myString doSthWithThisString];
}
Reference :
Mac OS Developer Library - Categories & Extensions
Objective-C Categories - Wiki

Localizing Core Data model properties for display

I'm working on an iOS project that uses a large and fairly complex data model. Some of the entities in the model have corresponding detail view controllers, which include table views that should display localized names and the corresponding values of certain properties.
I've looked at some of Apple's documentation for creating a strings file for a managed object model, but most of it seems geared toward displaying error messages generated by the SDK rather than accessing localized property names directly.
I created a strings file ("ModelModel.strings") for my model file ("Model.xcdatamodel"), and verified that it is loading correctly by looking at -localizationDictionary on my NSManagedObjectModel instance. My question is: how should I access the localized entity and property names in my code? Is there a way to get to them via NSEntityDescription, NSPropertyDescription, etc. or do I have to go through the NSManagedObjectModel every time?
I'm new at localization, so maybe the answer is obvious, but if so, feel free to just give me a nudge in the right direction.
Update
Following #ughoavgfhw's answer, I quickly came up with two categories to accomplish what I needed. Gist: https://gist.github.com/910824
NSEntityDescription:
#interface NSEntityDescription (LocalizedName)
#property (nonatomic, readonly) NSString *localizedName;
#end
#implementation NSEntityDescription (LocalizedName)
#dynamic localizedName;
- (NSString *)localizedName {
static NSString *const localizedNameKeyFormat = #"Entity/%#";
NSString *localizedNameKey = [NSString stringWithFormat:localizedNameKeyFormat, [self name]];
NSString *localizedName = [[[self managedObjectModel] localizationDictionary] objectForKey:localizedNameKey];
if (localizedName) {
return localizedName;
}
return [self name];
}
#end
NSPropertyDescription:
#interface NSPropertyDescription (LocalizedName)
#property (nonatomic, readonly) NSString *localizedName;
#end
#implementation NSPropertyDescription (LocalizedName)
#dynamic localizedName;
- (NSString *)localizedName {
static NSArray *localizedNameKeyFormats = nil;
if (!localizedNameKeyFormats) {
localizedNameKeyFormats = [[NSArray alloc] initWithObjects:#"Property/%#/Entity/%#", #"Property/%#", nil];
}
for (NSString *localizedNameKeyFormat in localizedNameKeyFormats) {
NSString *localizedNameKey = [NSString stringWithFormat:localizedNameKeyFormat, [self name], [[self entity] name]];
NSString *localizedName = [[[[self entity] managedObjectModel] localizationDictionary] objectForKey:localizedNameKey];
if (localizedName) {
return localizedName;
}
}
return [self name];
}
#end
There is no direct way to get that information provided by apple, but you could implement it yourself. You just need to add categories to NSEntityDescription, etc. which create the identifier and ask for the localized value from the model, and then treat it as if it were built in.
Here is an example NSEntityDescription implementation. For properties, you would do something similar, but you should use both the entity and property name in case multiple entities have properties with the same name (you may also need to use both the entity and property name as keys in your localization file. I don't know if the model will create them automatically).
#implementation NSEntityDescription (Localization)
- (NSString *)localizedName {
NSString *key = [NSString stringWithFormat:#"Entity/%#", [self name]];
NSDictionary *dictionary = [[self managedObjectModel] localizationDictionary];
NSString *localizedName = [dictionary objectForKey:key];
return (localizedName ? localizedName : [self name]);
}
#end
Here is a reference for the keys used in the localizations.