-
-
Save tapi/2489095 to your computer and use it in GitHub Desktop.
| // | |
| // NSObject+SMDictionaryMapping.h | |
| // SoundTrack | |
| // | |
| // Created by Paddy O'Brien on 12-04-24. | |
| // Copyright (c) 2012 Paddy O'Brien. All rights reserved. | |
| // | |
| #import <Foundation/Foundation.h> | |
| @protocol SMMappedObject <NSObject> | |
| + (NSDictionary *)mappedKeys; | |
| @end | |
| @interface NSObject (SMDictionaryMapping) | |
| - (id)initWithDictionary:(NSDictionary *)dictionary; | |
| - (void)updateWithDictionary:(NSDictionary *)dictionary; | |
| @end |
| // | |
| // NSObject+DictionaryMapping.m | |
| // SoundTrack | |
| // | |
| // Created by Paddy O'Brien on 12-04-24. | |
| // Copyright (c) 2012 Paddy O'Brien. All rights reserved. | |
| // | |
| #import "NSObject+DictionaryMapping.h" | |
| @interface NSObject () | |
| - (void)mapDictionaryToProperties:(NSDictionary *)dictionary; | |
| @end | |
| @implementation NSObject (DictionaryMapping) | |
| - (id)initWithDictionary:(NSDictionary *)dictionary | |
| { | |
| if (self) { | |
| [self mapDictionaryToProperties:dictionary]; | |
| } | |
| return self; | |
| } | |
| - (void)updatePropertiesWithDictionary:(NSDictionary *)dictionary | |
| { | |
| [self mapDictionaryToProperties:dictionary]; | |
| } | |
| - (void)mapDictionaryToProperties:(NSDictionary *)dictionary | |
| { | |
| NSArray* keys = [dictionary allKeys]; | |
| for (NSString *key in keys) { | |
| NSString *mappedKey = key; | |
| if ([self conformsToProtocol:@protocol(SMMappedObject)] && [self respondsToSelector:@selector(mappedKeys)]) { | |
| NSDictionary *mappedKeys = [self performSelector:@selector(mappedKeys)]; | |
| if ([mappedKeys valueForKey:key]) { | |
| mappedKey = [mappedKeys valueForKey:key]; | |
| } | |
| } | |
| [self setValue:[dictionary valueForKey:key] forKey:mappedKey]; | |
| } | |
| } | |
| @end |
| // | |
| // NSObject+SMDictionaryMapping.h | |
| // | |
| // Created by Paddy O'Brien on 12-04-24. | |
| // Copyright (c) 2012 Paddy O'Brien. All rights reserved. | |
| // | |
| #import <Foundation/Foundation.h> | |
| @protocol SMMappedObject <NSObject> | |
| + (NSDictionary *)mappedKeys; | |
| @end | |
| @interface NSObject (SMDictionaryMapping) | |
| - (id)initWithDictionary:(NSDictionary *)dictionary; | |
| - (void)updateWithDictionary:(NSDictionary *)dictionary; | |
| @end |
| // | |
| // NSObject+DictionaryMapping.m | |
| // | |
| // Created by Paddy O'Brien on 12-04-24. | |
| // Copyright (c) 2012 Paddy O'Brien. All rights reserved. | |
| // | |
| #import "NSObject+DictionaryMapping.h" | |
| @interface NSObject () | |
| - (void)mapDictionaryToProperties:(NSDictionary *)dictionary; | |
| @end | |
| @implementation NSObject (DictionaryMapping) | |
| - (id)initWithDictionary:(NSDictionary *)dictionary | |
| { | |
| if (self) { | |
| [self mapDictionaryToProperties:dictionary]; | |
| } | |
| return self; | |
| } | |
| - (void)updatePropertiesWithDictionary:(NSDictionary *)dictionary | |
| { | |
| [self mapDictionaryToProperties:dictionary]; | |
| } | |
| - (void)mapDictionaryToProperties:(NSDictionary *)dictionary | |
| { | |
| NSArray* keys = [dictionary allKeys]; | |
| for (NSString *key in keys) { | |
| NSString *mappedKey = key; | |
| if ([self conformsToProtocol:@protocol(SMMappedObject)] && [self respondsToSelector:@selector(mappedKeys)]) { | |
| NSDictionary *mappedKeys = [self performSelector:@selector(mappedKeys)]; | |
| if ([mappedKeys valueForKey:key]) { | |
| mappedKey = [mappedKeys valueForKey:key]; | |
| } | |
| } | |
| id value = ([[dictionary valueForKey:key] isKindOfClass:[NSNull class]]) ? nil : [dictionary valueForKey:key]; | |
| [self setValue:value forKey:mappedKey]; | |
| } | |
| } | |
| @end |
Could use reflection in setValue:forKey: or the property setter.
Could you write up a quick snippet that uses this? I think I see the general idea, but I'm having trouble stepping through the code. I'll admit, that might be because I just woke up.
Well, your current approach would work except if you're being passed in a sub-entity (like the user model of a photo model). So you could do something like this:
-(void)setUserModel:(id)theUserModel
{
if ([theUserModel isKindOfClass:[NSDictionary class]])
{
self.userModel = [[UserModel alloc] initWithDictionary:theUserModel];
return;
}
_userModel = theUserModel;
}
Thoughts?
Yes but what if you actually want a dictionary as a property on an object? Interogate the class of the property to see if it conforms to the protocol perhaps?
@warwick an example might look like this
@interface Contact : NSObject <SMMappedObject>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) Email *email;
@end
Later we want to get a bunch of contacts from some webservice that spits back JSON
- (NSArray *)getMyContacts
{
NSData *JSONData = [MagicWebRequestThatRequiresNoSetup GetURL:@"www.myaddressbook.com/contacts"];
NSArray *contacts = [JSONData parseObjectFromJSON];
NSMutableArray* retval = [NSMutableArray array];
for (NSDictionary *contactData in contact) {
Contact *newContact = [[Contact alloc] initWithDictionary:contactData];
[retval addObject:newContact];
}
return retval;
}
The idea is to have you local domain object populated without having to slog through writing your own mappings.
I'm curious about sub-entities and arrays of sub-entities, too. Would it be best to define those in the model class as an array of strings (JSON key names) you want explicitly mapped to sub-entities? One thing all of my JSON keys have in common is that the sub-entity keys are all upper-case first character. This could be the "auto" mode; if a mapped key is upper-case first character it looks for a model class of same name and maps accordingly. I haven't looked to see what something like RestKit does. Just seems like something that could be a single NSObject category rather than a giant library.
Frig I just realized that this wont handle nested objects.