/* * Copyright 2017 Google * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #import #import "FIRDatabaseReference.h" #import #import "FUtilities.h" #import "FNextPushId.h" #import "FIRDatabaseQuery_Private.h" #import "FValidation.h" #import "FIRDatabaseReference_Private.h" #import "FStringUtilities.h" #import "FSnapshotUtilities.h" #import "FIRDatabaseConfig.h" #import "FIRDatabaseConfig_Private.h" #import "FQueryParams.h" #import "FIRDatabase.h" @implementation FIRDatabaseReference + (FIRDatabaseConfig *)defaultConfig { return [FIRDatabaseConfig defaultConfig]; } #pragma mark - #pragma mark Constructors - (id) initWithConfig:(FIRDatabaseConfig *)config { FParsedUrl* parsedUrl = [FUtilities parseUrl:[[FIRApp defaultApp] options].databaseURL]; [FValidation validateFrom:@"initWithUrl:" validURL:parsedUrl]; return [self initWithRepo:[FRepoManager getRepo:parsedUrl.repoInfo config:config] path:parsedUrl.path]; } - (id) initWithRepo:(FRepo *)repo path:(FPath *)path { return [super initWithRepo:repo path:path params:[FQueryParams defaultInstance] orderByCalled:NO priorityMethodCalled:NO]; } #pragma mark - #pragma mark Ancillary methods - (NSString *) key { if([self.path isEmpty]) { return nil; } else { return [self.path getBack]; } } - (FIRDatabase *) database { return self.repo.database; } - (FIRDatabaseReference *) parent { FPath* parentPath = [self.path parent]; FIRDatabaseReference * parent = nil; if (parentPath != nil ) { parent = [[FIRDatabaseReference alloc] initWithRepo:self.repo path:parentPath]; } return parent; } - (NSString *) URL { FIRDatabaseReference * parent = [self parent]; return parent == nil ? [self.repo description] : [NSString stringWithFormat:@"%@/%@", [parent description], [FStringUtilities urlEncoded:self.key]]; } - (NSString *) description { return [self URL]; } - (FIRDatabaseReference *) root { return [[FIRDatabaseReference alloc] initWithRepo:self.repo path:[[FPath alloc] initWith:@""]]; } #pragma mark - #pragma mark Child methods - (FIRDatabaseReference *)childByAppendingPath:(NSString *)pathString { return [self child:pathString]; } - (FIRDatabaseReference *)child:(NSString *)pathString { if ([self.path getFront] == nil) { // we're at the root [FValidation validateFrom:@"child:" validRootPathString:pathString]; } else { [FValidation validateFrom:@"child:" validPathString:pathString]; } FPath* path = [self.path childFromString:pathString]; FIRDatabaseReference * firebaseRef = [[FIRDatabaseReference alloc] initWithRepo:self.repo path:path]; return firebaseRef; } - (FIRDatabaseReference *) childByAutoId { [FValidation validateFrom:@"childByAutoId:" writablePath:self.path]; NSString* name = [FNextPushId get:self.repo.serverTime]; return [self child:name]; } #pragma mark - #pragma mark Basic write methods - (void) setValue:(id)value { [self setValueInternal:value andPriority:nil withCompletionBlock:nil from:@"setValue:"]; } - (void) setValue:(id)value withCompletionBlock:(fbt_void_nserror_ref)block { [self setValueInternal:value andPriority:nil withCompletionBlock:block from:@"setValue:withCompletionBlock:"]; } - (void) setValue:(id)value andPriority:(id)priority { [self setValueInternal:value andPriority:priority withCompletionBlock:nil from:@"setValue:andPriority:"]; } - (void) setValue:(id)value andPriority:(id)priority withCompletionBlock:(fbt_void_nserror_ref)block { [self setValueInternal:value andPriority:priority withCompletionBlock:block from:@"setValue:andPriority:withCompletionBlock:"]; } - (void) setValueInternal:(id)value andPriority:(id)priority withCompletionBlock:(fbt_void_nserror_ref)block from:(NSString*)fn { [FValidation validateFrom:fn writablePath:self.path]; fbt_void_nserror_ref userCallback = [block copy]; id newNode = [FSnapshotUtilities nodeFrom:value priority:priority withValidationFrom:fn]; dispatch_async([FIRDatabaseQuery sharedQueue], ^{ [self.repo set:self.path withNode:newNode withCallback:userCallback]; }); } - (void) removeValue { [self setValueInternal:nil andPriority:nil withCompletionBlock:nil from:@"removeValue:"]; } - (void) removeValueWithCompletionBlock:(fbt_void_nserror_ref)block { [self setValueInternal:nil andPriority:nil withCompletionBlock:block from:@"removeValueWithCompletionBlock:"]; } - (void) setPriority:(id)priority { [self setPriorityInternal:priority withCompletionBlock:nil from:@"setPriority:"]; } - (void) setPriority:(id)priority withCompletionBlock:(fbt_void_nserror_ref)block { [self setPriorityInternal:priority withCompletionBlock:block from:@"setPriority:withCompletionBlock:"]; } - (void) setPriorityInternal:(id)priority withCompletionBlock:(fbt_void_nserror_ref)block from:(NSString*)fn { [FValidation validateFrom:fn writablePath:self.path]; fbt_void_nserror_ref userCallback = [block copy]; dispatch_async([FIRDatabaseQuery sharedQueue], ^{ [self.repo set:[self.path childFromString:@".priority"] withNode:[FSnapshotUtilities nodeFrom:priority] withCallback:userCallback]; }); } - (void) updateChildValues:(NSDictionary *)values { [self updateChildValuesInternal:values withCompletionBlock:nil from:@"updateChildValues:"]; } - (void) updateChildValues:(NSDictionary *)values withCompletionBlock:(fbt_void_nserror_ref)block { [self updateChildValuesInternal:values withCompletionBlock:block from:@"updateChildValues:withCompletionBlock:"]; } - (void) updateChildValuesInternal:(NSDictionary *)values withCompletionBlock:(fbt_void_nserror_ref)block from:(NSString*)fn { [FValidation validateFrom:fn writablePath:self.path]; FCompoundWrite *merge = [FSnapshotUtilities compoundWriteFromDictionary:values withValidationFrom:fn]; fbt_void_nserror_ref userCallback = [block copy]; dispatch_async([FIRDatabaseQuery sharedQueue], ^{ [self.repo update:self.path withNodes:merge withCallback:userCallback]; }); } #pragma mark - #pragma mark Disconnect Operations - (void) onDisconnectSetValue:(id)value { [self onDisconnectSetValueInternal:value andPriority:nil withCompletionBlock:nil from:@"onDisconnectSetValue:"]; } - (void) onDisconnectSetValue:(id)value withCompletionBlock:(fbt_void_nserror_ref)block { [self onDisconnectSetValueInternal:value andPriority:nil withCompletionBlock:block from:@"onDisconnectSetValue:withCompletionBlock:"]; } - (void) onDisconnectSetValue:(id)value andPriority:(id)priority { [self onDisconnectSetValueInternal:value andPriority:priority withCompletionBlock:nil from:@"onDisconnectSetValue:andPriority:"]; } - (void) onDisconnectSetValue:(id)value andPriority:(id)priority withCompletionBlock:(fbt_void_nserror_ref)block { [self onDisconnectSetValueInternal:value andPriority:priority withCompletionBlock:block from:@"onDisconnectSetValue:andPriority:withCompletionBlock:"]; } - (void) onDisconnectSetValueInternal:(id)value andPriority:(id)priority withCompletionBlock:(fbt_void_nserror_ref)block from:(NSString*)fn { [FValidation validateFrom:fn writablePath:self.path]; id newNodeUnresolved = [FSnapshotUtilities nodeFrom:value priority:priority withValidationFrom:fn]; fbt_void_nserror_ref userCallback = [block copy]; dispatch_async([FIRDatabaseQuery sharedQueue], ^{ [self.repo onDisconnectSet:self.path withNode:newNodeUnresolved withCallback:userCallback]; }); } - (void) onDisconnectRemoveValue { [self onDisconnectSetValueInternal:nil andPriority:nil withCompletionBlock:nil from:@"onDisconnectRemoveValue:"]; } - (void) onDisconnectRemoveValueWithCompletionBlock:(fbt_void_nserror_ref)block { [self onDisconnectSetValueInternal:nil andPriority:nil withCompletionBlock:block from:@"onDisconnectRemoveValueWithCompletionBlock:"]; } - (void) onDisconnectUpdateChildValues:(NSDictionary *)values { [self onDisconnectUpdateChildValuesInternal:values withCompletionBlock:nil from:@"onDisconnectUpdateChildValues:"]; } - (void) onDisconnectUpdateChildValues:(NSDictionary *)values withCompletionBlock:(fbt_void_nserror_ref)block { [self onDisconnectUpdateChildValuesInternal:values withCompletionBlock:block from:@"onDisconnectUpdateChildValues:withCompletionBlock:"]; } - (void) onDisconnectUpdateChildValuesInternal:(NSDictionary *)values withCompletionBlock:(fbt_void_nserror_ref)block from:(NSString*)fn { [FValidation validateFrom:fn writablePath:self.path]; FCompoundWrite *merge = [FSnapshotUtilities compoundWriteFromDictionary:values withValidationFrom:fn]; fbt_void_nserror_ref userCallback = [block copy]; dispatch_async([FIRDatabaseQuery sharedQueue], ^{ [self.repo onDisconnectUpdate:self.path withNodes:merge withCallback:userCallback]; }); } - (void) cancelDisconnectOperations { [self cancelDisconnectOperationsWithCompletionBlock:nil]; } - (void) cancelDisconnectOperationsWithCompletionBlock:(fbt_void_nserror_ref)block { fbt_void_nserror_ref callback = nil; if (block != nil) { callback = [block copy]; } dispatch_async([FIRDatabaseQuery sharedQueue], ^{ [self.repo onDisconnectCancel:self.path withCallback:callback]; }); } #pragma mark - #pragma mark Connection management methods + (void) goOffline { [FRepoManager interruptAll]; } + (void) goOnline { [FRepoManager resumeAll]; } #pragma mark - #pragma mark Data reading methods deferred to FQuery - (FIRDatabaseHandle)observeEventType:(FIRDataEventType)eventType withBlock:(fbt_void_datasnapshot)block { return [self observeEventType:eventType withBlock:block withCancelBlock:nil]; } - (FIRDatabaseHandle)observeEventType:(FIRDataEventType)eventType andPreviousSiblingKeyWithBlock:(fbt_void_datasnapshot_nsstring)block { return [self observeEventType:eventType andPreviousSiblingKeyWithBlock:block withCancelBlock:nil]; } - (FIRDatabaseHandle)observeEventType:(FIRDataEventType)eventType withBlock:(fbt_void_datasnapshot)block withCancelBlock:(fbt_void_nserror)cancelBlock { return [super observeEventType:eventType withBlock:block withCancelBlock:cancelBlock]; } - (FIRDatabaseHandle)observeEventType:(FIRDataEventType)eventType andPreviousSiblingKeyWithBlock:(fbt_void_datasnapshot_nsstring)block withCancelBlock:(fbt_void_nserror)cancelBlock { return [super observeEventType:eventType andPreviousSiblingKeyWithBlock:block withCancelBlock:cancelBlock]; } - (void) removeObserverWithHandle:(FIRDatabaseHandle)handle { [super removeObserverWithHandle:handle]; } - (void) removeAllObservers { [super removeAllObservers]; } - (void) keepSynced:(BOOL)keepSynced { [super keepSynced:keepSynced]; } - (void)observeSingleEventOfType:(FIRDataEventType)eventType withBlock:(fbt_void_datasnapshot)block { [self observeSingleEventOfType:eventType withBlock:block withCancelBlock:nil]; } - (void)observeSingleEventOfType:(FIRDataEventType)eventType andPreviousSiblingKeyWithBlock:(fbt_void_datasnapshot_nsstring)block { [self observeSingleEventOfType:eventType andPreviousSiblingKeyWithBlock:block withCancelBlock:nil]; } - (void)observeSingleEventOfType:(FIRDataEventType)eventType withBlock:(fbt_void_datasnapshot)block withCancelBlock:(fbt_void_nserror)cancelBlock { [super observeSingleEventOfType:eventType withBlock:block withCancelBlock:cancelBlock]; } - (void)observeSingleEventOfType:(FIRDataEventType)eventType andPreviousSiblingKeyWithBlock:(fbt_void_datasnapshot_nsstring)block withCancelBlock:(fbt_void_nserror)cancelBlock { [super observeSingleEventOfType:eventType andPreviousSiblingKeyWithBlock:block withCancelBlock:cancelBlock]; } #pragma mark - #pragma mark Query methods // These methods suppress warnings from having method definitions in FIRDatabaseReference.h for docs generation. - (FIRDatabaseQuery *)queryLimitedToFirst:(NSUInteger)limit { return [super queryLimitedToFirst:limit]; } - (FIRDatabaseQuery *)queryLimitedToLast:(NSUInteger)limit { return [super queryLimitedToLast:limit]; } - (FIRDatabaseQuery *)queryOrderedByChild:(NSString *)key { return [super queryOrderedByChild:key]; } - (FIRDatabaseQuery *) queryOrderedByKey { return [super queryOrderedByKey]; } - (FIRDatabaseQuery *) queryOrderedByPriority { return [super queryOrderedByPriority]; } - (FIRDatabaseQuery *)queryStartingAtValue:(id)startValue { return [super queryStartingAtValue:startValue]; } - (FIRDatabaseQuery *)queryStartingAtValue:(id)startValue childKey:(NSString *)childKey { return [super queryStartingAtValue:startValue childKey:childKey]; } - (FIRDatabaseQuery *)queryEndingAtValue:(id)endValue { return [super queryEndingAtValue:endValue]; } - (FIRDatabaseQuery *)queryEndingAtValue:(id)endValue childKey:(NSString *)childKey { return [super queryEndingAtValue:endValue childKey:childKey]; } - (FIRDatabaseQuery *)queryEqualToValue:(id)value { return [super queryEqualToValue:value]; } - (FIRDatabaseQuery *)queryEqualToValue:(id)value childKey:(NSString *)childKey { return [super queryEqualToValue:value childKey:childKey]; } #pragma mark - #pragma mark Transaction methods - (void) runTransactionBlock:(fbt_transactionresult_mutabledata)block { [FValidation validateFrom:@"runTransactionBlock:" writablePath:self.path]; [self runTransactionBlock:block andCompletionBlock:nil withLocalEvents:YES]; } - (void) runTransactionBlock:(fbt_transactionresult_mutabledata)update andCompletionBlock:(fbt_void_nserror_bool_datasnapshot)completionBlock { [FValidation validateFrom:@"runTransactionBlock:andCompletionBlock:" writablePath:self.path]; [self runTransactionBlock:update andCompletionBlock:completionBlock withLocalEvents:YES]; } - (void) runTransactionBlock:(fbt_transactionresult_mutabledata)block andCompletionBlock:(fbt_void_nserror_bool_datasnapshot)completionBlock withLocalEvents:(BOOL)localEvents { [FValidation validateFrom:@"runTransactionBlock:andCompletionBlock:withLocalEvents:" writablePath:self.path]; fbt_transactionresult_mutabledata updateCopy = [block copy]; fbt_void_nserror_bool_datasnapshot onCompleteCopy = [completionBlock copy]; dispatch_async([FIRDatabaseQuery sharedQueue], ^{ [self.repo startTransactionOnPath:self.path update:updateCopy onComplete:onCompleteCopy withLocalEvents:localEvents]; }); } @end