JSON in iOS

Jul 4, 2012 · Follow on Twitter and Mastodon objc

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.

Discussions & More

Please share any ideas, feedback or comments you may have in the Disqus section below, or by replying on Twitter or Mastodon..

If you found this text interesting, make sure to follow me on Twitter and Mastodon for more content like this, and to be notified when new content is published.

If you like & want to support my work, please consider sponsoring me on GitHub Sponsors.