Skip to content

Instantly share code, notes, and snippets.

@zaneclaes
Created May 4, 2014 15:37
Show Gist options
  • Save zaneclaes/95ae52612d9a9a45f765 to your computer and use it in GitHub Desktop.
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.
//
// 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
//
// 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