Search code examples
iosstructnscodinggithub-mantle

Encoding c-struct with Mantle (NSCoding)


I want to use Mantle framework (https://github.com/github/Mantle) to support NSCoding for my class with struct property:

typedef struct {
    int x;
    int y;
} MPoint;

typedef struct {
    MPoint min;
    MPoint max;
} MRect;


@interface MObject : MTLModel

@property (assign, nonatomic) MRect rect;

@end

@implementation MObject
@end

But when I tried to [NSKeyedArchiver archiveRootObject:obj toFile:@"file"]; its crashed in MTLModel+NSCoding.m, in - (void)encodeWithCoder:(NSCoder *)coder on line

case MTLModelEncodingBehaviorUnconditional:
    [coder encodeObject:value forKey:key];

Does Mantle supports c-struct encoding (and also decoding) or I've need to custom implementing NSCoding protocol for such classes?


Solution

  • It was easier than I thought:

    1. Exclude property in +encodingBehaviorsByPropertyKey
    2. Manual encode/encode excluded property

    Sample:

    #pragma mark - MTLModel + NSCoding
    
    - (id)initWithCoder:(NSCoder *)coder {
        self = [super initWithCoder:coder];
        if (self) {
            self.rect = [[self class] mRectFromData:[coder decodeObjectForKey:@"rectData"]];
        }
        return self;
    }
    
    - (void)encodeWithCoder:(NSCoder *)coder {
        [super encodeWithCoder:coder];
    
        [coder encodeObject:[[self class] dataFromMRect:self.rect] forKey:@"rectData"];
    }
    
    + (NSDictionary *)encodingBehaviorsByPropertyKey {
        NSDictionary *excludeProperties = @{
                                            NSStringFromSelector(@selector(rect)): @(MTLModelEncodingBehaviorExcluded)
                                            };
        NSDictionary *encodingBehaviors = [[super encodingBehaviorsByPropertyKey] mtl_dictionaryByAddingEntriesFromDictionary:excludeProperties];
        return encodingBehaviors;
    }
    
    #pragma mark - MRect transformations
    
    + (MRect)mRectFromData:(NSData *)rectData {
        MRect rect;
        [rectData getBytes:&rect length:sizeof(rect)];
        return rect;
    }
    
    + (NSData *)dataFromMRect:(MRect)rect {
        return [NSData dataWithBytes:&rect length:sizeof(rect)];
    }