It’s clear that we have a problem. We can’t log Firebase calls to setObject:forKey.

So we have the following options:

  1. Send email to Google and ask them to use our mySetObject:forKey.
  2. Use method swizzling.
  3. Some other hacky solution that I can’t think of right now.

So obviously we will proceed with option two (But if you decide to send email to Google please cc me. I would like to see their reaction 😂 ha ha ha).

The solution – I can think of

#import "NSUserDefaults+MonitoringWrites.h"
#import <objc/runtime.h>

@implementation NSUserDefaults (MonitoringWrites)

+ (void)load {

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
       Class class = [self class];
       SEL defaultSelector = @selector(setObject:forKey:);

       SEL swizzledSelector = @selector(swizzled_setObject:forKey:);
        Method defaultMethod = class_getInstanceMethod(class, defaultSelector);
  Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL isMethodExists = !class_addMethod(class, defaultSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));

         if (isMethodExists) {             method_exchangeImplementations(defaultMethod, swizzledMethod);
        }
        else {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(defaultMethod), method_getTypeEncoding(defaultMethod));

        }

    });

}
#pragma mark - Method Swizzling
- (void)swizzled_setObject:(id)value forKey:(NSString *)defaultName {
  [self iwsSetObject:value forKey:defaultName];
  NSLog(@"Set Object %@ for key %@",value,defaultName);

}

@end

Method, Selector and Implementation for Swizzle

SEL defaultSelector = @selector(setObject:forKey:);
SEL swizzledSelector = @selector(swizzled_setObject:forKey:);
        
Method defaultMethod = class_getInstanceMethod([self class], defaultSelector);
Method swizzledMethod = class_getInstanceMethod([self class], swizzledSelector);
BOOL isMethodExists = !class_addMethod([self class], defaultSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));

Tricky part we approached

BOOL isMethodExists = !class_addMethod([self class], defaultSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        
if (isMethodExists) {
    method_exchangeImplementations(defaultMethod, swizzledMethod);
}
else {
    class_replaceMethod([self class], swizzledSelector, method_getImplementation(defaultMethod), method_getTypeEncoding(defaultMethod));
}


Leave a Reply

Your email address will not be published.