aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firebase/Database/Api
diff options
context:
space:
mode:
authorGravatar Paul Beusterien <paulbeusterien@google.com>2017-05-15 12:27:07 -0700
committerGravatar Paul Beusterien <paulbeusterien@google.com>2017-05-15 12:27:07 -0700
commit98ba64449a632518bd2b86fe8d927f4a960d3ddc (patch)
tree131d9c4272fa6179fcda6c5a33fcb3b1bd57ad2e /Firebase/Database/Api
parent32461366c9e204a527ca05e6e9b9404a2454ac51 (diff)
Initial
Diffstat (limited to 'Firebase/Database/Api')
-rw-r--r--Firebase/Database/Api/FIRDataEventType.h39
-rw-r--r--Firebase/Database/Api/FIRDataSnapshot.h148
-rw-r--r--Firebase/Database/Api/FIRDataSnapshot.m101
-rw-r--r--Firebase/Database/Api/FIRDatabase.h140
-rw-r--r--Firebase/Database/Api/FIRDatabase.m268
-rw-r--r--Firebase/Database/Api/FIRDatabaseConfig.h63
-rw-r--r--Firebase/Database/Api/FIRDatabaseConfig.m117
-rw-r--r--Firebase/Database/Api/FIRDatabaseQuery.h315
-rw-r--r--Firebase/Database/Api/FIRDatabaseQuery.m525
-rw-r--r--Firebase/Database/Api/FIRDatabaseSwiftNameSupport.h29
-rw-r--r--Firebase/Database/Api/FIRMutableData.h130
-rw-r--r--Firebase/Database/Api/FIRMutableData.m134
-rw-r--r--Firebase/Database/Api/FIRServerValue.h35
-rw-r--r--Firebase/Database/Api/FIRServerValue.m30
-rw-r--r--Firebase/Database/Api/FIRTransactionResult.h47
-rw-r--r--Firebase/Database/Api/FIRTransactionResult.m39
-rw-r--r--Firebase/Database/Api/FirebaseDatabase.h29
-rw-r--r--Firebase/Database/Api/Private/FIRDataSnapshot_Private.h27
-rw-r--r--Firebase/Database/Api/Private/FIRDatabaseQuery_Private.h43
-rw-r--r--Firebase/Database/Api/Private/FIRDatabaseReference_Private.h29
-rw-r--r--Firebase/Database/Api/Private/FIRDatabase_Private.h28
-rw-r--r--Firebase/Database/Api/Private/FIRMutableData_Private.h26
-rw-r--r--Firebase/Database/Api/Private/FIRTransactionResult_Private.h25
-rw-r--r--Firebase/Database/Api/Private/FTypedefs_Private.h56
24 files changed, 2423 insertions, 0 deletions
diff --git a/Firebase/Database/Api/FIRDataEventType.h b/Firebase/Database/Api/FIRDataEventType.h
new file mode 100644
index 0000000..fccc98a
--- /dev/null
+++ b/Firebase/Database/Api/FIRDataEventType.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef Firebase_FIRDataEventType_h
+#define Firebase_FIRDataEventType_h
+
+#import <Foundation/Foundation.h>
+#import "FIRDatabaseSwiftNameSupport.h"
+
+/**
+ * This enum is the set of events that you can observe at a Firebase Database location.
+ */
+typedef NS_ENUM(NSInteger, FIRDataEventType) {
+ /// A new child node is added to a location.
+ FIRDataEventTypeChildAdded,
+ /// A child node is removed from a location.
+ FIRDataEventTypeChildRemoved,
+ /// A child node at a location changes.
+ FIRDataEventTypeChildChanged,
+ /// A child node moves relative to the other child nodes at a location.
+ FIRDataEventTypeChildMoved,
+ /// Any data changes at a location or, recursively, at any child node.
+ FIRDataEventTypeValue
+} FIR_SWIFT_NAME(DataEventType);
+
+#endif
diff --git a/Firebase/Database/Api/FIRDataSnapshot.h b/Firebase/Database/Api/FIRDataSnapshot.h
new file mode 100644
index 0000000..e615260
--- /dev/null
+++ b/Firebase/Database/Api/FIRDataSnapshot.h
@@ -0,0 +1,148 @@
+/*
+ * 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 <Foundation/Foundation.h>
+#import "FIRDatabaseSwiftNameSupport.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class FIRDatabaseReference;
+
+/**
+ * A FIRDataSnapshot contains data from a Firebase Database location. Any time you read
+ * Firebase data, you receive the data as a FIRDataSnapshot.
+ *
+ * FIRDataSnapshots are passed to the blocks you attach with observeEventType:withBlock: or observeSingleEvent:withBlock:.
+ * They are efficiently-generated immutable copies of the data at a Firebase Database location.
+ * They can't be modified and will never change. To modify data at a location,
+ * use a FIRDatabaseReference (e.g. with setValue:).
+ */
+FIR_SWIFT_NAME(DataSnapshot)
+@interface FIRDataSnapshot : NSObject
+
+
+#pragma mark - Navigating and inspecting a snapshot
+
+/**
+ * Gets a FIRDataSnapshot for the location at the specified relative path.
+ * The relative path can either be a simple child key (e.g. 'fred')
+ * or a deeper slash-separated path (e.g. 'fred/name/first'). If the child
+ * location has no data, an empty FIRDataSnapshot is returned.
+ *
+ * @param childPathString A relative path to the location of child data.
+ * @return The FIRDataSnapshot for the child location.
+ */
+- (FIRDataSnapshot *)childSnapshotForPath:(NSString *)childPathString;
+
+
+/**
+ * Return YES if the specified child exists.
+ *
+ * @param childPathString A relative path to the location of a potential child.
+ * @return YES if data exists at the specified childPathString, else NO.
+ */
+- (BOOL) hasChild:(NSString *)childPathString;
+
+
+/**
+ * Return YES if the DataSnapshot has any children.
+ *
+ * @return YES if this snapshot has any children, else NO.
+ */
+- (BOOL) hasChildren;
+
+
+/**
+ * Return YES if the DataSnapshot contains a non-null value.
+ *
+ * @return YES if this snapshot contains a non-null value, else NO.
+ */
+- (BOOL) exists;
+
+
+#pragma mark - Data export
+
+/**
+ * Returns the raw value at this location, coupled with any metadata, such as priority.
+ *
+ * Priorities, where they exist, are accessible under the ".priority" key in instances of NSDictionary.
+ * For leaf locations with priorities, the value will be under the ".value" key.
+ */
+- (id __nullable) valueInExportFormat;
+
+
+#pragma mark - Properties
+
+/**
+ * Returns the contents of this data snapshot as native types.
+ *
+ * Data types returned:
+ * + NSDictionary
+ * + NSArray
+ * + NSNumber (also includes booleans)
+ * + NSString
+ *
+ * @return The data as a native object.
+ */
+@property (strong, readonly, nonatomic, nullable) id value;
+
+
+/**
+ * Gets the number of children for this DataSnapshot.
+ *
+ * @return An integer indicating the number of children.
+ */
+@property (readonly, nonatomic) NSUInteger childrenCount;
+
+
+/**
+ * Gets a FIRDatabaseReference for the location that this data came from.
+ *
+ * @return A FIRDatabaseReference instance for the location of this data.
+ */
+@property (nonatomic, readonly, strong) FIRDatabaseReference * ref;
+
+
+/**
+ * The key of the location that generated this FIRDataSnapshot.
+ *
+ * @return An NSString containing the key for the location of this FIRDataSnapshot.
+ */
+@property (strong, readonly, nonatomic) NSString* key;
+
+
+/**
+ * An iterator for snapshots of the child nodes in this snapshot.
+ * You can use the native for..in syntax:
+ *
+ * for (FIRDataSnapshot* child in snapshot.children) {
+ * ...
+ * }
+ *
+ * @return An NSEnumerator of the children.
+ */
+@property (strong, readonly, nonatomic) NSEnumerator* children;
+
+/**
+ * The priority of the data in this FIRDataSnapshot.
+ *
+ * @return The priority as a string, or nil if no priority was set.
+ */
+@property (strong, readonly, nonatomic, nullable) id priority;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Database/Api/FIRDataSnapshot.m b/Firebase/Database/Api/FIRDataSnapshot.m
new file mode 100644
index 0000000..9559c38
--- /dev/null
+++ b/Firebase/Database/Api/FIRDataSnapshot.m
@@ -0,0 +1,101 @@
+/*
+ * 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 "FIRDataSnapshot.h"
+#import "FIRDataSnapshot_Private.h"
+#import "FChildrenNode.h"
+#import "FValidation.h"
+#import "FTransformedEnumerator.h"
+#import "FIRDatabaseReference.h"
+
+@interface FIRDataSnapshot ()
+@property (nonatomic, strong) FIRDatabaseReference *ref;
+@end
+
+@implementation FIRDataSnapshot
+
+- (id)initWithRef:(FIRDatabaseReference *)ref indexedNode:(FIndexedNode *)node
+{
+ self = [super init];
+ if (self != nil) {
+ self->_ref = ref;
+ self->_node = node;
+ }
+ return self;
+}
+
+- (id) value {
+ return [self.node.node val];
+}
+
+- (id) valueInExportFormat {
+ return [self.node.node valForExport:YES];
+}
+
+- (FIRDataSnapshot *)childSnapshotForPath:(NSString *)childPathString {
+ [FValidation validateFrom:@"child:" validPathString:childPathString];
+ FPath* childPath = [[FPath alloc] initWith:childPathString];
+ FIRDatabaseReference * childRef = [self.ref child:childPathString];
+
+ id<FNode> childNode = [self.node.node getChild:childPath];
+ return [[FIRDataSnapshot alloc] initWithRef:childRef indexedNode:[FIndexedNode indexedNodeWithNode:childNode]];
+}
+
+- (BOOL) hasChild:(NSString *)childPathString {
+ [FValidation validateFrom:@"hasChild:" validPathString:childPathString];
+ FPath* childPath = [[FPath alloc] initWith:childPathString];
+ return ! [[self.node.node getChild:childPath] isEmpty];
+}
+
+- (id) priority {
+ id<FNode> priority = [self.node.node getPriority];
+ return priority.val;
+}
+
+
+- (BOOL) hasChildren {
+ if([self.node.node isLeafNode]) {
+ return false;
+ }
+ else {
+ return ![self.node.node isEmpty];
+ }
+}
+
+- (BOOL) exists {
+ return ![self.node.node isEmpty];
+}
+
+- (NSString *) key {
+ return [self.ref key];
+}
+
+- (NSUInteger) childrenCount {
+ return [self.node.node numChildren];
+}
+
+- (NSEnumerator *) children {
+ return [[FTransformedEnumerator alloc] initWithEnumerator:self.node.childEnumerator andTransform:^id(FNamedNode *node) {
+ FIRDatabaseReference *childRef = [self.ref child:node.name];
+ return [[FIRDataSnapshot alloc] initWithRef:childRef indexedNode:[FIndexedNode indexedNodeWithNode:node.node]];
+ }];
+}
+
+- (NSString *) description {
+ return [NSString stringWithFormat:@"Snap (%@) %@", self.key, self.node.node];
+}
+
+@end
diff --git a/Firebase/Database/Api/FIRDatabase.h b/Firebase/Database/Api/FIRDatabase.h
new file mode 100644
index 0000000..e77ed31
--- /dev/null
+++ b/Firebase/Database/Api/FIRDatabase.h
@@ -0,0 +1,140 @@
+/*
+ * 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 <Foundation/Foundation.h>
+#import "FIRDatabaseReference.h"
+#import "FIRDatabaseSwiftNameSupport.h"
+
+@class FIRApp;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * The entry point for accessing a Firebase Database. You can get an instance by calling
+ * [FIRDatabase database]. To access a location in the database and read or write data,
+ * use [FIRDatabase reference].
+ */
+FIR_SWIFT_NAME(Database)
+@interface FIRDatabase : NSObject
+
+/**
+ * Gets the instance of FIRDatabase for the default FIRApp.
+ *
+ * @return A FIRDatabase instance.
+ */
++ (FIRDatabase *) database FIR_SWIFT_NAME(database());
+
+/**
+ * Gets an instance of FIRDatabase for a specific FIRApp.
+ *
+ * @param app The FIRApp to get a FIRDatabase for.
+ * @return A FIRDatabase instance.
+ */
++ (FIRDatabase *) databaseForApp:(FIRApp*)app FIR_SWIFT_NAME(database(app:));
+
+/** The FIRApp instance to which this FIRDatabase belongs. */
+@property (weak, readonly, nonatomic) FIRApp *app;
+
+/**
+ * Gets a FIRDatabaseReference for the root of your Firebase Database.
+ */
+- (FIRDatabaseReference *) reference;
+
+/**
+ * Gets a FIRDatabaseReference for the provided path.
+ *
+ * @param path Path to a location in your Firebase Database.
+ * @return A FIRDatabaseReference pointing to the specified path.
+ */
+- (FIRDatabaseReference *) referenceWithPath:(NSString *)path;
+
+/**
+ * Gets a FIRDatabaseReference for the provided URL. The URL must be a URL to a path
+ * within this Firebase Database. To create a FIRDatabaseReference to a different database,
+ * create a FIRApp} with a FIROptions object configured with the appropriate database URL.
+ *
+ * @param databaseUrl A URL to a path within your database.
+ * @return A FIRDatabaseReference for the provided URL.
+*/
+- (FIRDatabaseReference *) referenceFromURL:(NSString *)databaseUrl;
+
+/**
+ * The Firebase Database client automatically queues writes and sends them to the server at the earliest opportunity,
+ * depending on network connectivity. In some cases (e.g. offline usage) there may be a large number of writes
+ * waiting to be sent. Calling this method will purge all outstanding writes so they are abandoned.
+ *
+ * All writes will be purged, including transactions and onDisconnect writes. The writes will
+ * be rolled back locally, perhaps triggering events for affected event listeners, and the client will not
+ * (re-)send them to the Firebase Database backend.
+ */
+- (void)purgeOutstandingWrites;
+
+/**
+ * Shuts down our connection to the Firebase Database backend until goOnline is called.
+ */
+- (void)goOffline;
+
+/**
+ * Resumes our connection to the Firebase Database backend after a previous goOffline call.
+ */
+- (void)goOnline;
+
+/**
+ * The Firebase Database client will cache synchronized data and keep track of all writes you've
+ * initiated while your application is running. It seamlessly handles intermittent network
+ * connections and re-sends write operations when the network connection is restored.
+ *
+ * However by default your write operations and cached data are only stored in-memory and will
+ * be lost when your app restarts. By setting this value to `YES`, the data will be persisted
+ * to on-device (disk) storage and will thus be available again when the app is restarted
+ * (even when there is no network connectivity at that time). Note that this property must be
+ * set before creating your first Database reference and only needs to be called once per
+ * application.
+ *
+ */
+@property (nonatomic) BOOL persistenceEnabled FIR_SWIFT_NAME(isPersistenceEnabled);
+
+/**
+ * By default the Firebase Database client will use up to 10MB of disk space to cache data. If the cache grows beyond
+ * this size, the client will start removing data that hasn't been recently used. If you find that your application
+ * caches too little or too much data, call this method to change the cache size. This property must be set before
+ * creating your first FIRDatabaseReference and only needs to be called once per application.
+ *
+ * Note that the specified cache size is only an approximation and the size on disk may temporarily exceed it
+ * at times. Cache sizes smaller than 1 MB or greater than 100 MB are not supported.
+ */
+@property (nonatomic) NSUInteger persistenceCacheSizeBytes;
+
+/**
+ * Sets the dispatch queue on which all events are raised. The default queue is the main queue.
+ *
+ * Note that this must be set before creating your first Database reference.
+ */
+@property (nonatomic, strong) dispatch_queue_t callbackQueue;
+
+/**
+ * Enables verbose diagnostic logging.
+ *
+ * @param enabled YES to enable logging, NO to disable.
+ */
++ (void) setLoggingEnabled:(BOOL)enabled;
+
+/** Retrieve the Firebase Database SDK version. */
++ (NSString *) sdkVersion;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Database/Api/FIRDatabase.m b/Firebase/Database/Api/FIRDatabase.m
new file mode 100644
index 0000000..124b463
--- /dev/null
+++ b/Firebase/Database/Api/FIRDatabase.m
@@ -0,0 +1,268 @@
+/*
+ * 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 <Foundation/Foundation.h>
+#import "FIRDatabase.h"
+#import "FIRDatabase_Private.h"
+#import "FIRDatabaseQuery_Private.h"
+#import "FRepoManager.h"
+#import "FValidation.h"
+#import "FIRDatabaseConfig_Private.h"
+#import "FRepoInfo.h"
+#import "FIRDatabaseConfig.h"
+#import "FIRDatabaseReference_Private.h"
+
+/**
+ * This is a hack that defines all the methods we need from FIRApp/Options. At runtime we use reflection to get the
+ * default FIRApp instance if we need it. Since protocols don't carry any runtime information and selectors
+ * are invoked by name we can write code against this protocol as long as the method signatures don't change.
+ *
+ * TODO: Consider weak-linking the actual Firebase/Core framework or something.
+ */
+
+extern NSString *const kFIRDefaultAppName;
+
+@protocol FIROptionsLike <NSObject>
+@property(nonatomic, readonly, copy) NSString *databaseURL;
+@end
+
+@protocol FIRAppLike <NSObject>
+@property(nonatomic, readonly) id<FIROptionsLike> options;
+@property(nonatomic, copy, readonly) NSString *name;
+@end
+
+@interface FIRDatabase ()
+@property (nonatomic, strong) FRepoInfo *repoInfo;
+@property (nonatomic, strong) FIRDatabaseConfig *config;
+@property (nonatomic, strong) FRepo *repo;
+@end
+
+@implementation FIRDatabase
+
+// The STR and STR_EXPAND macro allow a numeric version passed to he compiler driver
+// with a -D to be treated as a string instead of an invalid floating point value.
+#define STR(x) STR_EXPAND(x)
+#define STR_EXPAND(x) #x
+static const char *FIREBASE_SEMVER = (const char *)STR(FIRDatabase_VERSION);
+
+/**
+ * A static NSMutableDictionary of FirebaseApp names to FirebaseDatabase instance. To ensure thread-
+ * safety, it should only be accessed in databaseForApp, which is synchronized.
+ *
+ * TODO: This serves a duplicate purpose as RepoManager. We should clean up.
+ * TODO: We should maybe be conscious of leaks and make this a weak map or similar
+ * but we have a lot of work to do to allow FirebaseDatabase/Repo etc. to be GC'd.
+ */
++ (NSMutableDictionary *)instances {
+ static dispatch_once_t pred = 0;
+ static NSMutableDictionary *instances;
+ dispatch_once(&pred, ^{
+ instances = [NSMutableDictionary dictionary];
+ });
+ return instances;
+}
+
++ (FIRDatabase *)database {
+ id<FIRAppLike> app = [FIRDatabase getDefaultApp];
+ if (app == nil) {
+ [NSException raise:@"FIRAppNotConfigured" format:@"Failed to get default FIRDatabase instance. Must call FIRApp.configure() before using FIRDatabase."];
+ }
+ return [FIRDatabase databaseForApp:(FIRApp*)app];
+}
+
++ (FIRDatabase *)databaseForApp:(id)app {
+ if (app == nil) {
+ [NSException raise:@"InvalidFIRApp" format:@"nil FIRApp instance passed to databaseForApp."];
+ }
+ NSMutableDictionary *instances = [self instances];
+ @synchronized (instances) {
+ id<FIRAppLike> appLike = (id<FIRAppLike>)app;
+ FIRDatabase *database = instances[appLike.name];
+ if (!database) {
+ NSString *databaseUrl = appLike.options.databaseURL;
+ if (databaseUrl == nil) {
+ [NSException raise:@"MissingDatabaseURL" format:@"Failed to get FIRDatabase instance: FIRApp object has no "
+ "databaseURL in its FirebaseOptions object."];
+ }
+
+ FParsedUrl *parsedUrl = [FUtilities parseUrl:databaseUrl];
+ if (![parsedUrl.path isEmpty]) {
+ [NSException raise:@"InvalidDatabaseURL" format:@"Configured Database URL '%@' is invalid. It should "
+ "point to the root of a Firebase Database but it includes a path: %@",
+ databaseUrl, [parsedUrl.path toString]];
+ }
+
+ id<FAuthTokenProvider> authTokenProvider = [FAuthTokenProvider authTokenProviderForApp:appLike];
+
+ // If this is the default app, don't set the session persistence key so that we use our
+ // default ("default") instead of the FIRApp default ("[DEFAULT]") so that we
+ // preserve the default location used by the legacy Firebase SDK.
+ NSString *sessionIdentifier = @"default";
+ if (![appLike.name isEqualToString:kFIRDefaultAppName]) {
+ sessionIdentifier = appLike.name;
+ }
+
+ FIRDatabaseConfig *config = [[FIRDatabaseConfig alloc] initWithSessionIdentifier:sessionIdentifier
+ authTokenProvider:authTokenProvider];
+ database = [[FIRDatabase alloc] initWithApp:appLike repoInfo:parsedUrl.repoInfo config:config];
+ instances[appLike.name] = database;
+ }
+
+ return database;
+ }
+}
+
++ (NSString *) buildVersion {
+ // TODO: Restore git hash when build moves back to git
+ return [NSString stringWithFormat:@"%s_%s", FIREBASE_SEMVER, __DATE__];
+}
+
++ (FIRDatabase *)createDatabaseForTests:(FRepoInfo *)repoInfo config:(FIRDatabaseConfig *)config {
+ FIRDatabase *db = [[FIRDatabase alloc] initWithApp:nil repoInfo:repoInfo config:config];
+ [db ensureRepo];
+ return db;
+}
+
+
++ (NSString *) sdkVersion {
+ return [NSString stringWithUTF8String:FIREBASE_SEMVER];
+}
+
++ (void) setLoggingEnabled:(BOOL)enabled {
+ [FUtilities setLoggingEnabled:enabled];
+ FFLog(@"I-RDB024001", @"BUILD Version: %@", [FIRDatabase buildVersion]);
+}
+
+
+- (id)initWithApp:(id <FIRAppLike>)appLike repoInfo:(FRepoInfo *)info config:(FIRDatabaseConfig *)config {
+ self = [super init];
+ if (self != nil) {
+ self->_repoInfo = info;
+ self->_config = config;
+ self->_app = (FIRApp*) appLike;
+ }
+ return self;
+}
+
+- (FIRDatabaseReference *)reference {
+ [self ensureRepo];
+
+ return [[FIRDatabaseReference alloc] initWithRepo:self.repo path:[FPath empty]];
+}
+
+- (FIRDatabaseReference *)referenceWithPath:(NSString *)path {
+ [self ensureRepo];
+
+ [FValidation validateFrom:@"referenceWithPath" validRootPathString:path];
+ FPath *childPath = [[FPath alloc] initWith:path];
+ return [[FIRDatabaseReference alloc] initWithRepo:self.repo path:childPath];
+}
+
+- (FIRDatabaseReference *)referenceFromURL:(NSString *)databaseUrl {
+ [self ensureRepo];
+
+ if (databaseUrl == nil) {
+ [NSException raise:@"InvalidDatabaseURL" format:@"Invalid nil url passed to referenceFromURL:"];
+ }
+ FParsedUrl *parsedUrl = [FUtilities parseUrl:databaseUrl];
+ [FValidation validateFrom:@"referenceFromURL:" validURL:parsedUrl];
+ if (![parsedUrl.repoInfo.host isEqualToString:_repoInfo.host]) {
+ [NSException raise:@"InvalidDatabaseURL" format:@"Invalid URL (%@) passed to getReference(). URL was expected "
+ "to match configured Database URL: %@", databaseUrl, [self reference].URL];
+ }
+ return [[FIRDatabaseReference alloc] initWithRepo:self.repo path:parsedUrl.path];
+}
+
+
+- (void)purgeOutstandingWrites {
+ [self ensureRepo];
+
+ dispatch_async([FIRDatabaseQuery sharedQueue], ^{
+ [self.repo purgeOutstandingWrites];
+ });
+}
+
+- (void)goOnline {
+ [self ensureRepo];
+
+ dispatch_async([FIRDatabaseQuery sharedQueue], ^{
+ [self.repo resume];
+ });
+}
+
+
+- (void)goOffline {
+ [self ensureRepo];
+
+ dispatch_async([FIRDatabaseQuery sharedQueue], ^{
+ [self.repo interrupt];
+ });
+}
+
++ (id<FIRAppLike>) getDefaultApp {
+ Class appClass = NSClassFromString(@"FIRApp");
+ if (appClass == nil) {
+ [NSException raise:@"FailedToFindFIRApp" format:@"Failed to find FIRApp class."];
+ return nil;
+ } else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wundeclared-selector"
+ return [appClass performSelector:@selector(defaultApp)];
+#pragma clang diagnostic pop
+ }
+}
+
+- (void)setPersistenceEnabled:(BOOL)persistenceEnabled {
+ [self assertUnfrozen:@"setPersistenceEnabled"];
+ self->_config.persistenceEnabled = persistenceEnabled;
+}
+
+- (BOOL)persistenceEnabled {
+ return self->_config.persistenceEnabled;
+}
+
+- (void)setPersistenceCacheSizeBytes:(NSUInteger)persistenceCacheSizeBytes {
+ [self assertUnfrozen:@"setPersistenceCacheSizeBytes"];
+ self->_config.persistenceCacheSizeBytes = persistenceCacheSizeBytes;
+}
+
+- (NSUInteger)persistenceCacheSizeBytes {
+ return self->_config.persistenceCacheSizeBytes;
+}
+
+- (void)setCallbackQueue:(dispatch_queue_t)callbackQueue {
+ [self assertUnfrozen:@"setCallbackQueue"];
+ self->_config.callbackQueue = callbackQueue;
+}
+
+- (dispatch_queue_t)callbackQueue {
+ return self->_config.callbackQueue;
+}
+
+- (void) assertUnfrozen:(NSString*)methodName {
+ if (self.repo != nil) {
+ [NSException raise:@"FIRDatabaseAlreadyInUse" format:@"Calls to %@ must be made before any other usage of "
+ "FIRDatabase instance.", methodName];
+ }
+}
+
+- (void) ensureRepo {
+ if (self.repo == nil) {
+ self.repo = [FRepoManager createRepo:self.repoInfo config:self.config database:self];
+ }
+}
+
+@end
diff --git a/Firebase/Database/Api/FIRDatabaseConfig.h b/Firebase/Database/Api/FIRDatabaseConfig.h
new file mode 100644
index 0000000..d41f3a8
--- /dev/null
+++ b/Firebase/Database/Api/FIRDatabaseConfig.h
@@ -0,0 +1,63 @@
+/*
+ * 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 <Foundation/Foundation.h>
+
+@protocol FAuthTokenProvider;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * TODO: Merge FIRDatabaseConfig into FIRDatabase.
+ */
+@interface FIRDatabaseConfig : NSObject
+
+- (id)initWithSessionIdentifier:(NSString *)identifier authTokenProvider:(id<FAuthTokenProvider>)authTokenProvider;
+
+/**
+ * By default the Firebase Database client will keep data in memory while your application is running, but not
+ * when it is restarted. By setting this value to YES, the data will be persisted to on-device (disk)
+ * storage and will thus be available again when the app is restarted (even when there is no network
+ * connectivity at that time). Note that this property must be set before creating your first FIRDatabaseReference
+ * and only needs to be called once per application.
+ *
+ * If your app uses Firebase Authentication, the client will automatically persist the user's authentication
+ * token across restarts, even without persistence enabled. But if the auth token expired while offline and
+ * you've enabled persistence, the client will pause write operations until you successfully re-authenticate
+ * (or explicitly unauthenticate) to prevent your writes from being sent unauthenticated and failing due to
+ * security rules.
+ */
+@property (nonatomic) BOOL persistenceEnabled;
+
+/**
+ * By default the Firebase Database client will use up to 10MB of disk space to cache data. If the cache grows beyond this size,
+ * the client will start removing data that hasn't been recently used. If you find that your application caches too
+ * little or too much data, call this method to change the cache size. This property must be set before creating
+ * your first FIRDatabaseReference and only needs to be called once per application.
+ *
+ * Note that the specified cache size is only an approximation and the size on disk may temporarily exceed it
+ * at times.
+ */
+@property (nonatomic) NSUInteger persistenceCacheSizeBytes;
+
+/**
+ * Sets the dispatch queue on which all events are raised. The default queue is the main queue.
+ */
+@property (nonatomic, strong) dispatch_queue_t callbackQueue;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Database/Api/FIRDatabaseConfig.m b/Firebase/Database/Api/FIRDatabaseConfig.m
new file mode 100644
index 0000000..f4639f9
--- /dev/null
+++ b/Firebase/Database/Api/FIRDatabaseConfig.m
@@ -0,0 +1,117 @@
+/*
+ * 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 "FIRApp.h"
+#import "FIRDatabaseConfig.h"
+#import "FIRDatabaseConfig_Private.h"
+#import "FIRNoopAuthTokenProvider.h"
+#import "FAuthTokenProvider.h"
+
+@interface FIRDatabaseConfig (Private)
+
+@property (nonatomic, strong, readwrite) NSString *sessionIdentifier;
+
+@end
+
+@implementation FIRDatabaseConfig
+
+- (id)init {
+ [NSException raise:NSInvalidArgumentException format:@"Can't create config objects!"];
+ return nil;
+}
+
+- (id)initWithSessionIdentifier:(NSString *)identifier authTokenProvider:(id<FAuthTokenProvider>)authTokenProvider {
+ self = [super init];
+ if (self != nil) {
+ self->_sessionIdentifier = identifier;
+ self->_callbackQueue = dispatch_get_main_queue();
+ self->_persistenceCacheSizeBytes = 10*1024*1024; // Default cache size is 10MB
+ self->_authTokenProvider = authTokenProvider;
+ }
+ return self;
+}
+
+- (void)assertUnfrozen {
+ if (self.isFrozen) {
+ [NSException raise:NSGenericException format:@"Can't modify config objects after they are in use for FIRDatabaseReferences."];
+ }
+}
+
+- (void)setAuthTokenProvider:(id<FAuthTokenProvider>)authTokenProvider {
+ [self assertUnfrozen];
+ self->_authTokenProvider = authTokenProvider;
+}
+
+- (void)setPersistenceEnabled:(BOOL)persistenceEnabled {
+ [self assertUnfrozen];
+ self->_persistenceEnabled = persistenceEnabled;
+}
+
+- (void)setPersistenceCacheSizeBytes:(NSUInteger)persistenceCacheSizeBytes {
+ [self assertUnfrozen];
+ // Can't be less than 1MB
+ if (persistenceCacheSizeBytes < 1024*1024) {
+ [NSException raise:NSInvalidArgumentException format:@"The minimum cache size must be at least 1MB"];
+ }
+ if (persistenceCacheSizeBytes > 100*1024*1024) {
+ [NSException raise:NSInvalidArgumentException format:@"Firebase Database currently doesn't support a cache size larger than 100MB"];
+ }
+ self->_persistenceCacheSizeBytes = persistenceCacheSizeBytes;
+}
+
+- (void)setCallbackQueue:(dispatch_queue_t)callbackQueue {
+ [self assertUnfrozen];
+ self->_callbackQueue = callbackQueue;
+}
+
+- (void)freeze {
+ self->_isFrozen = YES;
+}
+
+// TODO: Only used for tests. Migrate to FIRDatabase and remove.
++ (FIRDatabaseConfig *)defaultConfig {
+ static dispatch_once_t onceToken;
+ static FIRDatabaseConfig *defaultConfig;
+ dispatch_once(&onceToken, ^{
+ defaultConfig = [FIRDatabaseConfig configForName:@"default"];
+ });
+ return defaultConfig;
+}
+
+// TODO: This is only used for tests. We should fix them to go through FIRDatabase and remove
+// this method and the sessionsConfigs dictionary (FIRDatabase automatically creates one config per app).
++ (FIRDatabaseConfig *)configForName:(NSString *)name {
+ NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:@"^[a-zA-Z0-9-_]+$" options:0 error:nil];
+ if ([expression numberOfMatchesInString:name options:0 range:NSMakeRange(0, name.length)] == 0) {
+ [NSException raise:NSInvalidArgumentException format:@"Name can only contain [a-zA-Z0-9-_]"];
+ }
+
+ static dispatch_once_t onceToken;
+ static NSMutableDictionary *sessionConfigs;
+ dispatch_once(&onceToken, ^{
+ sessionConfigs = [NSMutableDictionary dictionary];
+ });
+ @synchronized(sessionConfigs) {
+ if (!sessionConfigs[name]) {
+ id<FAuthTokenProvider> authTokenProvider = [FAuthTokenProvider authTokenProviderForApp:[FIRApp defaultApp]];
+ sessionConfigs[name] = [[FIRDatabaseConfig alloc] initWithSessionIdentifier:name
+ authTokenProvider:authTokenProvider];
+ }
+ return sessionConfigs[name];
+ }
+}
+
+@end
diff --git a/Firebase/Database/Api/FIRDatabaseQuery.h b/Firebase/Database/Api/FIRDatabaseQuery.h
new file mode 100644
index 0000000..be4ad27
--- /dev/null
+++ b/Firebase/Database/Api/FIRDatabaseQuery.h
@@ -0,0 +1,315 @@
+/*
+ * 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 <Foundation/Foundation.h>
+#import "FIRDatabaseSwiftNameSupport.h"
+#import "FIRDataEventType.h"
+#import "FIRDataSnapshot.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * A FIRDatabaseHandle is used to identify listeners of Firebase Database events. These handles
+ * are returned by observeEventType: and and can later be passed to removeObserverWithHandle: to
+ * stop receiving updates.
+ */
+typedef NSUInteger FIRDatabaseHandle FIR_SWIFT_NAME(DatabaseHandle);
+
+/**
+ * A FIRDatabaseQuery instance represents a query over the data at a particular location.
+ *
+ * You create one by calling one of the query methods (queryOrderedByChild:, queryStartingAtValue:, etc.)
+ * on a FIRDatabaseReference. The query methods can be chained to further specify the data you are interested in
+ * observing
+ */
+FIR_SWIFT_NAME(DatabaseQuery)
+@interface FIRDatabaseQuery : NSObject
+
+
+#pragma mark - Attach observers to read data
+
+/**
+ * observeEventType:withBlock: is used to listen for data changes at a particular location.
+ * This is the primary way to read data from the Firebase Database. Your block will be triggered
+ * for the initial data and again whenever the data changes.
+ *
+ * Use removeObserverWithHandle: to stop receiving updates.
+ *
+ * @param eventType The type of event to listen for.
+ * @param block The block that should be called with initial data and updates. It is passed the data as a FIRDataSnapshot.
+ * @return A handle used to unregister this block later using removeObserverWithHandle:
+ */
+- (FIRDatabaseHandle)observeEventType:(FIRDataEventType)eventType withBlock:(void (^)(FIRDataSnapshot *snapshot))block;
+
+
+/**
+ * observeEventType:andPreviousSiblingKeyWithBlock: is used to listen for data changes at a particular location.
+ * This is the primary way to read data from the Firebase Database. Your block will be triggered
+ * for the initial data and again whenever the data changes. In addition, for FIRDataEventTypeChildAdded, FIRDataEventTypeChildMoved, and
+ * FIRDataEventTypeChildChanged events, your block will be passed the key of the previous node by priority order.
+ *
+ * Use removeObserverWithHandle: to stop receiving updates.
+ *
+ * @param eventType The type of event to listen for.
+ * @param block The block that should be called with initial data and updates. It is passed the data as a FIRDataSnapshot
+ * and the previous child's key.
+ * @return A handle used to unregister this block later using removeObserverWithHandle:
+ */
+- (FIRDatabaseHandle)observeEventType:(FIRDataEventType)eventType andPreviousSiblingKeyWithBlock:(void (^)(FIRDataSnapshot *snapshot, NSString *__nullable prevKey))block;
+
+
+/**
+ * observeEventType:withBlock: is used to listen for data changes at a particular location.
+ * This is the primary way to read data from the Firebase Database. Your block will be triggered
+ * for the initial data and again whenever the data changes.
+ *
+ * The cancelBlock will be called if you will no longer receive new events due to no longer having permission.
+ *
+ * Use removeObserverWithHandle: to stop receiving updates.
+ *
+ * @param eventType The type of event to listen for.
+ * @param block The block that should be called with initial data and updates. It is passed the data as a FIRDataSnapshot.
+ * @param cancelBlock The block that should be called if this client no longer has permission to receive these events
+ * @return A handle used to unregister this block later using removeObserverWithHandle:
+ */
+- (FIRDatabaseHandle)observeEventType:(FIRDataEventType)eventType withBlock:(void (^)(FIRDataSnapshot *snapshot))block withCancelBlock:(nullable void (^)(NSError* error))cancelBlock;
+
+
+/**
+ * observeEventType:andPreviousSiblingKeyWithBlock: is used to listen for data changes at a particular location.
+ * This is the primary way to read data from the Firebase Database. Your block will be triggered
+ * for the initial data and again whenever the data changes. In addition, for FIRDataEventTypeChildAdded, FIRDataEventTypeChildMoved, and
+ * FIRDataEventTypeChildChanged events, your block will be passed the key of the previous node by priority order.
+ *
+ * The cancelBlock will be called if you will no longer receive new events due to no longer having permission.
+ *
+ * Use removeObserverWithHandle: to stop receiving updates.
+ *
+ * @param eventType The type of event to listen for.
+ * @param block The block that should be called with initial data and updates. It is passed the data as a FIRDataSnapshot
+ * and the previous child's key.
+ * @param cancelBlock The block that should be called if this client no longer has permission to receive these events
+ * @return A handle used to unregister this block later using removeObserverWithHandle:
+ */
+- (FIRDatabaseHandle)observeEventType:(FIRDataEventType)eventType andPreviousSiblingKeyWithBlock:(void (^)(FIRDataSnapshot *snapshot, NSString *__nullable prevKey))block withCancelBlock:(nullable void (^)(NSError* error))cancelBlock;
+
+
+/**
+ * This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned.
+ *
+ * @param eventType The type of event to listen for.
+ * @param block The block that should be called. It is passed the data as a FIRDataSnapshot.
+ */
+- (void)observeSingleEventOfType:(FIRDataEventType)eventType withBlock:(void (^)(FIRDataSnapshot *snapshot))block;
+
+
+/**
+ * This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned. In addition, for FIRDataEventTypeChildAdded, FIRDataEventTypeChildMoved, and
+ * FIRDataEventTypeChildChanged events, your block will be passed the key of the previous node by priority order.
+ *
+ * @param eventType The type of event to listen for.
+ * @param block The block that should be called. It is passed the data as a FIRDataSnapshot and the previous child's key.
+ */
+- (void)observeSingleEventOfType:(FIRDataEventType)eventType andPreviousSiblingKeyWithBlock:(void (^)(FIRDataSnapshot *snapshot, NSString *__nullable prevKey))block;
+
+
+/**
+ * This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned.
+ *
+ * The cancelBlock will be called if you do not have permission to read data at this location.
+ *
+ * @param eventType The type of event to listen for.
+ * @param block The block that should be called. It is passed the data as a FIRDataSnapshot.
+ * @param cancelBlock The block that will be called if you don't have permission to access this data
+ */
+- (void)observeSingleEventOfType:(FIRDataEventType)eventType withBlock:(void (^)(FIRDataSnapshot *snapshot))block withCancelBlock:(nullable void (^)(NSError* error))cancelBlock;
+
+
+/**
+ * This is equivalent to observeEventType:withBlock:, except the block is immediately canceled after the initial data is returned. In addition, for FIRDataEventTypeChildAdded, FIRDataEventTypeChildMoved, and
+ * FIRDataEventTypeChildChanged events, your block will be passed the key of the previous node by priority order.
+ *
+ * The cancelBlock will be called if you do not have permission to read data at this location.
+ *
+ * @param eventType The type of event to listen for.
+ * @param block The block that should be called. It is passed the data as a FIRDataSnapshot and the previous child's key.
+ * @param cancelBlock The block that will be called if you don't have permission to access this data
+ */
+- (void)observeSingleEventOfType:(FIRDataEventType)eventType andPreviousSiblingKeyWithBlock:(void (^)(FIRDataSnapshot *snapshot, NSString *__nullable prevKey))block withCancelBlock:(nullable void (^)(NSError* error))cancelBlock;
+
+
+#pragma mark - Detaching observers
+
+/**
+ * Detach a block previously attached with observeEventType:withBlock:.
+ *
+ * @param handle The handle returned by the call to observeEventType:withBlock: which we are trying to remove.
+ */
+- (void) removeObserverWithHandle:(FIRDatabaseHandle)handle;
+
+
+/**
+ * Detach all blocks previously attached to this Firebase Database location with observeEventType:withBlock:
+ */
+- (void) removeAllObservers;
+
+/**
+ * By calling `keepSynced:YES` on a location, the data for that location will automatically be downloaded and
+ * kept in sync, even when no listeners are attached for that location. Additionally, while a location is kept
+ * synced, it will not be evicted from the persistent disk cache.
+ *
+ * @param keepSynced Pass YES to keep this location synchronized, pass NO to stop synchronization.
+*/
+ - (void) keepSynced:(BOOL)keepSynced;
+
+
+#pragma mark - Querying and limiting
+
+/**
+* queryLimitedToFirst: is used to generate a reference to a limited view of the data at this location.
+* The FIRDatabaseQuery instance returned by queryLimitedToFirst: will respond to at most the first limit child nodes.
+*
+* @param limit The upper bound, inclusive, for the number of child nodes to receive events for
+* @return A FIRDatabaseQuery instance, limited to at most limit child nodes.
+*/
+- (FIRDatabaseQuery *)queryLimitedToFirst:(NSUInteger)limit;
+
+
+/**
+* queryLimitedToLast: is used to generate a reference to a limited view of the data at this location.
+* The FIRDatabaseQuery instance returned by queryLimitedToLast: will respond to at most the last limit child nodes.
+*
+* @param limit The upper bound, inclusive, for the number of child nodes to receive events for
+* @return A FIRDatabaseQuery instance, limited to at most limit child nodes.
+*/
+- (FIRDatabaseQuery *)queryLimitedToLast:(NSUInteger)limit;
+
+/**
+ * queryOrderBy: is used to generate a reference to a view of the data that's been sorted by the values of
+ * a particular child key. This method is intended to be used in combination with queryStartingAtValue:,
+ * queryEndingAtValue:, or queryEqualToValue:.
+ *
+ * @param key The child key to use in ordering data visible to the returned FIRDatabaseQuery
+ * @return A FIRDatabaseQuery instance, ordered by the values of the specified child key.
+*/
+- (FIRDatabaseQuery *)queryOrderedByChild:(NSString *)key;
+
+/**
+ * queryOrderedByKey: is used to generate a reference to a view of the data that's been sorted by child key.
+ * This method is intended to be used in combination with queryStartingAtValue:, queryEndingAtValue:,
+ * or queryEqualToValue:.
+ *
+ * @return A FIRDatabaseQuery instance, ordered by child keys.
+ */
+- (FIRDatabaseQuery *) queryOrderedByKey;
+
+/**
+ * queryOrderedByValue: is used to generate a reference to a view of the data that's been sorted by child value.
+ * This method is intended to be used in combination with queryStartingAtValue:, queryEndingAtValue:,
+ * or queryEqualToValue:.
+ *
+ * @return A FIRDatabaseQuery instance, ordered by child value.
+ */
+- (FIRDatabaseQuery *) queryOrderedByValue;
+
+/**
+ * queryOrderedByPriority: is used to generate a reference to a view of the data that's been sorted by child
+ * priority. This method is intended to be used in combination with queryStartingAtValue:, queryEndingAtValue:,
+ * or queryEqualToValue:.
+ *
+ * @return A FIRDatabaseQuery instance, ordered by child priorities.
+ */
+- (FIRDatabaseQuery *) queryOrderedByPriority;
+
+/**
+ * queryStartingAtValue: is used to generate a reference to a limited view of the data at this location.
+ * The FIRDatabaseQuery instance returned by queryStartingAtValue: will respond to events at nodes with a value
+ * greater than or equal to startValue.
+ *
+ * @param startValue The lower bound, inclusive, for the value of data visible to the returned FIRDatabaseQuery
+ * @return A FIRDatabaseQuery instance, limited to data with value greater than or equal to startValue
+ */
+- (FIRDatabaseQuery *)queryStartingAtValue:(nullable id)startValue;
+
+/**
+ * queryStartingAtValue:childKey: is used to generate a reference to a limited view of the data at this location.
+ * The FIRDatabaseQuery instance returned by queryStartingAtValue:childKey will respond to events at nodes with a value
+ * greater than startValue, or equal to startValue and with a key greater than or equal to childKey. This is most
+ * useful when implementing pagination in a case where multiple nodes can match the startValue.
+ *
+ * @param startValue The lower bound, inclusive, for the value of data visible to the returned FIRDatabaseQuery
+ * @param childKey The lower bound, inclusive, for the key of nodes with value equal to startValue
+ * @return A FIRDatabaseQuery instance, limited to data with value greater than or equal to startValue
+ */
+- (FIRDatabaseQuery *)queryStartingAtValue:(nullable id)startValue childKey:(nullable NSString *)childKey;
+
+/**
+ * queryEndingAtValue: is used to generate a reference to a limited view of the data at this location.
+ * The FIRDatabaseQuery instance returned by queryEndingAtValue: will respond to events at nodes with a value
+ * less than or equal to endValue.
+ *
+ * @param endValue The upper bound, inclusive, for the value of data visible to the returned FIRDatabaseQuery
+ * @return A FIRDatabaseQuery instance, limited to data with value less than or equal to endValue
+ */
+- (FIRDatabaseQuery *)queryEndingAtValue:(nullable id)endValue;
+
+/**
+ * queryEndingAtValue:childKey: is used to generate a reference to a limited view of the data at this location.
+ * The FIRDatabaseQuery instance returned by queryEndingAtValue:childKey will respond to events at nodes with a value
+ * less than endValue, or equal to endValue and with a key less than or equal to childKey. This is most useful when
+ * implementing pagination in a case where multiple nodes can match the endValue.
+ *
+ * @param endValue The upper bound, inclusive, for the value of data visible to the returned FIRDatabaseQuery
+ * @param childKey The upper bound, inclusive, for the key of nodes with value equal to endValue
+ * @return A FIRDatabaseQuery instance, limited to data with value less than or equal to endValue
+ */
+- (FIRDatabaseQuery *)queryEndingAtValue:(nullable id)endValue childKey:(nullable NSString *)childKey;
+
+/**
+ * queryEqualToValue: is used to generate a reference to a limited view of the data at this location.
+ * The FIRDatabaseQuery instance returned by queryEqualToValue: will respond to events at nodes with a value equal
+ * to the supplied argument.
+ *
+ * @param value The value that the data returned by this FIRDatabaseQuery will have
+ * @return A FIRDatabaseQuery instance, limited to data with the supplied value.
+ */
+- (FIRDatabaseQuery *)queryEqualToValue:(nullable id)value;
+
+/**
+ * queryEqualToValue:childKey: is used to generate a reference to a limited view of the data at this location.
+ * The FIRDatabaseQuery instance returned by queryEqualToValue:childKey will respond to events at nodes with a value
+ * equal to the supplied argument and with their key equal to childKey. There will be at most one node that matches
+ * because child keys are unique.
+ *
+ * @param value The value that the data returned by this FIRDatabaseQuery will have
+ * @param childKey The name of nodes with the right value
+ * @return A FIRDatabaseQuery instance, limited to data with the supplied value and the key.
+ */
+- (FIRDatabaseQuery *)queryEqualToValue:(nullable id)value childKey:(nullable NSString *)childKey;
+
+
+#pragma mark - Properties
+
+/**
+* Gets a FIRDatabaseReference for the location of this query.
+*
+* @return A FIRDatabaseReference for the location of this query.
+*/
+@property (nonatomic, readonly, strong) FIRDatabaseReference * ref;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Database/Api/FIRDatabaseQuery.m b/Firebase/Database/Api/FIRDatabaseQuery.m
new file mode 100644
index 0000000..bcb1733
--- /dev/null
+++ b/Firebase/Database/Api/FIRDatabaseQuery.m
@@ -0,0 +1,525 @@
+/*
+ * 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 "FIRDatabaseQuery.h"
+#import "FIRDatabaseQuery_Private.h"
+#import "FValidation.h"
+#import "FQueryParams.h"
+#import "FQuerySpec.h"
+#import "FValueEventRegistration.h"
+#import "FChildEventRegistration.h"
+#import "FPath.h"
+#import "FKeyIndex.h"
+#import "FPathIndex.h"
+#import "FPriorityIndex.h"
+#import "FValueIndex.h"
+#import "FLeafNode.h"
+#import "FSnapshotUtilities.h"
+#import "FConstants.h"
+
+@implementation FIRDatabaseQuery
+
+@synthesize repo;
+@synthesize path;
+@synthesize queryParams;
+
+#define INVALID_QUERY_PARAM_ERROR @"InvalidQueryParameter"
+
+
++ (dispatch_queue_t)sharedQueue
+{
+ // We use this shared queue across all of the FQueries so things happen FIFO (as opposed to dispatch_get_global_queue(0, 0) which is concurrent)
+ static dispatch_once_t pred;
+ static dispatch_queue_t sharedDispatchQueue;
+
+ dispatch_once(&pred, ^{
+ sharedDispatchQueue = dispatch_queue_create("FirebaseWorker", NULL);
+ });
+
+ return sharedDispatchQueue;
+}
+
+- (id) initWithRepo:(FRepo *)theRepo path:(FPath *)thePath {
+ return [self initWithRepo:theRepo path:thePath params:nil orderByCalled:NO priorityMethodCalled:NO];
+}
+
+- (id) initWithRepo:(FRepo *)theRepo
+ path:(FPath *)thePath
+ params:(FQueryParams *)theParams
+ orderByCalled:(BOOL)orderByCalled
+priorityMethodCalled:(BOOL)priorityMethodCalled {
+ self = [super init];
+ if (self) {
+ self.repo = theRepo;
+ self.path = thePath;
+ if (!theParams) {
+ theParams = [FQueryParams defaultInstance];
+ }
+ if (![theParams isValid]) {
+ @throw [[NSException alloc] initWithName:@"InvalidArgumentError" reason:@"Queries are limited to two constraints" userInfo:nil];
+ }
+ self.queryParams = theParams;
+ self.orderByCalled = orderByCalled;
+ self.priorityMethodCalled = priorityMethodCalled;
+ }
+ return self;
+}
+
+- (FQuerySpec *)querySpec {
+ return [[FQuerySpec alloc] initWithPath:self.path params:self.queryParams];
+}
+
+- (void)validateQueryEndpointsForParams:(FQueryParams *)params {
+ if ([params.index isEqual:[FKeyIndex keyIndex]]) {
+ if ([params hasStart]) {
+ if (params.indexStartKey != [FUtilities minName]) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR format:@"Can't use queryStartingAtValue:childKey: or queryEqualTo:andChildKey: in combination with queryOrderedByKey"];
+ }
+ if (![params.indexStartValue.val isKindOfClass:[NSString class]]) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR format:@"Can't use queryStartingAtValue: with other types than string in combination with queryOrderedByKey"];
+ }
+ }
+ if ([params hasEnd]) {
+ if (params.indexEndKey != [FUtilities maxName]) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR format:@"Can't use queryEndingAtValue:childKey: or queryEqualToValue:childKey: in combination with queryOrderedByKey"];
+ }
+ if (![params.indexEndValue.val isKindOfClass:[NSString class]]) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR format:@"Can't use queryEndingAtValue: with other types than string in combination with queryOrderedByKey"];
+ }
+ }
+ } else if ([params.index isEqual:[FPriorityIndex priorityIndex]]) {
+ if (([params hasStart] && ![FValidation validatePriorityValue:params.indexStartValue.val]) ||
+ ([params hasEnd] && ![FValidation validatePriorityValue:params.indexEndValue.val])) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR format:@"When using queryOrderedByPriority, values provided to queryStartingAtValue:, queryEndingAtValue:, or queryEqualToValue: must be valid priorities."];
+ }
+ }
+}
+
+- (void)validateEqualToCall {
+ if ([self.queryParams hasStart]) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR format:@"Cannot combine queryEqualToValue: and queryStartingAtValue:"];
+ }
+ if ([self.queryParams hasEnd]) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR format:@"Cannot combine queryEqualToValue: and queryEndingAtValue:"];
+ }
+}
+
+- (void)validateNoPreviousOrderByCalled {
+ if (self.orderByCalled) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR format:@"Cannot use multiple queryOrderedBy calls!"];
+ }
+}
+
+- (void)validateIndexValueType:(id)type fromMethod:(NSString *)method {
+ if (type != nil &&
+ ![type isKindOfClass:[NSNumber class]] &&
+ ![type isKindOfClass:[NSString class]] &&
+ ![type isKindOfClass:[NSNull class]]) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR format:@"You can only pass nil, NSString or NSNumber to %@", method];
+ }
+}
+
+- (FIRDatabaseQuery *)queryStartingAtValue:(id)startValue {
+ return [self queryStartingAtInternal:startValue childKey:nil from:@"queryStartingAtValue:" priorityMethod:NO];
+}
+
+- (FIRDatabaseQuery *)queryStartingAtValue:(id)startValue childKey:(NSString *)childKey {
+ if ([self.queryParams.index isEqual:[FKeyIndex keyIndex]]) {
+ @throw [[NSException alloc] initWithName:INVALID_QUERY_PARAM_ERROR
+ reason:@"You must use queryStartingAtValue: instead of queryStartingAtValue:childKey: when using queryOrderedByKey:"
+ userInfo:nil];
+ }
+ return [self queryStartingAtInternal:startValue
+ childKey:childKey
+ from:@"queryStartingAtValue:childKey:"
+ priorityMethod:NO];
+}
+
+- (FIRDatabaseQuery *)queryStartingAtInternal:(id<FNode>)startValue
+ childKey:(NSString *)childKey
+ from:(NSString *)methodName
+ priorityMethod:(BOOL)priorityMethod {
+ [self validateIndexValueType:startValue fromMethod:methodName];
+ if (childKey != nil) {
+ [FValidation validateFrom:methodName validKey:childKey];
+ }
+ if ([self.queryParams hasStart]) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR
+ format:@"Can't call %@ after queryStartingAtValue or queryEqualToValue was previously called", methodName];
+ }
+ id<FNode> startNode = [FSnapshotUtilities nodeFrom:startValue];
+ FQueryParams* params = [self.queryParams startAt:startNode childKey:childKey];
+ [self validateQueryEndpointsForParams:params];
+ return [[FIRDatabaseQuery alloc] initWithRepo:self.repo
+ path:self.path
+ params:params
+ orderByCalled:self.orderByCalled
+ priorityMethodCalled:priorityMethod || self.priorityMethodCalled];
+}
+
+- (FIRDatabaseQuery *)queryEndingAtValue:(id)endValue {
+ return [self queryEndingAtInternal:endValue
+ childKey:nil
+ from:@"queryEndingAtValue:"
+ priorityMethod:NO];
+}
+
+- (FIRDatabaseQuery *)queryEndingAtValue:(id)endValue childKey:(NSString *)childKey {
+ if ([self.queryParams.index isEqual:[FKeyIndex keyIndex]]) {
+ @throw [[NSException alloc] initWithName:INVALID_QUERY_PARAM_ERROR
+ reason:@"You must use queryEndingAtValue: instead of queryEndingAtValue:childKey: when using queryOrderedByKey:"
+ userInfo:nil];
+ }
+
+ return [self queryEndingAtInternal:endValue
+ childKey:childKey
+ from:@"queryEndingAtValue:childKey:"
+ priorityMethod:NO];
+}
+
+- (FIRDatabaseQuery *)queryEndingAtInternal:(id)endValue
+ childKey:(NSString *)childKey
+ from:(NSString *)methodName
+ priorityMethod:(BOOL)priorityMethod {
+ [self validateIndexValueType:endValue fromMethod:methodName];
+ if (childKey != nil) {
+ [FValidation validateFrom:methodName validKey:childKey];
+ }
+ if ([self.queryParams hasEnd]) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR
+ format:@"Can't call %@ after queryEndingAtValue or queryEqualToValue was previously called", methodName];
+ }
+ id<FNode> endNode = [FSnapshotUtilities nodeFrom:endValue];
+ FQueryParams* params = [self.queryParams endAt:endNode childKey:childKey];
+ [self validateQueryEndpointsForParams:params];
+ return [[FIRDatabaseQuery alloc] initWithRepo:self.repo
+ path:self.path
+ params:params
+ orderByCalled:self.orderByCalled
+ priorityMethodCalled:priorityMethod || self.priorityMethodCalled];
+}
+
+- (FIRDatabaseQuery *)queryEqualToValue:(id)value {
+ return [self queryEqualToInternal:value childKey:nil from:@"queryEqualToValue:" priorityMethod:NO];
+}
+
+- (FIRDatabaseQuery *)queryEqualToValue:(id)value childKey:(NSString *)childKey {
+ if ([self.queryParams.index isEqual:[FKeyIndex keyIndex]]) {
+ @throw [[NSException alloc] initWithName:INVALID_QUERY_PARAM_ERROR
+ reason:@"You must use queryEqualToValue: instead of queryEqualTo:childKey: when using queryOrderedByKey:"
+ userInfo:nil];
+ }
+ return [self queryEqualToInternal:value childKey:childKey from:@"queryEqualToValue:childKey:" priorityMethod:NO];
+}
+
+- (FIRDatabaseQuery *)queryEqualToInternal:(id)value
+ childKey:(NSString *)childKey
+ from:(NSString *)methodName
+ priorityMethod:(BOOL)priorityMethod {
+ [self validateIndexValueType:value fromMethod:methodName];
+ if (childKey != nil) {
+ [FValidation validateFrom:methodName validKey:childKey];
+ }
+ if ([self.queryParams hasEnd] || [self.queryParams hasStart]) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR
+ format:@"Can't call %@ after queryStartingAtValue, queryEndingAtValue or queryEqualToValue was previously called", methodName];
+ }
+ id<FNode> node = [FSnapshotUtilities nodeFrom:value];
+ FQueryParams* params = [[self.queryParams startAt:node childKey:childKey] endAt:node childKey:childKey];
+ [self validateQueryEndpointsForParams:params];
+ return [[FIRDatabaseQuery alloc] initWithRepo:self.repo
+ path:self.path
+ params:params
+ orderByCalled:self.orderByCalled
+ priorityMethodCalled:priorityMethod || self.priorityMethodCalled];
+}
+
+- (void)validateLimitRange:(NSUInteger)limit
+{
+ // No need to check for negative ranges, since limit is unsigned
+ if (limit == 0) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR format:@"Limit can't be zero"];
+ }
+ if (limit >= 1l<<31) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR format:@"Limit must be less than 2,147,483,648"];
+ }
+}
+
+- (FIRDatabaseQuery *)queryLimitedToFirst:(NSUInteger)limit {
+ if (self.queryParams.limitSet) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR format:@"Can't call queryLimitedToFirst: if a limit was previously set"];
+ }
+ [self validateLimitRange:limit];
+ FQueryParams* params = [self.queryParams limitToFirst:limit];
+ return [[FIRDatabaseQuery alloc] initWithRepo:self.repo
+ path:self.path
+ params:params
+ orderByCalled:self.orderByCalled
+ priorityMethodCalled:self.priorityMethodCalled];
+}
+
+- (FIRDatabaseQuery *)queryLimitedToLast:(NSUInteger)limit {
+ if (self.queryParams.limitSet) {
+ [NSException raise:INVALID_QUERY_PARAM_ERROR format:@"Can't call queryLimitedToLast: if a limit was previously set"];
+ }
+ [self validateLimitRange:limit];
+ FQueryParams* params = [self.queryParams limitToLast:limit];
+ return [[FIRDatabaseQuery alloc] initWithRepo:self.repo
+ path:self.path
+ params:params
+ orderByCalled:self.orderByCalled
+ priorityMethodCalled:self.priorityMethodCalled];
+}
+
+- (FIRDatabaseQuery *)queryOrderedByChild:(NSString *)indexPathString {
+ if ([indexPathString isEqualToString:@"$key"] || [indexPathString isEqualToString:@".key"]) {
+ @throw [[NSException alloc] initWithName:INVALID_QUERY_PARAM_ERROR
+ reason:[NSString stringWithFormat:@"(queryOrderedByChild:) %@ is invalid. Use queryOrderedByKey: instead.", indexPathString]
+ userInfo:nil];
+ } else if ([indexPathString isEqualToString:@"$priority"] || [indexPathString isEqualToString:@".priority"]) {
+ @throw [[NSException alloc] initWithName:INVALID_QUERY_PARAM_ERROR
+ reason:[NSString stringWithFormat:@"(queryOrderedByChild:) %@ is invalid. Use queryOrderedByPriority: instead.", indexPathString]
+ userInfo:nil];
+ } else if ([indexPathString isEqualToString:@"$value"] || [indexPathString isEqualToString:@".value"]) {
+ @throw [[NSException alloc] initWithName:INVALID_QUERY_PARAM_ERROR
+ reason:[NSString stringWithFormat:@"(queryOrderedByChild:) %@ is invalid. Use queryOrderedByValue: instead.", indexPathString]
+ userInfo:nil];
+ }
+ [self validateNoPreviousOrderByCalled];
+
+ [FValidation validateFrom:@"queryOrderedByChild:" validPathString:indexPathString];
+ FPath *indexPath = [FPath pathWithString:indexPathString];
+ if (indexPath.isEmpty) {
+ @throw [[NSException alloc] initWithName:INVALID_QUERY_PARAM_ERROR
+ reason:[NSString stringWithFormat:@"(queryOrderedByChild:) with an empty path is invalid. Use queryOrderedByValue: instead."]
+ userInfo:nil];
+ }
+ id<FIndex> index = [[FPathIndex alloc] initWithPath:indexPath];
+
+ FQueryParams *params = [self.queryParams orderBy:index];
+ [self validateQueryEndpointsForParams:params];
+ return [[FIRDatabaseQuery alloc] initWithRepo:self.repo
+ path:self.path
+ params:params
+ orderByCalled:YES
+ priorityMethodCalled:self.priorityMethodCalled];
+}
+
+- (FIRDatabaseQuery *) queryOrderedByKey {
+ [self validateNoPreviousOrderByCalled];
+ FQueryParams *params = [self.queryParams orderBy:[FKeyIndex keyIndex]];
+ [self validateQueryEndpointsForParams:params];
+ return [[FIRDatabaseQuery alloc] initWithRepo:self.repo
+ path:self.path
+ params:params
+ orderByCalled:YES
+ priorityMethodCalled:self.priorityMethodCalled];
+}
+
+- (FIRDatabaseQuery *) queryOrderedByValue {
+ [self validateNoPreviousOrderByCalled];
+ FQueryParams *params = [self.queryParams orderBy:[FValueIndex valueIndex]];
+ return [[FIRDatabaseQuery alloc] initWithRepo:self.repo
+ path:self.path
+ params:params
+ orderByCalled:YES
+ priorityMethodCalled:self.priorityMethodCalled];
+}
+
+- (FIRDatabaseQuery *) queryOrderedByPriority {
+ [self validateNoPreviousOrderByCalled];
+ FQueryParams *params = [self.queryParams orderBy:[FPriorityIndex priorityIndex]];
+ return [[FIRDatabaseQuery alloc] initWithRepo:self.repo
+ path:self.path
+ params:params
+ orderByCalled:YES
+ priorityMethodCalled:self.priorityMethodCalled];
+}
+
+- (FIRDatabaseHandle)observeEventType:(FIRDataEventType)eventType withBlock:(void (^)(FIRDataSnapshot *))block {
+ [FValidation validateFrom:@"observeEventType:withBlock:" knownEventType:eventType];
+ return [self observeEventType:eventType withBlock:block withCancelBlock:nil];
+}
+
+
+- (FIRDatabaseHandle)observeEventType:(FIRDataEventType)eventType andPreviousSiblingKeyWithBlock:(fbt_void_datasnapshot_nsstring)block {
+ [FValidation validateFrom:@"observeEventType:andPreviousSiblingKeyWithBlock:" knownEventType:eventType];
+ return [self observeEventType:eventType andPreviousSiblingKeyWithBlock:block withCancelBlock:nil];
+}
+
+
+- (FIRDatabaseHandle)observeEventType:(FIRDataEventType)eventType withBlock:(fbt_void_datasnapshot)block withCancelBlock:(fbt_void_nserror)cancelBlock {
+ [FValidation validateFrom:@"observeEventType:withBlock:withCancelBlock:" knownEventType:eventType];
+
+ if (eventType == FIRDataEventTypeValue) {
+ // Handle FIRDataEventTypeValue specially because they shouldn't have prevName callbacks
+ NSUInteger handle = [[FUtilities LUIDGenerator] integerValue];
+ [self observeValueEventWithHandle:handle withBlock:block cancelCallback:cancelBlock];
+ return handle;
+ } else {
+ // Wrap up the userCallback so we can treat everything as a callback that has a prevName
+ fbt_void_datasnapshot userCallback = [block copy];
+ return [self observeEventType:eventType andPreviousSiblingKeyWithBlock:^(FIRDataSnapshot *snapshot, NSString *prevName) {
+ if (userCallback != nil) {
+ userCallback(snapshot);
+ }
+ } withCancelBlock:cancelBlock];
+ }
+}
+
+- (FIRDatabaseHandle)observeEventType:(FIRDataEventType)eventType andPreviousSiblingKeyWithBlock:(fbt_void_datasnapshot_nsstring)block withCancelBlock:(fbt_void_nserror)cancelBlock {
+ [FValidation validateFrom:@"observeEventType:andPreviousSiblingKeyWithBlock:withCancelBlock:" knownEventType:eventType];
+
+
+ if (eventType == FIRDataEventTypeValue) {
+ // TODO: This gets hit by observeSingleEventOfType. Need to fix.
+ /*
+ @throw [[NSException alloc] initWithName:@"InvalidEventTypeForObserver"
+ reason:@"(observeEventType:andPreviousSiblingKeyWithBlock:withCancelBlock:) Cannot use observeEventType:andPreviousSiblingKeyWithBlock:withCancelBlock: with FIRDataEventTypeValue. Use observeEventType:withBlock:withCancelBlock: instead."
+ userInfo:nil];
+ */
+ }
+
+ NSUInteger handle = [[FUtilities LUIDGenerator] integerValue];
+ NSDictionary *callbacks = @{[NSNumber numberWithInteger:eventType]: [block copy]};
+ [self observeChildEventWithHandle:handle withCallbacks:callbacks cancelCallback:cancelBlock];
+
+ return handle;
+}
+
+// If we want to distinguish between value event listeners and child event listeners, like in the Java client, we can
+// consider exporting this. If we do, add argument validation. Otherwise, arguments are validated in the public-facing
+// portions of the API. Also, move the FIRDatabaseHandle logic.
+- (void)observeValueEventWithHandle:(FIRDatabaseHandle)handle withBlock:(fbt_void_datasnapshot)block cancelCallback:(fbt_void_nserror)cancelBlock {
+ // Note that we don't need to copy the callbacks here, FEventRegistration callback properties set to copy
+ FValueEventRegistration *registration = [[FValueEventRegistration alloc] initWithRepo:self.repo
+ handle:handle
+ callback:block
+ cancelCallback:cancelBlock];
+ dispatch_async([FIRDatabaseQuery sharedQueue], ^{
+ [self.repo addEventRegistration:registration forQuery:self.querySpec];
+ });
+}
+
+// Note: as with the above method, we may wish to expose this at some point.
+- (void)observeChildEventWithHandle:(FIRDatabaseHandle)handle withCallbacks:(NSDictionary *)callbacks cancelCallback:(fbt_void_nserror)cancelBlock {
+ // Note that we don't need to copy the callbacks here, FEventRegistration callback properties set to copy
+ FChildEventRegistration *registration = [[FChildEventRegistration alloc] initWithRepo:self.repo
+ handle:handle
+ callbacks:callbacks
+ cancelCallback:cancelBlock];
+ dispatch_async([FIRDatabaseQuery sharedQueue], ^{
+ [self.repo addEventRegistration:registration forQuery:self.querySpec];
+ });
+}
+
+
+- (void) removeObserverWithHandle:(FIRDatabaseHandle)handle {
+ FValueEventRegistration *event = [[FValueEventRegistration alloc] initWithRepo:self.repo
+ handle:handle
+ callback:nil
+ cancelCallback:nil];
+ dispatch_async([FIRDatabaseQuery sharedQueue], ^{
+ [self.repo removeEventRegistration:event forQuery:self.querySpec];
+ });
+}
+
+
+- (void) removeAllObservers {
+ [self removeObserverWithHandle:NSNotFound];
+}
+
+- (void)keepSynced:(BOOL)keepSynced {
+ if ([self.path.getFront isEqualToString:kDotInfoPrefix]) {
+ [NSException raise:NSInvalidArgumentException format:@"Can't keep query on .info tree synced (this already is the case)."];
+ }
+ dispatch_async([FIRDatabaseQuery sharedQueue], ^{
+ [self.repo keepQuery:self.querySpec synced: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 {
+
+ // XXX: user reported memory leak in method
+
+ // "When you copy a block, any references to other blocks from within that block are copied if necessary—an entire tree may be copied (from the top). If you have block variables and you reference a block from within the block, that block will be copied."
+ // http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW1
+ // So... we don't need to do this since inside the on: we copy this block off the stack to the heap.
+ // __block fbt_void_datasnapshot userCallback = [callback copy];
+
+ [self observeSingleEventOfType:eventType andPreviousSiblingKeyWithBlock:^(FIRDataSnapshot *snapshot, NSString *prevName) {
+ if (block != nil) {
+ block(snapshot);
+ }
+ } withCancelBlock:cancelBlock];
+}
+
+/**
+* Attaches a listener, waits for the first event, and then removes the listener
+*/
+- (void)observeSingleEventOfType:(FIRDataEventType)eventType andPreviousSiblingKeyWithBlock:(fbt_void_datasnapshot_nsstring)block withCancelBlock:(fbt_void_nserror)cancelBlock {
+
+ // XXX: user reported memory leak in method
+
+ // "When you copy a block, any references to other blocks from within that block are copied if necessary—an entire tree may be copied (from the top). If you have block variables and you reference a block from within the block, that block will be copied."
+ // http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW1
+ // So... we don't need to do this since inside the on: we copy this block off the stack to the heap.
+ // __block fbt_void_datasnapshot userCallback = [callback copy];
+
+ __block FIRDatabaseHandle handle;
+ __block BOOL firstCall = YES;
+
+ fbt_void_datasnapshot_nsstring callback = [block copy];
+ fbt_void_datasnapshot_nsstring wrappedCallback = ^(FIRDataSnapshot *snap, NSString* prevName) {
+ if (firstCall) {
+ firstCall = NO;
+ [self removeObserverWithHandle:handle];
+ callback(snap, prevName);
+ }
+ };
+
+ fbt_void_nserror cancelCallback = [cancelBlock copy];
+ handle = [self observeEventType:eventType andPreviousSiblingKeyWithBlock:wrappedCallback withCancelBlock:^(NSError* error){
+
+ [self removeObserverWithHandle:handle];
+
+ if (cancelCallback) {
+ cancelCallback(error);
+ }
+ }];
+}
+
+- (NSString *) description {
+ return [NSString stringWithFormat:@"(%@ %@)", self.path, self.queryParams.description];
+}
+
+- (FIRDatabaseReference *) ref {
+ return [[FIRDatabaseReference alloc] initWithRepo:self.repo path:self.path];
+}
+
+@end
diff --git a/Firebase/Database/Api/FIRDatabaseSwiftNameSupport.h b/Firebase/Database/Api/FIRDatabaseSwiftNameSupport.h
new file mode 100644
index 0000000..529adf4
--- /dev/null
+++ b/Firebase/Database/Api/FIRDatabaseSwiftNameSupport.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef FIR_SWIFT_NAME
+
+#import <Foundation/Foundation.h>
+
+// NS_SWIFT_NAME can only translate factory methods before the iOS 9.3 SDK.
+// // Wrap it in our own macro if it's a non-compatible SDK.
+#ifdef __IPHONE_9_3
+#define FIR_SWIFT_NAME(X) NS_SWIFT_NAME(X)
+#else
+#define FIR_SWIFT_NAME(X) // Intentionally blank.
+#endif // #ifdef __IPHONE_9_3
+
+#endif // FIR_SWIFT_NAME \ No newline at end of file
diff --git a/Firebase/Database/Api/FIRMutableData.h b/Firebase/Database/Api/FIRMutableData.h
new file mode 100644
index 0000000..5c26024
--- /dev/null
+++ b/Firebase/Database/Api/FIRMutableData.h
@@ -0,0 +1,130 @@
+/*
+ * 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 <Foundation/Foundation.h>
+#import "FIRDatabaseSwiftNameSupport.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * A FIRMutableData instance is populated with data from a Firebase Database location.
+ * When you are using runTransactionBlock:, you will be given an instance containing the current
+ * data at that location. Your block will be responsible for updating that instance to the data
+ * you wish to save at that location, and then returning using [FIRTransactionResult successWithValue:].
+ *
+ * To modify the data, set its value property to any of the native types support by Firebase Database:
+ *
+ * + NSNumber (includes BOOL)
+ * + NSDictionary
+ * + NSArray
+ * + NSString
+ * + nil / NSNull to remove the data
+ *
+ * Note that changes made to a child FIRMutableData instance will be visible to the parent.
+ */
+FIR_SWIFT_NAME(MutableData)
+@interface FIRMutableData : NSObject
+
+
+#pragma mark - Inspecting and navigating the data
+
+
+/**
+ * Returns boolean indicating whether this mutable data has children.
+ *
+ * @return YES if this data contains child nodes.
+ */
+- (BOOL) hasChildren;
+
+
+/**
+ * Indicates whether this mutable data has a child at the given path.
+ *
+ * @param path A path string, consisting either of a single segment, like 'child', or multiple segments, 'a/deeper/child'
+ * @return YES if this data contains a child at the specified relative path
+ */
+- (BOOL) hasChildAtPath:(NSString *)path;
+
+
+/**
+ * Used to obtain a FIRMutableData instance that encapsulates the data at the given relative path.
+ * Note that changes made to the child will be visible to the parent.
+ *
+ * @param path A path string, consisting either of a single segment, like 'child', or multiple segments, 'a/deeper/child'
+ * @return A FIRMutableData instance containing the data at the given path
+ */
+- (FIRMutableData *)childDataByAppendingPath:(NSString *)path;
+
+
+#pragma mark - Properties
+
+
+/**
+ * To modify the data contained by this instance of FIRMutableData, set this to any of the native types supported by Firebase Database:
+ *
+ * + NSNumber (includes BOOL)
+ * + NSDictionary
+ * + NSArray
+ * + NSString
+ * + nil / NSNull to remove the data
+ *
+ * Note that setting this value will override the priority at this location.
+ *
+ * @return The current data at this location as a native object
+ */
+@property (strong, nonatomic, nullable) id value;
+
+
+/**
+ * Set this property to update the priority of the data at this location. Can be set to the following types:
+ *
+ * + NSNumber
+ * + NSString
+ * + nil / NSNull to remove the priority
+ *
+ * @return The priority of the data at this location
+ */
+@property (strong, nonatomic, nullable) id priority;
+
+
+/**
+ * @return The number of child nodes at this location
+ */
+@property (readonly, nonatomic) NSUInteger childrenCount;
+
+
+/**
+ * Used to iterate over the children at this location. You can use the native for .. in syntax:
+ *
+ * for (FIRMutableData* child in data.children) {
+ * ...
+ * }
+ *
+ * Note that this enumerator operates on an immutable copy of the child list. So, you can modify the instance
+ * during iteration, but the new additions will not be visible until you get a new enumerator.
+ */
+@property (readonly, nonatomic, strong) NSEnumerator* children;
+
+
+/**
+ * @return The key name of this node, or nil if it is the top-most location
+ */
+@property (readonly, nonatomic, strong, nullable) NSString* key;
+
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Database/Api/FIRMutableData.m b/Firebase/Database/Api/FIRMutableData.m
new file mode 100644
index 0000000..7e10dcd
--- /dev/null
+++ b/Firebase/Database/Api/FIRMutableData.m
@@ -0,0 +1,134 @@
+/*
+ * 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 "FIRMutableData.h"
+#import "FIRMutableData_Private.h"
+#import "FSnapshotHolder.h"
+#import "FSnapshotUtilities.h"
+#import "FChildrenNode.h"
+#import "FTransformedEnumerator.h"
+#import "FNamedNode.h"
+#import "FIndexedNode.h"
+
+@interface FIRMutableData ()
+
+- (id) initWithPrefixPath:(FPath *)path andSnapshotHolder:(FSnapshotHolder *)snapshotHolder;
+
+@property (strong, nonatomic) FSnapshotHolder* data;
+@property (strong, nonatomic) FPath* prefixPath;
+
+@end
+
+@implementation FIRMutableData
+
+@synthesize data;
+@synthesize prefixPath;
+
+- (id) initWithNode:(id<FNode>)node {
+ FSnapshotHolder* holder = [[FSnapshotHolder alloc] init];
+ FPath* path = [FPath empty];
+ [holder updateSnapshot:path withNewSnapshot:node];
+ return [self initWithPrefixPath:path andSnapshotHolder:holder];
+}
+
+- (id) initWithPrefixPath:(FPath *)path andSnapshotHolder:(FSnapshotHolder *)snapshotHolder {
+ self = [super init];
+ if (self) {
+ self.prefixPath = path;
+ self.data = snapshotHolder;
+ }
+ return self;
+}
+
+- (FIRMutableData *)childDataByAppendingPath:(NSString *)path {
+ FPath* wholePath = [self.prefixPath childFromString:path];
+ return [[FIRMutableData alloc] initWithPrefixPath:wholePath andSnapshotHolder:self.data];
+}
+
+- (FIRMutableData *) parent {
+ if ([self.prefixPath isEmpty]) {
+ return nil;
+ } else {
+ FPath* path = [self.prefixPath parent];
+ return [[FIRMutableData alloc] initWithPrefixPath:path andSnapshotHolder:self.data];
+ }
+}
+
+- (void) setValue:(id)aValue {
+ id<FNode> node = [FSnapshotUtilities nodeFrom:aValue withValidationFrom:@"setValue:"];
+ [self.data updateSnapshot:self.prefixPath withNewSnapshot:node];
+}
+
+- (void) setPriority:(id)aPriority {
+ id<FNode> node = [self.data getNode:self.prefixPath];
+ id<FNode> pri = [FSnapshotUtilities nodeFrom:aPriority];
+ node = [node updatePriority:pri];
+ [self.data updateSnapshot:self.prefixPath withNewSnapshot:node];
+}
+
+- (id) value {
+ return [[self.data getNode:self.prefixPath] val];
+}
+
+- (id) priority {
+ return [[[self.data getNode:self.prefixPath] getPriority] val];
+}
+
+- (BOOL) hasChildren {
+ id<FNode> node = [self.data getNode:self.prefixPath];
+ return ![node isLeafNode] && ![(FChildrenNode*)node isEmpty];
+}
+
+- (BOOL) hasChildAtPath:(NSString *)path {
+ id<FNode> node = [self.data getNode:self.prefixPath];
+ FPath* childPath = [[FPath alloc] initWith:path];
+ return ![[node getChild:childPath] isEmpty];
+}
+
+- (NSUInteger) childrenCount {
+ return [[self.data getNode:self.prefixPath] numChildren];
+}
+
+- (NSString *) key {
+ return [self.prefixPath getBack];
+}
+
+- (id<FNode>) nodeValue {
+ return [self.data getNode:self.prefixPath];
+}
+
+- (NSEnumerator *) children {
+ FIndexedNode *indexedNode = [FIndexedNode indexedNodeWithNode:self.nodeValue];
+ return [[FTransformedEnumerator alloc] initWithEnumerator:[indexedNode childEnumerator] andTransform:^id(FNamedNode *node) {
+ FPath* childPath = [self.prefixPath childFromString:node.name];
+ FIRMutableData * childData = [[FIRMutableData alloc] initWithPrefixPath:childPath andSnapshotHolder:self.data];
+ return childData;
+ }];
+}
+
+- (BOOL) isEqualToData:(FIRMutableData *)other {
+ return self.data == other.data && [[self.prefixPath description] isEqualToString:[other.prefixPath description]];
+}
+
+- (NSString *) description {
+ if (self.key == nil) {
+ return [NSString stringWithFormat:@"FIRMutableData (top-most transaction) %@ %@", self.key, self.value];
+ } else {
+ return [NSString stringWithFormat:@"FIRMutableData (%@) %@", self.key, self.value];
+ }
+}
+
+@end
diff --git a/Firebase/Database/Api/FIRServerValue.h b/Firebase/Database/Api/FIRServerValue.h
new file mode 100644
index 0000000..f5eadd5
--- /dev/null
+++ b/Firebase/Database/Api/FIRServerValue.h
@@ -0,0 +1,35 @@
+/*
+ * 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 "FIRDatabaseSwiftNameSupport.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Placeholder values you may write into Firebase Database as a value or priority
+ * that will automatically be populated by the Firebase Database server.
+ */
+FIR_SWIFT_NAME(ServerValue)
+@interface FIRServerValue : NSObject
+
+/**
+ * Placeholder value for the number of milliseconds since the Unix epoch
+ */
++ (NSDictionary *) timestamp;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Database/Api/FIRServerValue.m b/Firebase/Database/Api/FIRServerValue.m
new file mode 100644
index 0000000..14bb745
--- /dev/null
+++ b/Firebase/Database/Api/FIRServerValue.m
@@ -0,0 +1,30 @@
+/*
+ * 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 "FIRDatabaseReference.h"
+#import "FIRServerValue.h"
+
+@implementation FIRServerValue
+
++ (NSDictionary *) timestamp {
+ static NSDictionary *timestamp = nil;
+ if (timestamp == nil) {
+ timestamp = @{ @".sv": @"timestamp" };
+ }
+ return timestamp;
+}
+
+@end
diff --git a/Firebase/Database/Api/FIRTransactionResult.h b/Firebase/Database/Api/FIRTransactionResult.h
new file mode 100644
index 0000000..3c2d39a
--- /dev/null
+++ b/Firebase/Database/Api/FIRTransactionResult.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <Foundation/Foundation.h>
+#import "FIRDatabaseSwiftNameSupport.h"
+#import "FIRMutableData.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Used for runTransactionBlock:. An FIRTransactionResult instance is a container for the results of the transaction.
+ */
+FIR_SWIFT_NAME(TransactionResult)
+@interface FIRTransactionResult : NSObject
+
+/**
+ * Used for runTransactionBlock:. Indicates that the new value should be saved at this location
+ *
+ * @param value A FIRMutableData instance containing the new value to be set
+ * @return An FIRTransactionResult instance that can be used as a return value from the block given to runTransactionBlock:
+ */
++ (FIRTransactionResult *)successWithValue:(FIRMutableData *)value;
+
+
+/**
+ * Used for runTransactionBlock:. Indicates that the current transaction should no longer proceed.
+ *
+ * @return An FIRTransactionResult instance that can be used as a return value from the block given to runTransactionBlock:
+ */
++ (FIRTransactionResult *) abort;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Firebase/Database/Api/FIRTransactionResult.m b/Firebase/Database/Api/FIRTransactionResult.m
new file mode 100644
index 0000000..8afc5b7
--- /dev/null
+++ b/Firebase/Database/Api/FIRTransactionResult.m
@@ -0,0 +1,39 @@
+/*
+ * 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 "FIRTransactionResult.h"
+#import "FIRTransactionResult_Private.h"
+
+@implementation FIRTransactionResult
+
+@synthesize update;
+@synthesize isSuccess;
+
++ (FIRTransactionResult *)successWithValue:(FIRMutableData *)value {
+ FIRTransactionResult * result = [[FIRTransactionResult alloc] init];
+ result.isSuccess = YES;
+ result.update = value;
+ return result;
+}
+
++ (FIRTransactionResult *) abort {
+ FIRTransactionResult * result = [[FIRTransactionResult alloc] init];
+ result.isSuccess = NO;
+ result.update = nil;
+ return result;
+}
+
+@end
diff --git a/Firebase/Database/Api/FirebaseDatabase.h b/Firebase/Database/Api/FirebaseDatabase.h
new file mode 100644
index 0000000..e52f5d6
--- /dev/null
+++ b/Firebase/Database/Api/FirebaseDatabase.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#ifndef FirebaseDatabase_h
+#define FirebaseDatabase_h
+
+#import "FIRDatabase.h"
+#import "FIRDatabaseQuery.h"
+#import "FIRDatabaseReference.h"
+#import "FIRDataEventType.h"
+#import "FIRDataSnapshot.h"
+#import "FIRMutableData.h"
+#import "FIRServerValue.h"
+#import "FIRTransactionResult.h"
+
+#endif /* FirebaseDatabase_h */
diff --git a/Firebase/Database/Api/Private/FIRDataSnapshot_Private.h b/Firebase/Database/Api/Private/FIRDataSnapshot_Private.h
new file mode 100644
index 0000000..4ff285b
--- /dev/null
+++ b/Firebase/Database/Api/Private/FIRDataSnapshot_Private.h
@@ -0,0 +1,27 @@
+/*
+ * 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 "FIndexedNode.h"
+#import "FTypedefs_Private.h"
+
+@interface FIRDataSnapshot ()
+
+// in _Private for testing purposes
+@property (nonatomic, strong) FIndexedNode *node;
+
+- (id)initWithRef:(FIRDatabaseReference *)ref indexedNode:(FIndexedNode *)node;
+
+@end
diff --git a/Firebase/Database/Api/Private/FIRDatabaseQuery_Private.h b/Firebase/Database/Api/Private/FIRDatabaseQuery_Private.h
new file mode 100644
index 0000000..3a10fe3
--- /dev/null
+++ b/Firebase/Database/Api/Private/FIRDatabaseQuery_Private.h
@@ -0,0 +1,43 @@
+/*
+ * 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 "FRepo.h"
+#import "FPath.h"
+#import "FRepoManager.h"
+#import "FTypedefs_Private.h"
+#import "FQueryParams.h"
+#import "FIRDatabaseQuery.h"
+
+@interface FIRDatabaseQuery ()
+
++ (dispatch_queue_t)sharedQueue;
+
+- (id) initWithRepo:(FRepo *)repo path:(FPath *)path;
+- (id) initWithRepo:(FRepo *)repo
+ path:(FPath *)path
+ params:(FQueryParams *)params
+ orderByCalled:(BOOL)orderByCalled
+priorityMethodCalled:(BOOL)priorityMethodCalled;
+
+@property (nonatomic, strong) FRepo* repo;
+@property (nonatomic, strong) FPath* path;
+@property (nonatomic, strong) FQueryParams *queryParams;
+@property (nonatomic) BOOL orderByCalled;
+@property (nonatomic) BOOL priorityMethodCalled;
+
+- (FQuerySpec *)querySpec;
+
+@end
diff --git a/Firebase/Database/Api/Private/FIRDatabaseReference_Private.h b/Firebase/Database/Api/Private/FIRDatabaseReference_Private.h
new file mode 100644
index 0000000..cb28feb
--- /dev/null
+++ b/Firebase/Database/Api/Private/FIRDatabaseReference_Private.h
@@ -0,0 +1,29 @@
+/*
+ * 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 "FIRDatabaseReference.h"
+#import "FTypedefs_Private.h"
+#import "FIRDatabaseConfig.h"
+#import "FRepo.h"
+
+@interface FIRDatabaseReference ()
+
+- (id)initWithConfig:(FIRDatabaseConfig *)config;
+- (id)initWithRepo:(FRepo *)repo path:(FPath *)path;
+
+// TODO: Update tests to not use this.
++ (FIRDatabaseConfig *)defaultConfig;
+@end
diff --git a/Firebase/Database/Api/Private/FIRDatabase_Private.h b/Firebase/Database/Api/Private/FIRDatabase_Private.h
new file mode 100644
index 0000000..5b7f8cc
--- /dev/null
+++ b/Firebase/Database/Api/Private/FIRDatabase_Private.h
@@ -0,0 +1,28 @@
+/*
+ * 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 "FIRDatabase.h"
+
+@class FRepo;
+@class FRepoInfo;
+@class FIRDatabaseConfig;
+
+@interface FIRDatabase ()
+
++ (NSString *) buildVersion;
++ (FIRDatabase *) createDatabaseForTests:(FRepoInfo *)repoInfo config:(FIRDatabaseConfig *)config;
+
+@end
diff --git a/Firebase/Database/Api/Private/FIRMutableData_Private.h b/Firebase/Database/Api/Private/FIRMutableData_Private.h
new file mode 100644
index 0000000..ee3aa96
--- /dev/null
+++ b/Firebase/Database/Api/Private/FIRMutableData_Private.h
@@ -0,0 +1,26 @@
+/*
+ * 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 "FIRMutableData.h"
+#import "FNode.h"
+
+@interface FIRMutableData ()
+
+- (id) initWithNode:(id<FNode>)node;
+- (id<FNode>) nodeValue;
+- (BOOL) isEqualToData:(FIRMutableData *)other;
+
+@end
diff --git a/Firebase/Database/Api/Private/FIRTransactionResult_Private.h b/Firebase/Database/Api/Private/FIRTransactionResult_Private.h
new file mode 100644
index 0000000..82290f2
--- /dev/null
+++ b/Firebase/Database/Api/Private/FIRTransactionResult_Private.h
@@ -0,0 +1,25 @@
+/*
+ * 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 "FIRTransactionResult.h"
+#import "FIRMutableData.h"
+
+@interface FIRTransactionResult ()
+
+@property (nonatomic) BOOL isSuccess;
+@property (nonatomic, strong) FIRMutableData * update;
+
+@end
diff --git a/Firebase/Database/Api/Private/FTypedefs_Private.h b/Firebase/Database/Api/Private/FTypedefs_Private.h
new file mode 100644
index 0000000..73f4c9a
--- /dev/null
+++ b/Firebase/Database/Api/Private/FTypedefs_Private.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef __FTYPEDEFS_PRIVATE__
+#define __FTYPEDEFS_PRIVATE__
+
+#import <Foundation/Foundation.h>
+
+typedef NS_ENUM(NSInteger, FTransactionStatus) {
+ FTransactionInitializing, // 0
+ FTransactionRun, // 1
+ FTransactionSent, // 2
+ FTransactionCompleted, // 3
+ FTransactionSentNeedsAbort, // 4
+ FTransactionNeedsAbort // 5
+};
+
+@protocol FNode;
+@class FPath;
+@class FIRTransactionResult;
+@class FIRMutableData;
+@class FIRDataSnapshot;
+@class FCompoundHash;
+
+typedef void (^fbt_void_nserror_bool_datasnapshot) (NSError* error, BOOL committed, FIRDataSnapshot * snapshot);
+typedef FIRTransactionResult * (^fbt_transactionresult_mutabledata) (FIRMutableData * currentData);
+typedef void (^fbt_void_path_node) (FPath*, id<FNode>);
+typedef void (^fbt_void_nsstring) (NSString *);
+typedef BOOL (^fbt_bool_nsstring_node) (NSString *, id<FNode>);
+typedef void (^fbt_void_path_node_marray) (FPath *, id<FNode>, NSMutableArray *);
+typedef BOOL (^fbt_bool_void) (void);
+typedef void (^fbt_void_nsstring_nsstring)(NSString *str1, NSString* str2);
+typedef void (^fbt_void_nsstring_nserror)(NSString *str, NSError* error);
+typedef BOOL (^fbt_bool_path)(FPath *str);
+typedef void (^fbt_void_id)(id data);
+typedef NSString* (^fbt_nsstring_void) (void);
+typedef FCompoundHash* (^fbt_compoundhash_void) (void);
+typedef NSArray* (^fbt_nsarray_nsstring_id)(NSString *status, id Data);
+typedef NSArray* (^fbt_nsarray_nsstring)(NSString *status);
+
+// WWDC 2012 session 712 starting in page 83 for saving blocks in properties (use @property (strong) type name).
+
+#endif