Skip to content

Instantly share code, notes, and snippets.

@zadr
Last active May 26, 2023 08:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zadr/f0f31572243872bfadc98bee0c8632ca to your computer and use it in GitHub Desktop.
Save zadr/f0f31572243872bfadc98bee0c8632ca to your computer and use it in GitHub Desktop.
Dynamic User Defaults
@interface NSUserDefaults (idk)
@property (nonatomic, copy) NSString *jam;
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
[NSUserDefaults standardUserDefaults].jam = @"strawberry";
NSLog(@"%@", [NSUserDefaults standardUserDefaults].jam);
}
@end
@implementation NSUserDefaults (idk)
@dynamic jam; // so we can do this
/* // instead of this
- (NSString *)jam {
return [self objectForKey:@"jam"];
}
- (void)setJam:(NSString *)jam {
[self setObject:jam forKey:@"jam"];
}
*/
@end
#import "HackingSwizzlingAndProxyingOhMy.h"
#import <objc/runtime.h>
@interface NSUserDefaults (hsp_lol)
+ (instancetype)hsp_instance;
@end
@implementation NSUserDefaults (hsp_lol)
+ (void)load {
Method old = class_getClassMethod([NSUserDefaults class], @selector(standardUserDefaults));
Method new = class_getClassMethod([HackingSwizzlingAndProxyingOhMy class], @selector(hsp_instance));
method_exchangeImplementations(old, new);
}
+ (instancetype)hsp_instance {
static HackingSwizzlingAndProxyingOhMy *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[HackingSwizzlingAndProxyingOhMy alloc] init];
});
return instance;
}
@end
@implementation HackingSwizzlingAndProxyingOhMy
- (BOOL)_isGetter:(SEL)sel {
NSString *s = NSStringFromSelector(sel);
if ([s rangeOfString:@":"].location != NSNotFound) {
return NO;
}
return YES;
}
- (BOOL)_isSetter:(SEL)sel {
NSString *s = NSStringFromSelector(sel);
NSRange range = [s rangeOfString:@":"];
if (![s hasPrefix:@"set"] || [s rangeOfString:@":"].location == NSNotFound) {
return NO;
}
s = [s substringFromIndex:NSMaxRange(range)];
range = [s rangeOfString:@":"];
if (range.location != NSNotFound) {
return NO;
}
return YES;
}
#pragma mark -
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
NSMethodSignature *signature = [super methodSignatureForSelector:sel];
if (signature) {
return signature;
}
if ([self _isGetter:sel]) {
return [self methodSignatureForSelector:@selector(objectForKey:)];
}
if ([self _isSetter:sel]) {
return [self methodSignatureForSelector:@selector(setObject:forKey:)];
}
return nil;
}
- (void)forwardInvocation:(NSInvocation *)inv {
SEL sel = inv.selector;
if ([self _isGetter:sel]) {
inv.selector = @selector(objectForKey:);
id arg = NSStringFromSelector(sel);
[inv setArgument:&arg atIndex:2];
[inv retainArguments];
} else if ([self _isSetter:sel]) {
inv.selector = @selector(setObject:forKey:);
NSString *key = NSStringFromSelector(sel);
key = [[key substringFromIndex:3] substringToIndex:key.length - 4];
NSString *initial = [[key substringToIndex:1] lowercaseString];
key = [key stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:initial];
[inv setArgument:&key atIndex:3];
[inv retainArguments];
} else {
return [super forwardInvocation:inv];
}
[inv invoke];
}
@end
#import <Foundation/Foundation.h>
@interface HackingSwizzlingAndProxyingOhMy : NSUserDefaults
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment