Suppose I have a model like this:
#import <Mantle/Mantle.h>
#import "MyCustomObject.h"
@interface MyModel : MTLModel <MTLJSONSerializing>
@property (nonatomic, copy, readonly) NSString *UUID;
@property (nonatomic, copy) NSString *someProp;
@property (nonatomic, copy) MyCustomObject *anotherProp;
@end
#import "MyModel.h"
@implementation MyModel
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return @{
@"UUID": @"id",
@"anotherProp": NSNull.null
};
}
}
@end
As you can see, I want to ignore anotherProp
during the NSCoding serialization, as well as re-map "UUID" to "id". With YapDatabase, I do a
[transaction setObject:myModelObj forKey:@"key_1" inCollection:@"my_collection"]
but it attempts to serialize anotherProp
despite my custom JSONKeyPathsByPropertyKey
method, resulting in this error:
*** Caught exception encoding value for key "anotherProp" on class MyModel: -[YapDatabase encodeWithCoder:]: unrecognized selector sent to instance 0xc989630
Do I need to write a custom serializer to get YapDatabase to use JSONKeyPathsByPropertyKey
?
Here is my current approach using an MTLModel
extension to making this "just work" without a serializer or anything. If there is a serializer implementation or something that would be better, please let me know. Otherwise, this code is a life saver:
// JSONEncodableMTLModel.h
#import <Foundation/Foundation.h>
#import "Mantle.h"
@interface JSONEncodableMTLModel : MTLModel <MTLJSONSerializing>
+ (NSSet*)propertyKeysToExcludeInDictionaryValue;
@end
// JSONEncodableMTLModel.m
#import "JSONEncodableMTLModel.h"
#import <objc/runtime.h>
@implementation JSONEncodableMTLModel
+(NSDictionary *)JSONKeyPathsByPropertyKey {
return @{};
}
+ (NSSet*)propertyKeysToExcludeInDictionaryValue
{
return [NSSet set];
}
// this is required to ensure we don't have cyclical references when including the parent variable.
+ (NSSet *)propertyKeys {
NSSet *cachedKeys = objc_getAssociatedObject(self, HSModelCachedPropertyKeysKey);
if (cachedKeys != nil) return cachedKeys;
NSMutableSet *keys = [NSMutableSet setWithSet:[super propertyKeys]];
NSSet *exclusionKeys = [self propertyKeysToExcludeInDictionaryValue];
NSLog(@"Caching Your Property Keys");
[exclusionKeys enumerateObjectsUsingBlock:^(NSString *propertyKey, BOOL *stop) {
if([keys containsObject: propertyKey])
{
[keys removeObject: propertyKey];
}
}];
// It doesn't really matter if we replace another thread's work, since we do
// it atomically and the result should be the same.
objc_setAssociatedObject(self, HSModelCachedPropertyKeysKey, [keys copy], OBJC_ASSOCIATION_COPY);
return keys;
}
@end
With this code, I can explicitly set which properties do not need to be serialized by overriding propertyKeysToExcludeInDictionaryValue
in any model that is a subclass. Credit goes to Stephen O'Connor.