Blog

JSON in iOS


I’m currently creating an iOS app that will share data using JSON. Working with JSON is trivial in iOS 5, since there’s now a great, native JSON serializer and deserializer.

Using these classes works well, but I find it tedious to write the code for creating and parsing JSON data over and over again. To make my code cleaner and get loose coupling to the native classes, I decided to define a JSON serializer/deserializer protocol and implement it using the native classes.

I first created this simple protocol

#import <Foundation/Foundation.h>

@protocol ObjectSerializer <NSObject>

- (id)deserializeStringToObject:(NSString *)string;
- (NSString *)serializeObjectToString:(id)object;

@end

then created a small implementation of this protocol, which looks like this:

#import <Foundation/Foundation.h>
#import "ObjectSerializer.h"

@interface NativeJsonSerializer : NSObject<ObjectSerializer>

@end
#import "NativeJsonSerializer.h"

@implementation NativeJsonSerializer

- (id)deserializeStringToObject:(NSString *)string
{
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
    NSError *error = nil;
    id result = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
    if (!result) {
        NSLog(@"%@", error.description);
    }

    return result;
}

- (NSString *)serializeObjectToString:(id)data
{
    NSError *error;
    NSData *result = [NSJSONSerialization dataWithJSONObject:data options:NSJSONReadingAllowFragments|NSJSONWritingPrettyPrinted error:&error];
    if (!result) {
        NSLog(@"%@", error.description);
    }

    return [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
}

@end

Finally, I added some tests that test a lot of possible JSON operations:

#import <SenTestingKit/SenTestingKit.h>

@interface NativeJsonSerializerTests : SenTestCase

@end
#import "NativeJsonSerializerTests.h"
#import "NativeJsonSerializer.h"

@implementation NativeJsonSerializerTests

NativeJsonSerializer *_serializer;

- (void)setUp {
    [super setUp];
    _serializer = [[NativeJsonSerializer alloc] init];
}

- (void)tearDown {
    // Tear-down code here.
    [super tearDown];
}

- (void)test_serializerShouldDeserializeArray {
    NSArray *result = [_serializer deserializeStringToObject:@"[\"foo\",\"bar\"]"];

    bool error = FALSE;
    error = error && !(result.count == 2);
    error = error && !([(NSString *)[result objectAtIndex:0] isEqualToString:@"foo"]);
    error = error && !([(NSString *)[result objectAtIndex:1] isEqualToString:@"bar"]);

    if (error)
        STFail(@"NativeJsonSerializer could not deserialize array");
}

- (void)test_serializerShouldDeserializeDictionary {
    NSDictionary *result = [_serializer deserializeStringToObject:@"{\"foo\":\"bar\",\"bar\":\"foo\"}"];

    bool error = FALSE;
    error = error && !(result.count == 2);
    error = error && !([(NSString *)[result objectForKey:@"foo"] isEqualToString:@"bar"]);
    error = error && !([(NSString *)[result objectForKey:@"bar"] isEqualToString:@"foo"]);

    if (error)
        STFail(@"NativeJsonSerializer could not deserialize dictionary");
}

- (void)test_serializerShouldDeserializeFloat {
    NSNumber *result = [_serializer deserializeStringToObject:@"1.1"];
    if (![result floatValue] == 1.1)
        STFail(@"NativeJsonSerializer could not deserialize float");
}

- (void)test_serializerShouldDeserializeInteger {
    NSNumber *result = [_serializer deserializeStringToObject:@"1"];
    if (![result intValue] == 1)
        STFail(@"NativeJsonSerializer could not deserialize integer");
}

- (void)test_serializerShouldDeserializeString {
    NSString *result = [_serializer deserializeStringToObject:@"\"Foo Bar\""];
    if (![result isEqualToString:@"Foo Bar"])
        STFail(@"NativeJsonSerializer could not deserialize string");
}

- (void)test_serializerShouldSerializeArray {
    NSMutableArray *data = [[NSMutableArray alloc] initWithCapacity:2];
    [data addObject:@"foo"];
    [data addObject:@"bar"];

    NSString *result = [_serializer serializeObjectToString:data];
    NSString *expectedResult = @"[\n  \"foo\",\n  \"bar\"\n]";

    if (![result isEqualToString:expectedResult])
        STFail(@"NativeJsonSerializer could not serialize array");
}

- (void)test_serializerShouldSerializeDictionary {
    NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
    [data setObject:@"bar" forKey:@"foo"];
    [data setObject:@"foo" forKey:@"bar"];

    NSString *result = [_serializer serializeObjectToString:data];
    NSString *expectedResult = @"{\n  \"foo\" : \"bar\",\n  \"bar\" : \"foo\"\n}";

    if (![result isEqualToString:expectedResult])
        STFail(@"NativeJsonSerializer could not serialize dictionary");
}

- (void)test_serializerShouldSerializeFloat {
    NSString *result = [_serializer serializeObjectToString:[NSNumber numberWithFloat:1.1]];
    if (![result isEqualToString:@"1.1"])
        STFail(@"NativeJsonSerializer could not serialize float");
}

- (void)test_serializerShouldSerializeInteger {
    NSString *result = [_serializer serializeObjectToString:[NSNumber numberWithInt:1]];
    if (![result isEqualToString:@"1"])
        STFail(@"NativeJsonSerializer could not serialize integer");
}

- (void)test_serializerShouldSerializeString {
    NSString *result = [_serializer serializeObjectToString:@"Foo Bar"];
    if (![result isEqualToString:@"\"Foo Bar\""])
        STFail(@"NativeJsonSerializer could not serialize string");
}

@end

With this in place, handling JSON works like a dream in Objective-C.