Created
May 4, 2014 15:37
-
-
Save zaneclaes/95ae52612d9a9a45f765 to your computer and use it in GitHub Desktop.
This Firebase helper class uses transient Firebase objects, so that each object is never used for more than a single transaction. It also prevents Firebase crashes by creating mutable deep copies. The observe method also does sanity checking.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SNFirebase.h | |
// SharedNotes | |
// | |
// Created by Zane Claes on 4/12/14. | |
// Copyright (c) 2014 inZania LLC. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#import <Firebase/Firebase.h> | |
@interface SNFirebase : NSObject | |
@property (nonatomic, strong) NSMutableDictionary *contents; | |
@property (nonatomic, strong) NSMutableArray *observers; | |
@property (nonatomic, readonly) Firebase *firebase; | |
- (void)save; | |
- (void)save:(void (^)(BOOL success))block; | |
- (void)load:(void (^)(BOOL success))block; | |
- (Firebase*)observe:(NSString*)path eventType:(FEventType)type expectedClass:(Class)cls withBlock:(void (^)(id copiedValue))block; | |
- (void)stopObservingChanges; // Removes all observers | |
- (void)startObservingChanges; // Needs to be subclassed | |
- (id)initWithFirebase:(Firebase*)firebase contents:(NSDictionary*)contents; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SNFirebase.m | |
// SharedNotes | |
// | |
// Created by Zane Claes on 4/12/14. | |
// Copyright (c) 2014 inZania LLC. All rights reserved. | |
// | |
#import "SNFirebase.h" | |
@implementation NSDictionary (DeepCopy) | |
- (NSMutableDictionary *)mutableDeepCopy | |
{ | |
return (NSMutableDictionary *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)self, kCFPropertyListMutableContainers)); | |
} | |
@end | |
@implementation NSArray (DeepCopy) | |
- (NSMutableArray *)mutableDeepCopy | |
{ | |
return (NSMutableArray *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFArrayRef)self, kCFPropertyListMutableContainers)); | |
} | |
@end | |
@interface SNFirebase () | |
@property (nonatomic, strong) NSString *firebaseURL; | |
@end | |
@implementation SNFirebase | |
- (Firebase*)observe:(NSString*)path eventType:(FEventType)type expectedClass:(Class)cls withBlock:(void (^)(id copiedValue))block { | |
__block Firebase *fb = path.length ? [self.firebase childByAppendingPath:path] : self.firebase; | |
[fb observeEventType:type withBlock:^(FDataSnapshot *snapshot) { | |
if(![snapshot.value isKindOfClass:cls]) { | |
NSLog(@"Object changed at %@ is not a %@; it is a %@ (%@)",snapshot.ref.description,cls,[snapshot.value class],snapshot.value); | |
return; | |
} | |
id copied = nil; | |
if(cls == [NSDictionary class] || [cls isSubclassOfClass:[NSDictionary class]] || | |
cls == [NSArray class] || [cls isSubclassOfClass:[NSArray class]]) { | |
copied = [snapshot.value mutableDeepCopy]; | |
} | |
else { | |
copied = [snapshot.value mutableCopy]; | |
} | |
NSAssert([copied isKindOfClass:cls],@"%@ was not a %@ after copying (%@)",copied,cls,[copied class]); | |
block(copied); | |
}]; | |
self.observers = self.observers ?: [NSMutableArray new]; | |
[self.observers addObject:fb]; | |
return fb; | |
} | |
- (void)startObservingChanges { | |
NSAssert(NO,@"startObservingChanges should be overwritten"); | |
} | |
- (void)stopObservingChanges { | |
for(Firebase *observer in self.observers) { | |
[observer removeAllObservers]; | |
} | |
self.observers = nil; | |
} | |
- (void)save:(void (^)(BOOL success))completionHandler { | |
Firebase *fb = [[Firebase alloc] initWithUrl:self.firebaseURL]; | |
[fb setValue:[self.contents mutableDeepCopy] withCompletionBlock:^(NSError *error, Firebase *ref) { | |
if(completionHandler) { | |
completionHandler(error ? NO : YES); | |
} | |
}]; | |
} | |
- (void)save { | |
[self save:nil]; | |
} | |
- (void)load:(void (^)(BOOL success))block { | |
Firebase *fb = [[Firebase alloc] initWithUrl:self.firebaseURL]; | |
[fb observeSingleEventOfType:FEventTypeValue withBlock:^(FDataSnapshot *snapshot) { | |
_contents = [[snapshot.value isKindOfClass:[NSDictionary class]]?snapshot.value:@{} mutableDeepCopy]; | |
block(_contents.allKeys.count > 0); | |
}]; | |
} | |
- (Firebase*)firebase { | |
return [[Firebase alloc] initWithUrl:self.firebaseURL]; | |
} | |
- (id)initWithFirebase:(Firebase*)firebase contents:(NSDictionary*)contents { | |
if((self = [super init])) { | |
_firebaseURL = firebase.description; | |
_contents = [contents?:@{} mutableDeepCopy]; | |
} | |
return self; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment