Skip to content

Instantly share code, notes, and snippets.

@loganwright
Created January 27, 2015 18:29
Show Gist options
  • Save loganwright/3e132b1df47938b8ffbe to your computer and use it in GitHub Desktop.
Save loganwright/3e132b1df47938b8ffbe to your computer and use it in GitHub Desktop.
JSONMappableObject

A basic class for mapping JSON objects to their models. Also fully compliant w/ NSCoding automatically. The only thing you need to do is subclass the model, declare your properties, and then override mapping using the following syntax:

- (NSMutableDictionary *)mapping {
  NSMutableDictionary *mapping = [super mapping];
  mapping[@"propertyName"] = @"associatedJSONKey";
}

If your property maps to another JSONMappableObject, or an array of them, use the following syntax for your mapping key:

@``<#propertyName#>``:``<#ClassName#>

- (NSMutableDictionary *)mapping {
  NSMutableDictionary *mapping = [super mapping];
  mapping[@"@propertyName:TypeOfClass"] = @"associatedJSONKey";
}

In the future, I will add the introspection necessary to get the class type and avoid having to change this, but for now, just specify the class.

###Example

GIGitHubIssue.h

#import "JSONMappableObject.h"

@class GIGitHubUser;
@class GIGitHubIssueMilestone;
@class GIGitHubPartialPullRequest;

@interface GIGitHubIssue : JSONMappableObject
@property (nonatomic) NSInteger identifier;
@property (copy, nonatomic) NSString *apiURL;
@property (copy, nonatomic) NSString *htmlURL;
@property (nonatomic) NSInteger number;
@property (copy, nonatomic) NSString *state;
@property (copy, nonatomic) NSString *title;
@property (copy, nonatomic) NSString *bodyMarkdown;
@property (strong, nonatomic) GIGitHubUser *user;
@property (strong, nonatomic) NSArray *labels; // GIGitHubIssueLabel
@property (strong, nonatomic) GIGitHubUser *assignee;
@property (strong, nonatomic) GIGitHubIssueMilestone *milestone;
@property (strong, nonatomic) GIGitHubPartialPullRequest *pullRequest;
@property (nonatomic) NSInteger commentCount;
@property (copy, nonatomic) NSString *closedAt;
@property (copy, nonatomic) NSString *createdAt;
@property (copy, nonatomic) NSString *updatedAt;
@property (nonatomic) BOOL locked;
@end

GIGitHubIssue.m

#import "GIGitHubIssue.h"

@implementation GIGitHubIssue
- (NSMutableDictionary *)mapping {
    NSMutableDictionary *mapping = [super mapping];
    mapping[@"identifier"] = @"id";
    mapping[@"apiURL"] = @"url";
    mapping[@"htmlURL"] = @"html_url";
    mapping[@"number"] = @"number";
    mapping[@"state"] = @"state";
    mapping[@"title"] = @"title";
    mapping[@"bodyMarkdown"] = @"body";
    mapping[@"@user:GIGitHubUser"] = @"user";
    mapping[@"@labels:GIGitHubIssueLabel"] = @"labels";
    mapping[@"@assignee:GIGitHubUser"] = @"assignee";
    mapping[@"@milestone:GIGitHubIssueMilestone"] = @"milestone";
    mapping[@"commentCount"] = @"comments";
    mapping[@"@pullRequest:GIGitHubPartialPullRequest"] = @"pull_request";
    mapping[@"closedAt"] = @"closed_at";
    mapping[@"createdAt"] = @"created_at";
    mapping[@"updatedAt"] = @"updated_at";
    mapping[@"locked"] = @"locked";
    return mapping;
}
@end
//
// Created by Logan Wright on 1/24/15.
// Copyright (c) 2015 LowriDevs. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface JSONMappableObject : NSObject <NSCoding>
- (NSMutableDictionary *)mapping;
- (instancetype)initWithJSONRepresentation:(NSDictionary *)jsonRepresentation;
@end
//
// JSONObject.m
// GitIssues-iOS
//
// Created by Logan Wright on 1/24/15.
// Copyright (c) 2015 LowriDevs. All rights reserved.
//
#import "JSONMappableObject.h"
#import <objc/objc-runtime.h>
@implementation JSONMappableObject
- (NSMutableDictionary *)mapping {
return [NSMutableDictionary dictionary];
}
- (instancetype)init {
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:@"-init is not a valid initializer for a JSONObject class."
userInfo:nil];
return nil;
}
- (instancetype)initWithJSONRepresentation:(NSDictionary *)jsonRepresentation {
self = [super init];
if (self) {
NSDictionary *mapping = [self mapping];
for (__strong NSString *propertyName in mapping.allKeys) {
NSString *associatedJSONKey = mapping[propertyName];
id associatedValue = jsonRepresentation[associatedJSONKey];
if ([propertyName hasPrefix:@"@"]) { // Use prefix to indicate another JSONObject
propertyName = [propertyName substringFromIndex:1];
NSArray *components = [propertyName componentsSeparatedByString:@":"];
NSString *assertionMessage = @"Declaring a property as a subclass of JSONMappableObject requires the following syntax '@propertyName:ClassName'";
NSAssert(components.count == 2, assertionMessage);
propertyName = components.firstObject;
NSString *className = components.lastObject;
Class propertyClass = NSClassFromString(className);
if (!associatedValue || [associatedValue isKindOfClass:[NSNull class]]) {
[self setValue:nil forKey:propertyName];
} else if ([associatedValue isKindOfClass:[NSArray class]]) {
NSMutableArray *objects = [NSMutableArray array];
for (id ob in (NSArray *)associatedValue) {
JSONMappableObject *mappedOb = [[propertyClass alloc] initWithJSONRepresentation:ob];
[objects addObject:mappedOb];
}
[self setValue:objects forKey:propertyName];
} else {
JSONMappableObject *jsonObject = [[propertyClass alloc] initWithJSONRepresentation:associatedValue];
[self setValue:jsonObject forKey:propertyName];
}
} else {
if (!associatedValue || [associatedValue isKindOfClass:[NSNull class]]) {
[self setValue:nil forKey:propertyName];
continue;
}
[self setValue:associatedValue forKey:propertyName];
}
}
}
return self;
}
#pragma mark - NSCoding
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
for (NSString *property in [self properties]) {
[self setValue:[aDecoder decodeObjectForKey:property] forKey:property];
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
for (NSString *property in [self properties]) {
[aCoder encodeObject:[self valueForKey:property] forKey:property];
}
}
- (NSArray *)properties {
unsigned count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
NSMutableArray *propertyNames = [NSMutableArray array];
for (int i = 0; i < count; i++) {
objc_property_t property = properties[i];
const char * name = property_getName(property);
NSString *stringName = [NSString stringWithUTF8String:name];
[propertyNames addObject:stringName];
}
free(properties);
return propertyNames;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment