I am creating a helper to ease transforming snapshots to objects and viceversa. I haven't finished my project but it is working so far, I will update whenever I do changes.
What the class does is assign automatically the value for key, but if the key represents a dictionary, then it is mapped to another object again (which may be another class object)
The getMap method is pretty straight forward, converting each property to a dictionary or object. When the property is another object. You can't assign nil values so the transformation must be done to [NSNull].
I couldn't find a way to auto-detect BOOL / Double / int etc, so they should be mapped correctly on getMap method, or simply use NSNumbers in model the properties.
Interface
#import <Foundation/Foundation.h>
@import FirebaseDatabase;
#ifndef FIRModel_m
#define FIRModel_m
#define IS_OBJECT(T) _Generic( (T), id: YES, default: NO)
#endif
/** Firebase model that helps converting Firebase Snapshot to object, and converting the object
 * to a dictionary mapping for updates */
@interface FIRModel : NSObject
/** Parses the snapshot data into the object */
- (void) parseFromSnapshot: (FIRDataSnapshot*) snapshot;
/** Returns a new model for the given key */
- (FIRModel*) modelForKey: (NSString*) key;
/** Returns the dictionary representation of this object */
- (NSMutableDictionary*) getMap;
/** Returns an object value for the given preference
 * If the property is null, then NSNUll is returned
 */
- (NSObject*) objFor: (id) value;
@end
Implementation
#import "FIRModel.h"
@implementation FIRModel
/** Parses the snapshot data into the object */
- (void) parseFromSnapshot: (FIRDataSnapshot*) snapshot {
    [self setValuesFromDictionary: snapshot.value];
}
/** Custom implementation for setValuesForKeysWithDictionary 
 *  Whenever it finds a Dictionary, it is transformed to the corresponding model object
 */
- (void)setValuesFromDictionary:(NSDictionary*)dict
{
    NSLog(@"Parsing in %@ the following received info: %@", [self class], dict);
    for (NSString* key in dict) {
        NSObject* value = [dict objectForKey:key];
        if(!value || [value isKindOfClass: [NSNull class]]) {
            //do nothing, value stays null
        }
        //TODO: Do the same for arrays
        else if(value && [value isKindOfClass: [NSDictionary class]]) {
            FIRModel* submodel = [self modelForKey: key];
            if(submodel) {
                [submodel setValuesFromDictionary: (NSDictionary*)value];
                [self setValue: submodel forKey: key];
            } else {
                NSLog(@"ERROR - *** Nil model returned from modelForKey for key: %@ ***", key );
            }
        }
        else {
            [self setValue: value forKey:key];
        }
    }
}
/** Override for added firebase properties**/
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    NSLog(@"Unknown key: %@ on object: %@", key, [self class] );
}
/** Returns a new model for the given key */
- (FIRModel*) modelForKey: (NSString*) key {
    return nil; //to be implemented by subclasses
}
/** Returns the dictionary representation of this object */
- (NSMutableDictionary*) getMap {
    [NSException raise:@"getMap not implmented" format:@"ERROR - Not implementing getMap for %@", self.class];
    return [NSMutableDictionary dictionary];
}
/** Returns an object value for the given preference
 * If the property is null, then NSNUll is returned
 */
- (NSObject*) objFor: (id) value {
    if(!value || !IS_OBJECT(value)) {
        return [NSNull null];
    }
    return value;
}
@end
Example usage:
#import <Foundation/Foundation.h>
#import "FIRModel.h"
/** The user object */
@class PublicInfo;
@interface User : FIRModel
@property (nonatomic, strong) NSString* email;
@property (nonatomic, strong) NSString* phone;
@property (nonatomic, strong) PublicInfo* publicInfo;
@property (nonatomic, assign) double aDoubleValue;  
@property (nonatomic, assign) BOOL aBoolValue;  
@property (nonatomic, strong) id timestampJoined;   //Map or NSNumber
@property (nonatomic, strong) id timestampLastLogin;  //Map or NSNumber
@end
@interface PublicInfo : FIRModel
@property (nonatomic, strong) NSString* key;
@property (nonatomic, strong) NSString* name;
@property (nonatomic, strong) NSString* pic;
@end
Implementation
#import "User.h"
@implementation User
/** Returns a new model for the given key */
- (FIRModel*) modelForKey: (NSString*) key {
    if ([key isEqualToString: @"publicInfo"]) {
        return [[PublicInfo alloc] init];
    }
    return nil;
}
- (NSMutableDictionary *)getMap {
    NSMutableDictionary* map = [NSMutableDictionary dictionary];
    map[@"email"] =  [self objFor: self.email];
    map[@"phone"] = [self objFor: self.phone];
    map[@"aDoubleValue"] = @(self.aDoubleValue);
    map[@"aBoolValue"] = @(self.aBoolValue);
    map[@"publicInfo"] = self.publicInfo ? [self.publicInfo getMap] : [NSNull null];
    map[@"timestampJoined"] =  [self objFor: self.timestampJoined];
    map[@"timestampLastLogin"] = [self objFor: self.timestampLastLogin];
    return map;
}
@end
#pragma mark -
@implementation PublicInfo
- (NSMutableDictionary *)getMap {
    NSMutableDictionary* map = [NSMutableDictionary dictionary];
    map[@"name"] =  [self objFor: self.name];
    map[@"pic"] =  [self objFor: self.pic];
    map[@"key"] = [self objFor: self.key];
    return map;
}
@end
Usage
//Parsing model
User *user = [[User alloc] init];
[user parseFromSnapshot: snapshot];
//Getting map for updateChildValues method
[user getMap]