Version 0.9.0. See changelog.

This commit is contained in:
Kevin Renskers
2013-01-28 16:38:11 +00:00
parent 0f1e87170c
commit 9aaa668109
9 changed files with 325 additions and 62 deletions

View File

@@ -1,3 +1,7 @@
0.9.0 - January 28, 2013
- Support primitive types (int, float, bool, double)
- Improved performance due to accessor creation on start up
0.4.1 - January 7, 2013
- Fix endless loop if no transformKey: method was present

View File

@@ -13,8 +13,8 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Removing it for debugging, starting with a clean slate every time
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"NSUserDefaultUserName"];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"NSUserDefaultUserId"];
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];

View File

@@ -14,32 +14,37 @@
- (void)setUp {
[super setUp];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"NSUserDefaultUserName"];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"NSUserDefaultUserId"];
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
}
- (void)tearDown {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"NSUserDefaultUserName"];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"NSUserDefaultUserId"];
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
[super tearDown];
}
- (void)testDefaults {
STAssertEqualObjects([GVUserDefaults standardUserDefaults].userName, @"default", nil);
STAssertEqualObjects([GVUserDefaults standardUserDefaults].userId, @1, nil);
STAssertEquals([GVUserDefaults standardUserDefaults].integerValue, 123, nil);
STAssertEquals([GVUserDefaults standardUserDefaults].boolValue, YES, nil);
}
- (void)testSetters {
[GVUserDefaults standardUserDefaults].userName = @"changed";
[GVUserDefaults standardUserDefaults].userId = @2;
[GVUserDefaults standardUserDefaults].integerValue = 456;
STAssertEqualObjects([GVUserDefaults standardUserDefaults].userName, @"changed", nil);
STAssertEqualObjects([GVUserDefaults standardUserDefaults].userId, @2, nil);
STAssertEquals([GVUserDefaults standardUserDefaults].integerValue, 456, nil);
}
- (void)testGetters {
STAssertEqualObjects([GVUserDefaults standardUserDefaults].userName, [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultUserName"], nil);
STAssertEqualObjects([GVUserDefaults standardUserDefaults].userId, [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultUserId"], nil);
STAssertEquals([GVUserDefaults standardUserDefaults].integerValue, [[NSUserDefaults standardUserDefaults] integerForKey:@"NSUserDefaultIntegerValue"], nil);
}
@end

View File

@@ -12,5 +12,8 @@
@property (nonatomic, weak) NSString *userName;
@property (nonatomic, weak) NSNumber *userId;
@property (nonatomic) NSInteger integerValue;
@property (nonatomic) BOOL boolValue;
@property (nonatomic) float floatValue;
@end

View File

@@ -10,10 +10,19 @@
@implementation GVUserDefaults (Properties)
@dynamic userName;
@dynamic userId;
@dynamic integerValue;
@dynamic boolValue;
@dynamic floatValue;
- (NSDictionary *)setupDefaults {
return @{
@"userName": @"default",
@"userId": @1
@"userId": @1,
@"integerValue": @123,
@"boolValue": @YES,
@"floatValue": @12.3,
};
}
@@ -22,7 +31,4 @@
return [NSString stringWithFormat:@"NSUserDefault%@", key];
}
@dynamic userName;
@dynamic userId;
@end

View File

@@ -15,28 +15,106 @@
- (void)viewDidLoad {
[super viewDidLoad];
NSString *test;
LogTimestamp;
test = [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultUserName"];
LogTimestamp;
test = [GVUserDefaults standardUserDefaults].userName;
NSLog(@"userName: %@", test);
LogTimestamp;
[[NSUserDefaults standardUserDefaults] setObject:@"Hello!" forKey:@"NSUSerDefaultUserName"];
LogTimestamp;
[GVUserDefaults standardUserDefaults].userName = @"Hello!";
LogTimestamp;
NSLog(@"Should be null:");
NSLog(@"userName: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultUserName"]);
NSLog(@"--------------------");
NSLog(@"Should be default:");
NSLog(@"userName: %@", [GVUserDefaults standardUserDefaults].userName);
NSLog(@"--------------------");
[[NSUserDefaults standardUserDefaults] setObject:@"test1" forKey:@"NSUserDefaultUserName"];
NSLog(@"Should all be test1:");
NSLog(@"userName: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultUserName"]);
NSLog(@"userName: %@", [GVUserDefaults standardUserDefaults].userName);
NSLog(@"--------------------");
[GVUserDefaults standardUserDefaults].userName = @"Test 2";
NSLog(@"Should all be test2:");
NSLog(@"userName: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultUserName"]);
NSLog(@"userName: %@", [GVUserDefaults standardUserDefaults].userName);
NSLog(@"--------------------");
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"NSUserDefaultUserName"];
NSLog(@"Should all be default:");
NSLog(@"userName: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultUserName"]);
NSLog(@"userName: %@", [GVUserDefaults standardUserDefaults].userName);
NSLog(@"--------------------");
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
NSLog(@"Should all be 123:");
NSLog(@"integerValue: %i", [GVUserDefaults standardUserDefaults].integerValue);
NSLog(@"integerValue: %i", [[NSUserDefaults standardUserDefaults] integerForKey:@"NSUserDefaultIntegerValue"]);
NSLog(@"integerValue OBJECT: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultIntegerValue"]);
NSLog(@"--------------------");
[GVUserDefaults standardUserDefaults].integerValue = 789;
NSLog(@"Should all be 789:");
NSLog(@"integerValue: %i", [GVUserDefaults standardUserDefaults].integerValue);
NSLog(@"integerValue: %i", [[NSUserDefaults standardUserDefaults] integerForKey:@"NSUserDefaultIntegerValue"]);
NSLog(@"integerValue OBJECT: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultIntegerValue"]);
NSLog(@"--------------------");
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"NSUserDefaultIntegerValue"];
NSLog(@"Should all be 123:");
NSLog(@"integerValue: %i", [GVUserDefaults standardUserDefaults].integerValue);
NSLog(@"integerValue: %i", [[NSUserDefaults standardUserDefaults] integerForKey:@"NSUserDefaultIntegerValue"]);
NSLog(@"integerValue OBJECT: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultIntegerValue"]);
NSLog(@"--------------------");
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
NSLog(@"Should all be 1:");
NSLog(@"boolValue: %i", [GVUserDefaults standardUserDefaults].boolValue);
NSLog(@"boolValue: %i", [[NSUserDefaults standardUserDefaults] integerForKey:@"NSUserDefaultBoolValue"]);
NSLog(@"boolValue OBJECT: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultBoolValue"]);
NSLog(@"--------------------");
[GVUserDefaults standardUserDefaults].boolValue = NO;
NSLog(@"Should all be 0:");
NSLog(@"boolValue: %i", [GVUserDefaults standardUserDefaults].boolValue);
NSLog(@"boolValue: %i", [[NSUserDefaults standardUserDefaults] integerForKey:@"NSUserDefaultBoolValue"]);
NSLog(@"boolValue OBJECT: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultBoolValue"]);
NSLog(@"--------------------");
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"NSUserDefaultBoolValue"];
NSLog(@"Should all be 1:");
NSLog(@"boolValue: %i", [GVUserDefaults standardUserDefaults].boolValue);
NSLog(@"boolValue: %i", [[NSUserDefaults standardUserDefaults] integerForKey:@"NSUserDefaultBoolValue"]);
NSLog(@"boolValue OBJECT: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultBoolValue"]);
NSLog(@"--------------------");
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
NSLog(@"Should all be 12.3:");
NSLog(@"floatValue: %f", [GVUserDefaults standardUserDefaults].floatValue);
NSLog(@"floatValue: %f", [[NSUserDefaults standardUserDefaults] floatForKey:@"NSUserDefaultFloatValue"]);
NSLog(@"floatValue OBJECT: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultFloatValue"]);
NSLog(@"--------------------");
[GVUserDefaults standardUserDefaults].floatValue = 66.6;
NSLog(@"Should all be 66.6:");
NSLog(@"floatValue: %f", [GVUserDefaults standardUserDefaults].floatValue);
NSLog(@"floatValue: %f", [[NSUserDefaults standardUserDefaults] floatForKey:@"NSUserDefaultFloatValue"]);
NSLog(@"floatValue OBJECT: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultFloatValue"]);
NSLog(@"--------------------");
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"NSUserDefaultFloatValue"];
NSLog(@"Should all be 12.3:");
NSLog(@"floatValue: %f", [GVUserDefaults standardUserDefaults].floatValue);
NSLog(@"floatValue: %f", [[NSUserDefaults standardUserDefaults] floatForKey:@"NSUserDefaultFloatValue"]);
NSLog(@"floatValue OBJECT: %@", [[NSUserDefaults standardUserDefaults] objectForKey:@"NSUserDefaultFloatValue"]);
NSLog(@"--------------------");
}
@end

View File

@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "GVUserDefaults"
s.version = "0.4.1"
s.version = "0.9.0"
s.summary = "NSUserDefaults access via properties."
s.homepage = "https://github.com/gangverk/GVUserDefaults"
s.license = 'MIT'

View File

@@ -9,8 +9,104 @@
#import "GVUserDefaults.h"
#import <objc/runtime.h>
@interface GVUserDefaults ()
@property (strong, nonatomic) NSMutableDictionary *mapping;
@end
@implementation GVUserDefaults
enum TypeEncodings {
Char = 'c',
Short = 's',
Int = 'i',
Long = 'l',
LongLong = 'q',
UnsignedChar = 'C',
UnsignedShort = 'S',
UnsignedInt = 'I',
UnsignedLong = 'L',
UnsignedLongLong = 'Q',
Float = 'f',
Double = 'd',
Object = '@'
};
- (NSString *)defaultsKeyForPropertyNamed:(char const *)propertyName {
NSString *key = [NSString stringWithFormat:@"%s", propertyName];
return [self _transformKey:key];
}
- (NSString *)defaultsKeyForSelector:(SEL)selector {
return [self.mapping objectForKey:NSStringFromSelector(selector)];
}
static long long longLongGetter(GVUserDefaults *self, SEL _cmd) {
NSString *key = [self defaultsKeyForSelector:_cmd];
return [[[NSUserDefaults standardUserDefaults] objectForKey:key] longLongValue];
}
static void longLongSetter(GVUserDefaults *self, SEL _cmd, long long value) {
NSString *key = [self defaultsKeyForSelector:_cmd];
NSNumber *object = [NSNumber numberWithLongLong:value];
[[NSUserDefaults standardUserDefaults] setObject:object forKey:key];
}
static bool boolGetter(GVUserDefaults *self, SEL _cmd) {
NSString *key = [self defaultsKeyForSelector:_cmd];
return [[NSUserDefaults standardUserDefaults] boolForKey:key];
}
static void boolSetter(GVUserDefaults *self, SEL _cmd, bool value) {
NSString *key = [self defaultsKeyForSelector:_cmd];
[[NSUserDefaults standardUserDefaults] setBool:value forKey:key];
}
static int integerGetter(GVUserDefaults *self, SEL _cmd) {
NSString *key = [self defaultsKeyForSelector:_cmd];
return [[NSUserDefaults standardUserDefaults] integerForKey:key];
}
static void integerSetter(GVUserDefaults *self, SEL _cmd, int value) {
NSString *key = [self defaultsKeyForSelector:_cmd];
[[NSUserDefaults standardUserDefaults] setInteger:value forKey:key];
}
static float floatGetter(GVUserDefaults *self, SEL _cmd) {
NSString *key = [self defaultsKeyForSelector:_cmd];
return [[NSUserDefaults standardUserDefaults] floatForKey:key];
}
static void floatSetter(GVUserDefaults *self, SEL _cmd, float value) {
NSString *key = [self defaultsKeyForSelector:_cmd];
[[NSUserDefaults standardUserDefaults] setFloat:value forKey:key];
}
static double doubleGetter(GVUserDefaults *self, SEL _cmd) {
NSString *key = [self defaultsKeyForSelector:_cmd];
return [[NSUserDefaults standardUserDefaults] doubleForKey:key];
}
static void doubleSetter(GVUserDefaults *self, SEL _cmd, double value) {
NSString *key = [self defaultsKeyForSelector:_cmd];
[[NSUserDefaults standardUserDefaults] setDouble:value forKey:key];
}
static id objectGetter(GVUserDefaults *self, SEL _cmd) {
NSString *key = [self defaultsKeyForSelector:_cmd];
return [[NSUserDefaults standardUserDefaults] objectForKey:key];
}
static void objectSetter(GVUserDefaults *self, SEL _cmd, id object) {
NSString *key = [self defaultsKeyForSelector:_cmd];
if (object) {
[[NSUserDefaults standardUserDefaults] setObject:object forKey:key];
} else {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
}
}
#pragma mark - Begin
+ (GVUserDefaults *)standardUserDefaults {
static dispatch_once_t pred;
static GVUserDefaults *sharedInstance = nil;
@@ -31,27 +127,12 @@
}
[[NSUserDefaults standardUserDefaults] registerDefaults:mutableDefaults];
}
[self generateAccessorMethods];
}
return self;
}
+ (BOOL)resolveInstanceMethod:(SEL)aSEL {
NSString *method = NSStringFromSelector(aSEL);
if ([method isEqualToString:@"transformKey:"] || [method isEqualToString:@"setupDefaults"]) {
// Prevent endless loop for optional (and missing) category methods
return [super resolveInstanceMethod:aSEL];
}
if ([method hasPrefix:@"set"]) {
class_addMethod([self class], aSEL, (IMP) accessorSetter, "v@:@");
return YES;
} else {
class_addMethod([self class], aSEL, (IMP) accessorGetter, "@@:");
return YES;
}
}
- (NSString *)_transformKey:(NSString *)key {
if ([self respondsToSelector:@selector(transformKey:)]) {
return [self performSelector:@selector(transformKey:) withObject:key];
@@ -60,20 +141,98 @@
return key;
}
id accessorGetter(GVUserDefaults *self, SEL _cmd) {
NSString *key = NSStringFromSelector(_cmd);
key = [self _transformKey:key];
return [[NSUserDefaults standardUserDefaults] objectForKey:key];
}
- (void)generateAccessorMethods {
unsigned int count = 0;
objc_property_t *properties = class_copyPropertyList([self class], &count);
void accessorSetter(GVUserDefaults *self, SEL _cmd, id newValue) {
NSString *method = NSStringFromSelector(_cmd);
NSString *key = [[method stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:@""] stringByReplacingOccurrencesOfString:@":" withString:@""];
key = [key stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:[[key substringToIndex:1] lowercaseString]];
key = [self _transformKey:key];
self.mapping = [NSMutableDictionary dictionary];
// Set value of the key anID to newValue
[[NSUserDefaults standardUserDefaults] setObject:newValue forKey:key];
for (int i = 0; i < count; ++i) {
objc_property_t property = properties[i];
const char *name = property_getName(property);
const char *attributes = property_getAttributes(property);
char *getter = strstr(attributes, ",G");
if (getter) {
getter = strdup(getter + 2);
getter = strsep(&getter, ",");
} else {
getter = strdup(name);
}
SEL getterSel = sel_registerName(getter);
free(getter);
char *setter = strstr(attributes, ",S");
if (setter) {
setter = strdup(setter + 2);
setter = strsep(&setter, ",");
} else {
asprintf(&setter, "set%c%s:", toupper(name[0]), name + 1);
}
SEL setterSel = sel_registerName(setter);
free(setter);
NSString *key = [self defaultsKeyForPropertyNamed:name];
[self.mapping setValue:key forKey:NSStringFromSelector(getterSel)];
[self.mapping setValue:key forKey:NSStringFromSelector(setterSel)];
IMP getterImp = NULL;
IMP setterImp = NULL;
char type = attributes[1];
switch (type) {
case Short:
case Long:
case LongLong:
case UnsignedChar:
case UnsignedShort:
case UnsignedInt:
case UnsignedLong:
case UnsignedLongLong:
getterImp = (IMP)longLongGetter;
setterImp = (IMP)longLongSetter;
break;
case Char:
getterImp = (IMP)boolGetter;
setterImp = (IMP)boolSetter;
break;
case Int:
getterImp = (IMP)integerGetter;
setterImp = (IMP)integerSetter;
break;
case Float:
getterImp = (IMP)floatGetter;
setterImp = (IMP)floatSetter;
break;
case Double:
getterImp = (IMP)doubleGetter;
setterImp = (IMP)doubleSetter;
break;
case Object:
getterImp = (IMP)objectGetter;
setterImp = (IMP)objectSetter;
break;
default:
free(properties);
[NSException raise:NSInternalInconsistencyException format:@"Unsupported type of property \"%s\" in class %@", name, self];
break;
}
char types[5];
snprintf(types, 4, "%c@:", type);
class_addMethod([self class], getterSel, getterImp, types);
snprintf(types, 5, "v@:%c", type);
class_addMethod([self class], setterSel, setterImp, types);
}
free(properties);
}
@end

View File

@@ -9,12 +9,18 @@ Create a category on `GVUserDefaults`, add some properties in the .h file and ma
@interface GVUserDefaults (Properties)
@property (nonatomic, weak) NSString *userName;
@property (nonatomic, weak) NSNumber *userId;
@property (nonatomic) NSInteger integerValue;
@property (nonatomic) BOOL boolValue;
@property (nonatomic) float floatValue;
@end
// .m
@implementation GVUserDefaults (Properties)
@dynamic userName;
@dynamic userId;
@dynamic integerValue;
@dynamic boolValue;
@dynamic floatValue;
@end
Now, instead of using `[[NSUserDefaults standardUserDefaults] objectForKey:@"userName"]`, you can simply use `[GVUserDefaults standardUserDefaults].userName`.
@@ -23,8 +29,6 @@ You can even save defaults by setting the property:
[GVUserDefaults standardUserDefaults].userName = @"myusername";
### Objects only
At this moment only objects can be stored, so no integers or booleans. Just wrap them in an NSNumber.
### Key prefix
The keys in NSUserDefaults are the same name as your properties. If you'd like to prefix or alter them, add a `transformKey:` method to your category. For example, to turn "userName" into "NSUserDefaultUserName":
@@ -78,3 +82,7 @@ GVUserDefaults is an open source project and your contribution is very much appr
## License
GVUserDefaults is available under the MIT license. See the LICENSE file for more info.
## Thanks
A huge thank you goes to [ADVUserDefaults](https://github.com/advantis/ADVUserDefaults) for its method of creating accessors for primitive types.