Yes, you can do it automatically. First import them into your class:
#import <objc/runtime.h> #import <objc/message.h>
Now add this method, which will use low-level methods to get property names:
- (NSArray *)propertyKeys { NSMutableArray *array = [NSMutableArray array]; Class class = [self class]; while (class != [NSObject class]) { unsigned int propertyCount; objc_property_t *properties = class_copyPropertyList(class, &propertyCount); for (int i = 0; i < propertyCount; i++) { //get property objc_property_t property = properties[i]; const char *propertyName = property_getName(property); NSString *key = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]; //check if read-only BOOL readonly = NO; const char *attributes = property_getAttributes(property); NSString *encoding = [NSString stringWithCString:attributes encoding:NSUTF8StringEncoding]; if ([[encoding componentsSeparatedByString:@","] containsObject:@"R"]) { readonly = YES; //see if there is a backing ivar with a KVC-compliant name NSRange iVarRange = [encoding rangeOfString:@",V"]; if (iVarRange.location != NSNotFound) { NSString *iVarName = [encoding substringFromIndex:iVarRange.location + 2]; if ([iVarName isEqualToString:key] || [iVarName isEqualToString:[@"_" stringByAppendingString:key]]) { //setValue:forKey: will still work readonly = NO; } } } if (!readonly) { //exclude read-only properties [array addObject:key]; } } free(properties); class = [class superclass]; } return array; }
The following are your NSCoder methods:
- (id)initWithCoder:(NSCoder *)aDecoder { if ((self = [self init])) { for (NSString *key in [self propertyKeys]) { id value = [aDecoder decodeObjectForKey:key]; [self setValue:value forKey:key]; } } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { for (NSString *key in [self propertyKeys]) { id value = [self valueForKey:key]; [aCoder encodeObject:value forKey:key]; } }
You have to be a little careful with this. The following reservations exist:
This will work for properties that are numbers, bools, objects, etc., but custom structures will not work. Also, if any properties of your class are objects that do not support NSCoding themes, this will not work.
This will only work with synthesized properties, not with ivars.
You can add error handling by checking the type of the value in encodeWithCoder before encoding it or by overriding the setValueForUndefinedKey method to handle the problem more elegantly.
UPDATE:
I combined these methods into a library: https://github.com/nicklockwood/AutoCoding - the library implements these methods as a category in NSObject, so any class can be saved or loaded, and also adds support for encoding inherited properties that my original answer did not handles.
UPDATE 2:
I updated the answer to properly deal with inherited and read-only properties.
source share