iOS Category Inheritance to encapsulate NSJSONSerialization

ios, objective-c, objective-c-category

Solution

In the code:

+ (NSString *)JSONString;

The `+` (Plus sign) indicates a class method. You most likely want an instance method, like so:

- (NSString *)JSONString;

Regarding the methodology of how to implement serialization: This is quite the "aside". The scope of what you are asking is very broad and you have given very few specifics. This is really a function of the scope of your needs and how much time/energy you want to invest, but to weigh-in an opinion:

A category on `NSObject` seems a bit overly-broad to me. I would think that it's unlikely to yield better results than, what seems like a more logical choice, a protocol.

I frankly wish `NSJSONSerialization` had declared a protocol for serializing our own custom model objects, similar to `NSCoding`, but they didn't. That's still generally the route I go when encoding object into `JSON`. A protocol could look something like this:

NSString * const MySerializationClassKey = @"MySerializationClassKey";
@protocol MySerializationProtocol <NSObject>
@required
-(NSDictionary *)dictionarySerialization;
-(id)initFromDictionarySerialization:(NSDictionary *)dictionary;
@end

And then your custom model objects can serialize and de-serialize like this:(Overly simplistic)

@interface MyModel() <MySerializationProtocol>
@property (strong) NSString *name;
@end
@implementation MyModel
NSString * const MyModelNameKey = @"MyModelNameKey";
@synthesize name = _name;
-(NSDictionary *)dictionarySerialization{
    // encode object as dictionary
    return @{MySerializationClassKey: NSStringFromClass(self.class), MyModelNameKey: _name.copy};
}
-(id)initFromDictionarySerialization:(NSDictionary *)dictionary{
    if ((self = [super init])){
        _name = [dictionary objectForKey:MyModelNameKey];
    }
    return self;
}
@end

Now suppose, for the sake of not typing a complete serialization library into an answer, that you only ever had a flat `NSArray` of these model objects. You could create a class that uses `NSJSONSerialization` in cooperation with these methods. To make each call easier, like so:

@interface MySerializer : NSObject
@end
@implementation MySerializer
+(NSData *)jsonDataFromObject:(NSMutableArray *)array{
    for (NSUInteger index = 0; index < array.count; index++) {
        NSObject *object = [array objectAtIndex:index];
        if ([object conformsToProtocol:@protocol(MySerializationProtocol)]){
            NSDictionary *dictRep = [(id<MySerializationProtocol>)object dictionarySerialization];
            [array replaceObjectAtIndex:index withObject:dictRep];
        }
    }
    return [NSJSONSerialization dataWithJSONObject:array options:0 error:nil];
}
+(NSMutableArray *)objectFromJSONData:(NSData *)data{
    NSMutableArray *array = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
    for (NSUInteger index = 0; index < array.count; index++) {
        NSObject *object = [array objectAtIndex:index];
        if ([object isKindOfClass:[NSDictionary class]]){
            NSDictionary *dict = (NSDictionary *)object;
            NSString *className = [dict objectForKey:MySerializationClassKey];
            if (className.length > 0) {
                NSObject *deserialized = [[NSClassFromString(className) alloc] initFromDictionarySerialization:dict];
                if (deserialized) [array replaceObjectAtIndex:index withObject:deserialized];
            }
        }
    }
    return array;
}
@end

Now obviously to get any mileage out of code like this you would have to recursively traverse the possible arrays and dictionaries. But this is my preferred route.

Problem

I am attempting to encapsulate NSJSONSerialization methods in a `Category` on `NSObject` instead of repeating the [de]/serialization throughout the code. .h ``` #import <Foundation/Foundation.h> @interface NSObject (AYIAdditions) + (NSString *)JSONString; + (id)objectFromJSONString; + (id)objectFromJSONData; @end ``` However, I am receiving the error: No visible @interface for 'NSMutableDictionary' declares the selector 'JSONString' `NSMutableDictionary` inherits `NSObject` and therefore should inherit these category methods, right? What am I missing? As an aside, is using categories a good approach (mentioned by Ray Wenderlich). Or should I simply create an custom object with some class methods.

Original source