adding pods method of package managing
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* 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 FIRAppCheckTokenResultInterop;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
NS_SWIFT_NAME(AppCheckTokenHandlerInterop)
|
||||
typedef void (^FIRAppCheckTokenHandlerInterop)(id<FIRAppCheckTokenResultInterop> tokenResult);
|
||||
|
||||
@protocol FIRAppCheckInterop <NSObject>
|
||||
|
||||
/// Retrieve a cached or generate a new FAA Token. If forcingRefresh == YES always generates a new
|
||||
/// token and updates the cache.
|
||||
- (void)getTokenForcingRefresh:(BOOL)forcingRefresh
|
||||
completion:(FIRAppCheckTokenHandlerInterop)handler
|
||||
NS_SWIFT_NAME(getToken(forcingRefresh:completion:));
|
||||
|
||||
/// A notification with the specified name is sent to the default notification center
|
||||
/// (`NotificationCenter.default`) each time a Firebase app check token is refreshed.
|
||||
/// The user info dictionary contains `-[self notificationTokenKey]` and
|
||||
/// `-[self notificationAppNameKey]` keys.
|
||||
- (NSString *)tokenDidChangeNotificationName;
|
||||
|
||||
/// `userInfo` key for the FAC token in a notification for `tokenDidChangeNotificationName`.
|
||||
- (NSString *)notificationTokenKey;
|
||||
/// `userInfo` key for the `FirebaseApp.name` in a notification for
|
||||
/// `tokenDidChangeNotificationName`.
|
||||
- (NSString *)notificationAppNameKey;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol FIRAppCheckTokenResultInterop <NSObject>
|
||||
|
||||
/// App Check token in the case of success or a dummy token in the case of a failure.
|
||||
/// In general, the value of the token should always be set to the request header.
|
||||
@property(nonatomic, readonly) NSString *token;
|
||||
|
||||
/// A token fetch error in the case of a failure or `nil` in the case of success.
|
||||
@property(nonatomic, readonly, nullable) NSError *error;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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 <FirebaseCore/FIRApp.h>
|
||||
|
||||
@class FIRComponentContainer;
|
||||
@protocol FIRLibrary;
|
||||
|
||||
/**
|
||||
* The internal interface to FIRApp. This is meant for first-party integrators, who need to receive
|
||||
* FIRApp notifications, log info about the success or failure of their configuration, and access
|
||||
* other internal functionality of FIRApp.
|
||||
*
|
||||
* TODO(b/28296561): Restructure this header.
|
||||
*/
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, FIRConfigType) {
|
||||
FIRConfigTypeCore = 1,
|
||||
FIRConfigTypeSDK = 2,
|
||||
};
|
||||
|
||||
extern NSString *const kFIRDefaultAppName;
|
||||
extern NSString *const kFIRAppReadyToConfigureSDKNotification;
|
||||
extern NSString *const kFIRAppDeleteNotification;
|
||||
extern NSString *const kFIRAppIsDefaultAppKey;
|
||||
extern NSString *const kFIRAppNameKey;
|
||||
extern NSString *const kFIRGoogleAppIDKey;
|
||||
extern NSString *const kFirebaseCoreErrorDomain;
|
||||
|
||||
/** The NSUserDefaults suite name for FirebaseCore, for those storage locations that use it. */
|
||||
extern NSString *const kFirebaseCoreDefaultsSuiteName;
|
||||
|
||||
/**
|
||||
* The format string for the User Defaults key used for storing the data collection enabled flag.
|
||||
* This includes formatting to append the Firebase App's name.
|
||||
*/
|
||||
extern NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat;
|
||||
|
||||
/**
|
||||
* The plist key used for storing the data collection enabled flag.
|
||||
*/
|
||||
extern NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey;
|
||||
|
||||
/** @var FIRAuthStateDidChangeInternalNotification
|
||||
@brief The name of the @c NSNotificationCenter notification which is posted when the auth state
|
||||
changes (e.g. a new token has been produced, a user logs in or out). The object parameter of
|
||||
the notification is a dictionary possibly containing the key:
|
||||
@c FIRAuthStateDidChangeInternalNotificationTokenKey (the new access token.) If it does not
|
||||
contain this key it indicates a sign-out event took place.
|
||||
*/
|
||||
extern NSString *const FIRAuthStateDidChangeInternalNotification;
|
||||
|
||||
/** @var FIRAuthStateDidChangeInternalNotificationTokenKey
|
||||
@brief A key present in the dictionary object parameter of the
|
||||
@c FIRAuthStateDidChangeInternalNotification notification. The value associated with this
|
||||
key will contain the new access token.
|
||||
*/
|
||||
extern NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey;
|
||||
|
||||
/** @var FIRAuthStateDidChangeInternalNotificationAppKey
|
||||
@brief A key present in the dictionary object parameter of the
|
||||
@c FIRAuthStateDidChangeInternalNotification notification. The value associated with this
|
||||
key will contain the FIRApp associated with the auth instance.
|
||||
*/
|
||||
extern NSString *const FIRAuthStateDidChangeInternalNotificationAppKey;
|
||||
|
||||
/** @var FIRAuthStateDidChangeInternalNotificationUIDKey
|
||||
@brief A key present in the dictionary object parameter of the
|
||||
@c FIRAuthStateDidChangeInternalNotification notification. The value associated with this
|
||||
key will contain the new user's UID (or nil if there is no longer a user signed in).
|
||||
*/
|
||||
extern NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey;
|
||||
|
||||
@interface FIRApp ()
|
||||
|
||||
/**
|
||||
* A flag indicating if this is the default app (has the default app name).
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL isDefaultApp;
|
||||
|
||||
/*
|
||||
* The container of interop SDKs for this app.
|
||||
*/
|
||||
@property(nonatomic) FIRComponentContainer *container;
|
||||
|
||||
/**
|
||||
* Checks if the default app is configured without trying to configure it.
|
||||
*/
|
||||
+ (BOOL)isDefaultAppConfigured;
|
||||
|
||||
/**
|
||||
* Registers a given third-party library with the given version number to be reported for
|
||||
* analytics.
|
||||
*
|
||||
* @param name Name of the library.
|
||||
* @param version Version of the library.
|
||||
*/
|
||||
+ (void)registerLibrary:(nonnull NSString *)name withVersion:(nonnull NSString *)version;
|
||||
|
||||
/**
|
||||
* Registers a given internal library to be reported for analytics.
|
||||
*
|
||||
* @param library Optional parameter for component registration.
|
||||
* @param name Name of the library.
|
||||
*/
|
||||
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
|
||||
withName:(nonnull NSString *)name;
|
||||
|
||||
/**
|
||||
* Registers a given internal library with the given version number to be reported for
|
||||
* analytics. This should only be used for non-Firebase libraries that have their own versioning
|
||||
* scheme.
|
||||
*
|
||||
* @param library Optional parameter for component registration.
|
||||
* @param name Name of the library.
|
||||
* @param version Version of the library.
|
||||
*/
|
||||
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
|
||||
withName:(nonnull NSString *)name
|
||||
withVersion:(nonnull NSString *)version;
|
||||
|
||||
/**
|
||||
* A concatenated string representing all the third-party libraries and version numbers.
|
||||
*/
|
||||
+ (NSString *)firebaseUserAgent;
|
||||
|
||||
/**
|
||||
* Can be used by the unit tests in eack SDK to reset FIRApp. This method is thread unsafe.
|
||||
*/
|
||||
+ (void)resetApps;
|
||||
|
||||
/**
|
||||
* Can be used by the unit tests in each SDK to set customized options.
|
||||
*/
|
||||
- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2018 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>
|
||||
|
||||
@class FIRApp;
|
||||
@class FIRComponentContainer;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Provides a system to clean up cached instances returned from the component system.
|
||||
NS_SWIFT_NAME(ComponentLifecycleMaintainer)
|
||||
@protocol FIRComponentLifecycleMaintainer
|
||||
/// The associated app will be deleted, clean up any resources as they are about to be deallocated.
|
||||
- (void)appWillBeDeleted:(FIRApp *)app;
|
||||
@end
|
||||
|
||||
typedef _Nullable id (^FIRComponentCreationBlock)(FIRComponentContainer *container,
|
||||
BOOL *isCacheable)
|
||||
NS_SWIFT_NAME(ComponentCreationBlock);
|
||||
|
||||
@class FIRDependency;
|
||||
|
||||
/// Describes the timing of instantiation. Note: new components should default to lazy unless there
|
||||
/// is a strong reason to be eager.
|
||||
typedef NS_ENUM(NSInteger, FIRInstantiationTiming) {
|
||||
FIRInstantiationTimingLazy,
|
||||
FIRInstantiationTimingAlwaysEager,
|
||||
FIRInstantiationTimingEagerInDefaultApp
|
||||
} NS_SWIFT_NAME(InstantiationTiming);
|
||||
|
||||
/// A component that can be used from other Firebase SDKs.
|
||||
NS_SWIFT_NAME(Component)
|
||||
@interface FIRComponent : NSObject
|
||||
|
||||
/// The protocol describing functionality provided from the Component.
|
||||
@property(nonatomic, strong, readonly) Protocol *protocol;
|
||||
|
||||
/// The timing of instantiation.
|
||||
@property(nonatomic, readonly) FIRInstantiationTiming instantiationTiming;
|
||||
|
||||
/// An array of dependencies for the component.
|
||||
@property(nonatomic, copy, readonly) NSArray<FIRDependency *> *dependencies;
|
||||
|
||||
/// A block to instantiate an instance of the component with the appropriate dependencies.
|
||||
@property(nonatomic, copy, readonly) FIRComponentCreationBlock creationBlock;
|
||||
|
||||
// There's an issue with long NS_SWIFT_NAMES that causes compilation to fail, disable clang-format
|
||||
// for the next two methods.
|
||||
// clang-format off
|
||||
|
||||
/// Creates a component with no dependencies that will be lazily initialized.
|
||||
+ (instancetype)componentWithProtocol:(Protocol *)protocol
|
||||
creationBlock:(FIRComponentCreationBlock)creationBlock
|
||||
NS_SWIFT_NAME(init(_:creationBlock:));
|
||||
|
||||
/// Creates a component to be registered with the component container.
|
||||
///
|
||||
/// @param protocol - The protocol describing functionality provided by the component.
|
||||
/// @param instantiationTiming - When the component should be initialized. Use .lazy unless there's
|
||||
/// a good reason to be instantiated earlier.
|
||||
/// @param dependencies - Any dependencies the `implementingClass` has, optional or required.
|
||||
/// @param creationBlock - A block to instantiate the component with a container, and if
|
||||
/// @return A component that can be registered with the component container.
|
||||
+ (instancetype)componentWithProtocol:(Protocol *)protocol
|
||||
instantiationTiming:(FIRInstantiationTiming)instantiationTiming
|
||||
dependencies:(NSArray<FIRDependency *> *)dependencies
|
||||
creationBlock:(FIRComponentCreationBlock)creationBlock
|
||||
NS_SWIFT_NAME(init(_:instantiationTiming:dependencies:creationBlock:));
|
||||
|
||||
// clang-format on
|
||||
|
||||
/// Unavailable.
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2018 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// A type-safe macro to retrieve a component from a container. This should be used to retrieve
|
||||
/// components instead of using the container directly.
|
||||
#define FIR_COMPONENT(type, container) \
|
||||
[FIRComponentType<id<type>> instanceForProtocol:@protocol(type) inContainer:container]
|
||||
|
||||
@class FIRApp;
|
||||
|
||||
/// A container that holds different components that are registered via the
|
||||
/// `registerAsComponentRegistrant:` call. These classes should conform to `FIRComponentRegistrant`
|
||||
/// in order to properly register components for Core.
|
||||
NS_SWIFT_NAME(FirebaseComponentContainer)
|
||||
@interface FIRComponentContainer : NSObject
|
||||
|
||||
/// A weak reference to the app that an instance of the container belongs to.
|
||||
@property(nonatomic, weak, readonly) FIRApp *app;
|
||||
|
||||
/// Unavailable. Use the `container` property on `FIRApp`.
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2018 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>
|
||||
|
||||
@class FIRComponentContainer;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Do not use directly. A placeholder type in order to provide a macro that will warn users of
|
||||
/// mis-matched protocols.
|
||||
NS_SWIFT_NAME(ComponentType)
|
||||
@interface FIRComponentType<__covariant T> : NSObject
|
||||
|
||||
/// Do not use directly. A factory method to retrieve an instance that provides a specific
|
||||
/// functionality.
|
||||
+ (T)instanceForProtocol:(Protocol *)protocol inContainer:(FIRComponentContainer *)container;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2019 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>
|
||||
|
||||
@class FIRDiagnosticsData;
|
||||
@class FIROptions;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** Connects FIRCore with the CoreDiagnostics library. */
|
||||
@interface FIRCoreDiagnosticsConnector : NSObject
|
||||
|
||||
/** Logs FirebaseCore related data.
|
||||
*
|
||||
* @param options The options object containing data to log.
|
||||
*/
|
||||
+ (void)logCoreTelemetryWithOptions:(FIROptions *)options;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2018 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// A dependency on a specific protocol's functionality.
|
||||
NS_SWIFT_NAME(Dependency)
|
||||
@interface FIRDependency : NSObject
|
||||
|
||||
/// The protocol describing functionality being depended on.
|
||||
@property(nonatomic, strong, readonly) Protocol *protocol;
|
||||
|
||||
/// A flag to specify if the dependency is required or not.
|
||||
@property(nonatomic, readonly) BOOL isRequired;
|
||||
|
||||
/// Initializes a dependency that is required. Calls `initWithProtocol:isRequired` with `YES` for
|
||||
/// the required parameter.
|
||||
/// Creates a required dependency on the specified protocol's functionality.
|
||||
+ (instancetype)dependencyWithProtocol:(Protocol *)protocol;
|
||||
|
||||
/// Creates a dependency on the specified protocol's functionality and specify if it's required for
|
||||
/// the class's functionality.
|
||||
+ (instancetype)dependencyWithProtocol:(Protocol *)protocol isRequired:(BOOL)required;
|
||||
|
||||
/// Use `dependencyWithProtocol:isRequired:` instead.
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2019 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FIRHeartbeatInfo : NSObject
|
||||
|
||||
// Enum representing the different heartbeat codes.
|
||||
typedef NS_ENUM(NSInteger, FIRHeartbeatInfoCode) {
|
||||
FIRHeartbeatInfoCodeNone = 0,
|
||||
FIRHeartbeatInfoCodeSDK = 1,
|
||||
FIRHeartbeatInfoCodeGlobal = 2,
|
||||
FIRHeartbeatInfoCodeCombined = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* Get heartbeat code required for the sdk.
|
||||
* @param heartbeatTag String representing the sdk heartbeat tag.
|
||||
* @return Heartbeat code indicating whether or not an sdk/global heartbeat
|
||||
* needs to be sent
|
||||
*/
|
||||
+ (FIRHeartbeatInfoCode)heartbeatCodeForTag:(NSString *)heartbeatTag;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2018 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 FIRLibrary_h
|
||||
#define FIRLibrary_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class FIRApp;
|
||||
@class FIRComponent;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// Provide an interface to register a library for userAgent logging and availability to others.
|
||||
NS_SWIFT_NAME(Library)
|
||||
@protocol FIRLibrary
|
||||
|
||||
/// Returns one or more FIRComponents that will be registered in
|
||||
/// FIRApp and participate in dependency resolution and injection.
|
||||
+ (NSArray<FIRComponent *> *)componentsToRegister;
|
||||
|
||||
@optional
|
||||
/// Implement this method if the library needs notifications for lifecycle events. This method is
|
||||
/// called when the developer calls `FirebaseApp.configure()`.
|
||||
+ (void)configureWithApp:(FIRApp *)app;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#endif /* FIRLibrary_h */
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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 <FirebaseCore/FIRLoggerLevel.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* The Firebase services used in Firebase logger.
|
||||
*/
|
||||
typedef NSString *const FIRLoggerService;
|
||||
|
||||
extern FIRLoggerService kFIRLoggerAnalytics;
|
||||
extern FIRLoggerService kFIRLoggerCrash;
|
||||
extern FIRLoggerService kFIRLoggerCore;
|
||||
extern FIRLoggerService kFIRLoggerRemoteConfig;
|
||||
|
||||
/**
|
||||
* The key used to store the logger's error count.
|
||||
*/
|
||||
extern NSString *const kFIRLoggerErrorCountKey;
|
||||
|
||||
/**
|
||||
* The key used to store the logger's warning count.
|
||||
*/
|
||||
extern NSString *const kFIRLoggerWarningCountKey;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
/**
|
||||
* Enables or disables Analytics debug mode.
|
||||
* If set to YES, the logging level for Analytics will be set to FIRLoggerLevelDebug.
|
||||
* Enabling the debug mode has no effect if the app is running from App Store.
|
||||
* (required) analytics debug mode flag.
|
||||
*/
|
||||
void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode);
|
||||
|
||||
/**
|
||||
* Changes the default logging level of FIRLoggerLevelNotice to a user-specified level.
|
||||
* The default level cannot be set above FIRLoggerLevelNotice if the app is running from App Store.
|
||||
* (required) log level (one of the FIRLoggerLevel enum values).
|
||||
*/
|
||||
void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel);
|
||||
|
||||
/**
|
||||
* Checks if the specified logger level is loggable given the current settings.
|
||||
* (required) log level (one of the FIRLoggerLevel enum values).
|
||||
* (required) whether or not this function is called from the Analytics component.
|
||||
*/
|
||||
BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel, BOOL analyticsComponent);
|
||||
|
||||
/**
|
||||
* Logs a message to the Xcode console and the device log. If running from AppStore, will
|
||||
* not log any messages with a level higher than FIRLoggerLevelNotice to avoid log spamming.
|
||||
* (required) log level (one of the FIRLoggerLevel enum values).
|
||||
* (required) service name of type FIRLoggerService.
|
||||
* (required) message code starting with "I-" which means iOS, followed by a capitalized
|
||||
* three-character service identifier and a six digit integer message ID that is unique
|
||||
* within the service.
|
||||
* An example of the message code is @"I-COR000001".
|
||||
* (required) message string which can be a format string.
|
||||
* (optional) variable arguments list obtained from calling va_start, used when message is a format
|
||||
* string.
|
||||
*/
|
||||
extern void FIRLogBasic(FIRLoggerLevel level,
|
||||
FIRLoggerService service,
|
||||
NSString *messageCode,
|
||||
NSString *message,
|
||||
// On 64-bit simulators, va_list is not a pointer, so cannot be marked nullable
|
||||
// See: http://stackoverflow.com/q/29095469
|
||||
#if __LP64__ && TARGET_OS_SIMULATOR || TARGET_OS_OSX
|
||||
va_list args_ptr
|
||||
#else
|
||||
va_list _Nullable args_ptr
|
||||
#endif
|
||||
);
|
||||
|
||||
/**
|
||||
* The following functions accept the following parameters in order:
|
||||
* (required) service name of type FIRLoggerService.
|
||||
* (required) message code starting from "I-" which means iOS, followed by a capitalized
|
||||
* three-character service identifier and a six digit integer message ID that is unique
|
||||
* within the service.
|
||||
* An example of the message code is @"I-COR000001".
|
||||
* See go/firebase-log-proposal for details.
|
||||
* (required) message string which can be a format string.
|
||||
* (optional) the list of arguments to substitute into the format string.
|
||||
* Example usage:
|
||||
* FIRLogError(kFIRLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name);
|
||||
*/
|
||||
extern void FIRLogError(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
|
||||
NS_FORMAT_FUNCTION(3, 4);
|
||||
extern void FIRLogWarning(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
|
||||
NS_FORMAT_FUNCTION(3, 4);
|
||||
extern void FIRLogNotice(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
|
||||
NS_FORMAT_FUNCTION(3, 4);
|
||||
extern void FIRLogInfo(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
|
||||
NS_FORMAT_FUNCTION(3, 4);
|
||||
extern void FIRLogDebug(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
|
||||
NS_FORMAT_FUNCTION(3, 4);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
@interface FIRLoggerWrapper : NSObject
|
||||
|
||||
/**
|
||||
* Objective-C wrapper for FIRLogBasic to allow weak linking to FIRLogger
|
||||
* (required) log level (one of the FIRLoggerLevel enum values).
|
||||
* (required) service name of type FIRLoggerService.
|
||||
* (required) message code starting with "I-" which means iOS, followed by a capitalized
|
||||
* three-character service identifier and a six digit integer message ID that is unique
|
||||
* within the service.
|
||||
* An example of the message code is @"I-COR000001".
|
||||
* (required) message string which can be a format string.
|
||||
* (optional) variable arguments list obtained from calling va_start, used when message is a format
|
||||
* string.
|
||||
*/
|
||||
|
||||
+ (void)logWithLevel:(FIRLoggerLevel)level
|
||||
withService:(FIRLoggerService)service
|
||||
withCode:(NSString *)messageCode
|
||||
withMessage:(NSString *)message
|
||||
withArgs:(va_list)args;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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 <FirebaseCore/FIROptions.h>
|
||||
|
||||
/**
|
||||
* Keys for the strings in the plist file.
|
||||
*/
|
||||
extern NSString *const kFIRAPIKey;
|
||||
extern NSString *const kFIRTrackingID;
|
||||
extern NSString *const kFIRGoogleAppID;
|
||||
extern NSString *const kFIRClientID;
|
||||
extern NSString *const kFIRGCMSenderID;
|
||||
extern NSString *const kFIRAndroidClientID;
|
||||
extern NSString *const kFIRDatabaseURL;
|
||||
extern NSString *const kFIRStorageBucket;
|
||||
extern NSString *const kFIRBundleID;
|
||||
extern NSString *const kFIRProjectID;
|
||||
|
||||
/**
|
||||
* Keys for the plist file name
|
||||
*/
|
||||
extern NSString *const kServiceInfoFileName;
|
||||
|
||||
extern NSString *const kServiceInfoFileType;
|
||||
|
||||
/**
|
||||
* This header file exposes the initialization of FIROptions to internal use.
|
||||
*/
|
||||
@interface FIROptions ()
|
||||
|
||||
/**
|
||||
* resetDefaultOptions and initInternalWithOptionsDictionary: are exposed only for unit tests.
|
||||
*/
|
||||
+ (void)resetDefaultOptions;
|
||||
|
||||
/**
|
||||
* Initializes the options with dictionary. The above strings are the keys of the dictionary.
|
||||
* This is the designated initializer.
|
||||
*/
|
||||
- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)serviceInfoDictionary
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* defaultOptions and defaultOptionsDictionary are exposed in order to be used in FIRApp and
|
||||
* other first party services.
|
||||
*/
|
||||
+ (FIROptions *)defaultOptions;
|
||||
|
||||
+ (NSDictionary *)defaultOptionsDictionary;
|
||||
|
||||
/**
|
||||
* Indicates whether or not Analytics collection was explicitly enabled via a plist flag or at
|
||||
* runtime.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL isAnalyticsCollectionExplicitlySet;
|
||||
|
||||
/**
|
||||
* Whether or not Analytics Collection was enabled. Analytics Collection is enabled unless
|
||||
* explicitly disabled in GoogleService-Info.plist.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL isAnalyticsCollectionEnabled;
|
||||
|
||||
/**
|
||||
* Whether or not Analytics Collection was completely disabled. If YES, then
|
||||
* isAnalyticsCollectionEnabled will be NO.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL isAnalyticsCollectionDeactivated;
|
||||
|
||||
/**
|
||||
* The version ID of the client library, e.g. @"1100000".
|
||||
*/
|
||||
@property(nonatomic, readonly, copy) NSString *libraryVersionID;
|
||||
|
||||
/**
|
||||
* The flag indicating whether this object was constructed with the values in the default plist
|
||||
* file.
|
||||
*/
|
||||
@property(nonatomic) BOOL usingOptionsFromDefaultPlist;
|
||||
|
||||
/**
|
||||
* Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in
|
||||
* GoogleService-Info.plist.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL isMeasurementEnabled;
|
||||
|
||||
/**
|
||||
* Whether or not Analytics was enabled in the developer console.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL isAnalyticsEnabled;
|
||||
|
||||
/**
|
||||
* Whether or not SignIn was enabled in the developer console.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL isSignInEnabled;
|
||||
|
||||
/**
|
||||
* Whether or not editing is locked. This should occur after FIROptions has been set on a FIRApp.
|
||||
*/
|
||||
@property(nonatomic, getter=isEditingLocked) BOOL editingLocked;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
// An umbrella header, for any other libraries in this repo to access Firebase Public and Private
|
||||
// headers. Any package manager complexity should be handled here.
|
||||
|
||||
#import <FirebaseCore/FirebaseCore.h>
|
||||
|
||||
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
|
||||
#import "FirebaseCore/Sources/Private/FIRComponent.h"
|
||||
#import "FirebaseCore/Sources/Private/FIRComponentContainer.h"
|
||||
#import "FirebaseCore/Sources/Private/FIRComponentType.h"
|
||||
#import "FirebaseCore/Sources/Private/FIRDependency.h"
|
||||
#import "FirebaseCore/Sources/Private/FIRHeartbeatInfo.h"
|
||||
#import "FirebaseCore/Sources/Private/FIRLibrary.h"
|
||||
#import "FirebaseCore/Sources/Private/FIRLogger.h"
|
||||
#import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDataSnapshot.h"
|
||||
#import "FirebaseDatabase/Sources/Api/Private/FIRDataSnapshot_Private.h"
|
||||
#import "FirebaseDatabase/Sources/FTransformedEnumerator.h"
|
||||
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabaseReference.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FChildrenNode.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FValidation.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<FIRDataSnapshot *> *)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
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* 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 "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
|
||||
#import "Interop/Auth/Public/FIRAuthInterop.h"
|
||||
|
||||
#import "FirebaseDatabase/Sources/Api/FIRDatabaseComponent.h"
|
||||
#import "FirebaseDatabase/Sources/Api/Private/FIRDatabaseQuery_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Api/Private/FIRDatabaseReference_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Api/Private/FIRDatabase_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FRepoInfo.h"
|
||||
#import "FirebaseDatabase/Sources/FIRDatabaseConfig_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabase.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FValidation.h"
|
||||
|
||||
@implementation FIRDatabase
|
||||
|
||||
+ (FIRDatabase *)database {
|
||||
if (![FIRApp isDefaultAppConfigured]) {
|
||||
[NSException
|
||||
raise:@"FIRAppNotConfigured"
|
||||
format:@"The default FirebaseApp instance must be "
|
||||
@"configured before the default Database instance "
|
||||
@"can be initialized. One way to ensure this is to "
|
||||
@"call `FirebaseApp.configure()` in the App Delegate's "
|
||||
@"`application(_:didFinishLaunchingWithOptions:)` "
|
||||
@"(or the `@main` struct's initializer in SwiftUI)."];
|
||||
}
|
||||
return [FIRDatabase databaseForApp:[FIRApp defaultApp]];
|
||||
}
|
||||
|
||||
+ (FIRDatabase *)databaseWithURL:(NSString *)url {
|
||||
FIRApp *app = [FIRApp defaultApp];
|
||||
if (app == nil) {
|
||||
[NSException
|
||||
raise:@"FIRAppNotConfigured"
|
||||
format:
|
||||
@"Failed to get default Firebase Database instance. "
|
||||
@"Must call `[FIRApp configure]` (`FirebaseApp.configure()` in "
|
||||
@"Swift) before using Firebase Database."];
|
||||
}
|
||||
return [FIRDatabase databaseForApp:app URL:url];
|
||||
}
|
||||
|
||||
+ (FIRDatabase *)databaseForApp:(FIRApp *)app {
|
||||
if (app == nil) {
|
||||
[NSException raise:@"InvalidFIRApp"
|
||||
format:@"nil FIRApp instance passed to databaseForApp."];
|
||||
}
|
||||
NSString *url = app.options.databaseURL;
|
||||
if (!url) {
|
||||
if (!app.options.projectID) {
|
||||
[NSException
|
||||
raise:@"MissingProjectId"
|
||||
format:@"Can't determine Firebase Database URL. Be sure to "
|
||||
@"include a Project ID when calling "
|
||||
@"`FirebaseApp.configure()`."];
|
||||
}
|
||||
FFLog(@"I-RDB024002", @"Using default host for project %@",
|
||||
app.options.projectID);
|
||||
url = [NSString
|
||||
stringWithFormat:@"https://%@-default-rtdb.firebaseio.com",
|
||||
app.options.projectID];
|
||||
}
|
||||
return [FIRDatabase databaseForApp:app URL:url];
|
||||
}
|
||||
|
||||
+ (FIRDatabase *)databaseForApp:(FIRApp *)app URL:(NSString *)url {
|
||||
if (app == nil) {
|
||||
[NSException raise:@"InvalidFIRApp"
|
||||
format:@"nil FIRApp instance passed to databaseForApp."];
|
||||
}
|
||||
if (url == nil) {
|
||||
[NSException raise:@"MissingDatabaseURL"
|
||||
format:@"Failed to get FirebaseDatabase instance: "
|
||||
@"Specify DatabaseURL within FIRApp or from your "
|
||||
@"databaseForApp:URL: call."];
|
||||
}
|
||||
id<FIRDatabaseProvider> provider =
|
||||
FIR_COMPONENT(FIRDatabaseProvider, app.container);
|
||||
return [provider databaseForApp:app URL:url];
|
||||
}
|
||||
|
||||
+ (NSString *)buildVersion {
|
||||
// TODO: Restore git hash when build moves back to git
|
||||
return [NSString stringWithFormat:@"%@_%s", FIRFirebaseVersion(), __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 FIRFirebaseVersion();
|
||||
}
|
||||
|
||||
+ (void)setLoggingEnabled:(BOOL)enabled {
|
||||
[FUtilities setLoggingEnabled:enabled];
|
||||
FFLog(@"I-RDB024001", @"BUILD Version: %@", [FIRDatabase buildVersion]);
|
||||
}
|
||||
|
||||
- (id)initWithApp:(FIRApp *)app
|
||||
repoInfo:(FRepoInfo *)info
|
||||
config:(FIRDatabaseConfig *)config {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
self->_repoInfo = info;
|
||||
self->_config = config;
|
||||
self->_app = app;
|
||||
}
|
||||
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];
|
||||
|
||||
BOOL isInvalidHost =
|
||||
!parsedUrl.repoInfo.isCustomHost &&
|
||||
![_repoInfo.host isEqualToString:parsedUrl.repoInfo.host];
|
||||
if (isInvalidHost) {
|
||||
[NSException raise:@"InvalidDatabaseURL"
|
||||
format:@"Invalid URL (%@) passed to getReference(). URL "
|
||||
@"was expected to match configured Database URL: %@",
|
||||
databaseUrl, _repoInfo.host];
|
||||
}
|
||||
return [[FIRDatabaseReference alloc] initWithRepo:self.repo
|
||||
path:parsedUrl.path];
|
||||
}
|
||||
|
||||
- (void)purgeOutstandingWrites {
|
||||
[self ensureRepo];
|
||||
|
||||
dispatch_async([FIRDatabaseQuery sharedQueue], ^{
|
||||
[self.repo purgeOutstandingWrites];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)useEmulatorWithHost:(NSString *)host port:(NSInteger)port {
|
||||
if (host.length == 0) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Cannot connect to nil or empty host."];
|
||||
}
|
||||
if (self.repo != nil) {
|
||||
[NSException
|
||||
raise:NSInternalInconsistencyException
|
||||
format:@"Cannot connect to emulator after database initialization. "
|
||||
@"Call useEmulator(host:port:) before creating a database "
|
||||
@"reference or trying to load data."];
|
||||
}
|
||||
NSString *fullHost =
|
||||
[NSString stringWithFormat:@"%@:%li", host, (long)port];
|
||||
FRepoInfo *emulatorInfo = [[FRepoInfo alloc] initWithInfo:self.repoInfo
|
||||
emulatedHost:fullHost];
|
||||
self->_repoInfo = emulatorInfo;
|
||||
}
|
||||
|
||||
- (void)goOnline {
|
||||
[self ensureRepo];
|
||||
|
||||
dispatch_async([FIRDatabaseQuery sharedQueue], ^{
|
||||
[self.repo resume];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)goOffline {
|
||||
[self ensureRepo];
|
||||
|
||||
dispatch_async([FIRDatabaseQuery sharedQueue], ^{
|
||||
[self.repo interrupt];
|
||||
});
|
||||
}
|
||||
|
||||
- (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
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2018 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>
|
||||
|
||||
@class FIRApp;
|
||||
@class FIRDatabase;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/// This protocol is used in the interop registration process to register an
|
||||
/// instance provider for individual FIRApps.
|
||||
@protocol FIRDatabaseProvider
|
||||
|
||||
/// Gets a FirebaseDatabase instance for the specified URL, using the specified
|
||||
/// FirebaseApp.
|
||||
- (FIRDatabase *)databaseForApp:(FIRApp *)app URL:(NSString *)url;
|
||||
|
||||
@end
|
||||
|
||||
/// A concrete implementation for FIRDatabaseProvider to create Database
|
||||
/// instances.
|
||||
@interface FIRDatabaseComponent : NSObject <FIRDatabaseProvider>
|
||||
|
||||
/// The FIRApp that instances will be set up with.
|
||||
@property(nonatomic, weak, readonly) FIRApp *app;
|
||||
|
||||
/// Unavailable, use `databaseForApp:URL:` instead.
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2018 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 "FirebaseDatabase/Sources/Api/FIRDatabaseComponent.h"
|
||||
|
||||
#import "FirebaseDatabase/Sources/Api/Private/FIRDatabase_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FRepoManager.h"
|
||||
#import "FirebaseDatabase/Sources/FIRDatabaseConfig_Private.h"
|
||||
|
||||
#import "FirebaseAppCheck/Sources/Interop/FIRAppCheckInterop.h"
|
||||
#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
|
||||
#import "Interop/Auth/Public/FIRAuthInterop.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** A NSMutableDictionary of FirebaseApp name and FRepoInfo to FirebaseDatabase
|
||||
* instance. */
|
||||
typedef NSMutableDictionary<NSString *, FIRDatabase *> FIRDatabaseDictionary;
|
||||
|
||||
@interface FIRDatabaseComponent () <FIRComponentLifecycleMaintainer, FIRLibrary>
|
||||
@property(nonatomic) FIRDatabaseDictionary *instances;
|
||||
/// Internal intializer.
|
||||
- (instancetype)initWithApp:(FIRApp *)app;
|
||||
@end
|
||||
|
||||
@implementation FIRDatabaseComponent
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
- (instancetype)initWithApp:(FIRApp *)app {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_app = app;
|
||||
_instances = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
+ (void)load {
|
||||
[FIRApp registerInternalLibrary:(Class<FIRLibrary>)self
|
||||
withName:@"fire-db"];
|
||||
}
|
||||
|
||||
#pragma mark - FIRComponentRegistrant
|
||||
|
||||
+ (NSArray<FIRComponent *> *)componentsToRegister {
|
||||
FIRDependency *authDep =
|
||||
[FIRDependency dependencyWithProtocol:@protocol(FIRAuthInterop)
|
||||
isRequired:NO];
|
||||
FIRComponentCreationBlock creationBlock =
|
||||
^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
|
||||
*isCacheable = YES;
|
||||
return [[FIRDatabaseComponent alloc] initWithApp:container.app];
|
||||
};
|
||||
FIRComponent *databaseProvider =
|
||||
[FIRComponent componentWithProtocol:@protocol(FIRDatabaseProvider)
|
||||
instantiationTiming:FIRInstantiationTimingLazy
|
||||
dependencies:@[ authDep ]
|
||||
creationBlock:creationBlock];
|
||||
return @[ databaseProvider ];
|
||||
}
|
||||
|
||||
#pragma mark - Instance management.
|
||||
|
||||
- (void)appWillBeDeleted:(FIRApp *)app {
|
||||
NSString *appName = app.name;
|
||||
if (appName == nil) {
|
||||
return;
|
||||
}
|
||||
FIRDatabaseDictionary *instances = [self instances];
|
||||
@synchronized(instances) {
|
||||
// Clean up the deleted instance in an effort to remove any resources
|
||||
// still in use. Note: Any leftover instances of this exact database
|
||||
// will be invalid.
|
||||
for (FIRDatabase *database in [instances allValues]) {
|
||||
[FRepoManager disposeRepos:database.config];
|
||||
}
|
||||
[instances removeAllObjects];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - FIRDatabaseProvider Conformance
|
||||
|
||||
- (FIRDatabase *)databaseForApp:(FIRApp *)app URL:(NSString *)url {
|
||||
if (app == nil) {
|
||||
[NSException raise:@"InvalidFIRApp"
|
||||
format:@"nil FIRApp instance passed to databaseForApp."];
|
||||
}
|
||||
|
||||
if (url == nil) {
|
||||
[NSException raise:@"MissingDatabaseURL"
|
||||
format:@"Failed to get FirebaseDatabase instance: "
|
||||
"Specify DatabaseURL within FIRApp or from your "
|
||||
"databaseForApp:URL: call."];
|
||||
}
|
||||
|
||||
NSURL *databaseUrl = [NSURL URLWithString:url];
|
||||
|
||||
if (databaseUrl == nil) {
|
||||
[NSException raise:@"InvalidDatabaseURL"
|
||||
format:@"The Database URL '%@' cannot be parsed. "
|
||||
"Specify a valid DatabaseURL within FIRApp or from "
|
||||
"your databaseForApp:URL: call.",
|
||||
url];
|
||||
} else if (![databaseUrl.path isEqualToString:@""] &&
|
||||
![databaseUrl.path isEqualToString:@"/"]) {
|
||||
[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, databaseUrl.path];
|
||||
}
|
||||
|
||||
FIRDatabaseDictionary *instances = [self instances];
|
||||
@synchronized(instances) {
|
||||
FParsedUrl *parsedUrl =
|
||||
[FUtilities parseUrl:databaseUrl.absoluteString];
|
||||
NSString *urlIndex =
|
||||
[NSString stringWithFormat:@"%@:%@", parsedUrl.repoInfo.host,
|
||||
[parsedUrl.path toString]];
|
||||
FIRDatabase *database = instances[urlIndex];
|
||||
if (!database) {
|
||||
id<FIRDatabaseConnectionContextProvider> contextProvider =
|
||||
[FIRDatabaseConnectionContextProvider
|
||||
contextProviderWithAuth:FIR_COMPONENT(FIRAuthInterop,
|
||||
app.container)
|
||||
appCheck:FIR_COMPONENT(FIRAppCheckInterop,
|
||||
app.container)];
|
||||
|
||||
// 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 (![FIRApp isDefaultAppConfigured] ||
|
||||
app != [FIRApp defaultApp]) {
|
||||
sessionIdentifier = app.name;
|
||||
}
|
||||
|
||||
FIRDatabaseConfig *config = [[FIRDatabaseConfig alloc]
|
||||
initWithSessionIdentifier:sessionIdentifier
|
||||
googleAppID:app.options.googleAppID
|
||||
contextProvider:contextProvider];
|
||||
database = [[FIRDatabase alloc] initWithApp:app
|
||||
repoInfo:parsedUrl.repoInfo
|
||||
config:config];
|
||||
instances[urlIndex] = database;
|
||||
}
|
||||
|
||||
return database;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 FIRDatabaseConnectionContextProvider;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* TODO: Merge FIRDatabaseConfig into FIRDatabase.
|
||||
*/
|
||||
@interface FIRDatabaseConfig : NSObject
|
||||
|
||||
- (id)initWithSessionIdentifier:(NSString *)identifier
|
||||
googleAppID:(NSString *)googleAppID
|
||||
contextProvider:
|
||||
(id<FIRDatabaseConnectionContextProvider>)contextProvider;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Api/FIRDatabaseConfig.h"
|
||||
|
||||
#import "FirebaseDatabase/Sources/FIRDatabaseConfig_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Login/FIRDatabaseConnectionContextProvider.h"
|
||||
|
||||
@interface FIRDatabaseConfig (Private)
|
||||
|
||||
@property(nonatomic, strong, readwrite) NSString *sessionIdentifier;
|
||||
@property(nonatomic, strong, readwrite) NSString *googleAppID;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FIRDatabaseConfig
|
||||
|
||||
- (id)init {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Can't create config objects!"];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)initWithSessionIdentifier:(NSString *)identifier
|
||||
googleAppID:(NSString *)googleAppID
|
||||
contextProvider:
|
||||
(id<FIRDatabaseConnectionContextProvider>)contextProvider {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
self->_sessionIdentifier = identifier;
|
||||
self->_callbackQueue = dispatch_get_main_queue();
|
||||
self->_googleAppID = googleAppID;
|
||||
self->_persistenceCacheSizeBytes =
|
||||
10 * 1024 * 1024; // Default cache size is 10MB
|
||||
self->_contextProvider = contextProvider;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)assertUnfrozen {
|
||||
if (self.isFrozen) {
|
||||
[NSException raise:NSGenericException
|
||||
format:@"Can't modify config objects after they are in use "
|
||||
@"for FIRDatabaseReferences."];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setContextProvider:
|
||||
(id<FIRDatabaseConnectionContextProvider>)contextProvider {
|
||||
[self assertUnfrozen];
|
||||
self->_contextProvider = contextProvider;
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,765 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabaseQuery.h"
|
||||
#import "FirebaseDatabase/Sources/Api/Private/FIRDatabaseQuery_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Constants/FConstants.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FQueryParams.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FQuerySpec.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FChildEventRegistration.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FValueEventRegistration.h"
|
||||
#import "FirebaseDatabase/Sources/FKeyIndex.h"
|
||||
#import "FirebaseDatabase/Sources/FPathIndex.h"
|
||||
#import "FirebaseDatabase/Sources/FPriorityIndex.h"
|
||||
#import "FirebaseDatabase/Sources/FValueIndex.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FLeafNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FSnapshotUtilities.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FNextPushId.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FValidation.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] &&
|
||||
params.indexStartKey != [FUtilities maxName]) {
|
||||
[NSException raise:INVALID_QUERY_PARAM_ERROR
|
||||
format:@"Can't use queryStartingAtValue:childKey:, "
|
||||
@"queryStartingAfterValue: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: or "
|
||||
@"queryStartingAfterValue: "
|
||||
@"with non-string types when used with "
|
||||
@"queryOrderedByKey"];
|
||||
}
|
||||
}
|
||||
if ([params hasEnd]) {
|
||||
if (params.indexEndKey != [FUtilities maxName] &&
|
||||
params.indexEndKey != [FUtilities minName]) {
|
||||
[NSException raise:INVALID_QUERY_PARAM_ERROR
|
||||
format:@"Can't use queryEndingAtValue:childKey: or "
|
||||
@"queryEndingBeforeValue:childKey: "
|
||||
@"queryEqualToValue:childKey: in "
|
||||
@"combination with queryOrderedByKey"];
|
||||
}
|
||||
if (![params.indexEndValue.val isKindOfClass:[NSString class]]) {
|
||||
[NSException
|
||||
raise:INVALID_QUERY_PARAM_ERROR
|
||||
format:@"Can't use queryEndingAtValue: or "
|
||||
@"queryEndingBeforeValue: "
|
||||
@"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:, queryStartingAfterValue:, "
|
||||
@"queryEndingAtValue:, queryEndingBeforeValue:, or "
|
||||
@"queryEqualToValue: must be valid priorities."];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)validateEqualToCall {
|
||||
if ([self.queryParams hasStart]) {
|
||||
[NSException
|
||||
raise:INVALID_QUERY_PARAM_ERROR
|
||||
format:
|
||||
@"Cannot combine queryEqualToValue: and queryStartingAtValue: "
|
||||
@"or queryStartingAfterValue:"];
|
||||
}
|
||||
if ([self.queryParams hasEnd]) {
|
||||
[NSException
|
||||
raise:INVALID_QUERY_PARAM_ERROR
|
||||
format:@"Cannot combine queryEqualToValue: and queryEndingAtValue: "
|
||||
@"or queryEndingBeforeValue:"];
|
||||
}
|
||||
}
|
||||
|
||||
- (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];
|
||||
}
|
||||
NSString *methodName = @"queryStartingAtValue:childKey:";
|
||||
if (childKey != nil) {
|
||||
[FValidation validateFrom:methodName validKey:childKey];
|
||||
}
|
||||
return [self queryStartingAtInternal:startValue
|
||||
childKey:childKey
|
||||
from:methodName
|
||||
priorityMethod:NO];
|
||||
}
|
||||
|
||||
- (FIRDatabaseQuery *)queryStartingAfterValue:(id)startAfterValue {
|
||||
return [self queryStartingAfterValue:startAfterValue childKey:nil];
|
||||
}
|
||||
|
||||
- (FIRDatabaseQuery *)queryStartingAfterValue:(id)startAfterValue
|
||||
childKey:(NSString *)childKey {
|
||||
if ([self.queryParams.index isEqual:[FKeyIndex keyIndex]]) {
|
||||
if (childKey != nil) {
|
||||
@throw [[NSException alloc]
|
||||
initWithName:INVALID_QUERY_PARAM_ERROR
|
||||
reason:
|
||||
@"You must use queryStartingAfterValue: instead of "
|
||||
@"queryStartingAfterValue:childKey: when using "
|
||||
@"queryOrderedByKey:"
|
||||
userInfo:nil];
|
||||
}
|
||||
if ([startAfterValue isKindOfClass:[NSString class]]) {
|
||||
startAfterValue = [FNextPushId successor:startAfterValue];
|
||||
}
|
||||
} else {
|
||||
if (childKey == nil) {
|
||||
childKey = [FUtilities maxName];
|
||||
} else {
|
||||
childKey = [FNextPushId successor:childKey];
|
||||
}
|
||||
}
|
||||
NSString *methodName = @"queryStartingAfterValue:childKey:";
|
||||
if (childKey != nil && ![childKey isEqual:[FUtilities maxName]]) {
|
||||
[FValidation validateFrom:methodName validKey:childKey];
|
||||
}
|
||||
return [self queryStartingAtInternal:startAfterValue
|
||||
childKey:childKey
|
||||
from:methodName
|
||||
priorityMethod:NO];
|
||||
}
|
||||
|
||||
- (FIRDatabaseQuery *)queryStartingAtInternal:(id<FNode>)startValue
|
||||
childKey:(NSString *)childKey
|
||||
from:(NSString *)methodName
|
||||
priorityMethod:(BOOL)priorityMethod {
|
||||
[self validateIndexValueType:startValue fromMethod:methodName];
|
||||
if ([self.queryParams hasStart]) {
|
||||
[NSException raise:INVALID_QUERY_PARAM_ERROR
|
||||
format:@"Can't call %@ after queryStartingAtValue, "
|
||||
@"queryStartingAfterValue, 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];
|
||||
}
|
||||
NSString *methodName = @"queryEndingAtValue:childKey:";
|
||||
if (childKey != nil) {
|
||||
[FValidation validateFrom:methodName validKey:childKey];
|
||||
}
|
||||
return [self queryEndingAtInternal:endValue
|
||||
childKey:childKey
|
||||
from:methodName
|
||||
priorityMethod:NO];
|
||||
}
|
||||
|
||||
- (FIRDatabaseQuery *)queryEndingBeforeValue:(id)endValue {
|
||||
return [self queryEndingBeforeValue:endValue childKey:nil];
|
||||
}
|
||||
|
||||
- (FIRDatabaseQuery *)queryEndingBeforeValue:(id)endValue
|
||||
childKey:(NSString *)childKey {
|
||||
if ([self.queryParams.index isEqual:[FKeyIndex keyIndex]]) {
|
||||
if (childKey != nil) {
|
||||
@throw [[NSException alloc]
|
||||
initWithName:INVALID_QUERY_PARAM_ERROR
|
||||
reason:@"You must use queryEndingBeforeValue: instead of "
|
||||
@"queryEndingBeforeValue:childKey: when using "
|
||||
@"queryOrderedByKey:"
|
||||
userInfo:nil];
|
||||
}
|
||||
if ([endValue isKindOfClass:[NSString class]]) {
|
||||
endValue = [FNextPushId predecessor:endValue];
|
||||
}
|
||||
} else {
|
||||
if (childKey == nil) {
|
||||
childKey = [FUtilities minName];
|
||||
} else {
|
||||
childKey = [FNextPushId predecessor:childKey];
|
||||
}
|
||||
}
|
||||
NSString *methodName = @"queryEndingBeforeValue:childKey:";
|
||||
if (childKey != nil && ![childKey isEqual:[FUtilities minName]]) {
|
||||
[FValidation validateFrom:methodName validKey:childKey];
|
||||
}
|
||||
return [self queryEndingAtInternal:endValue
|
||||
childKey:childKey
|
||||
from:methodName
|
||||
priorityMethod:NO];
|
||||
}
|
||||
|
||||
- (FIRDatabaseQuery *)queryEndingAtInternal:(id)endValue
|
||||
childKey:(NSString *)childKey
|
||||
from:(NSString *)methodName
|
||||
priorityMethod:(BOOL)priorityMethod {
|
||||
[self validateIndexValueType:endValue fromMethod:methodName];
|
||||
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, "
|
||||
@"queryStartingAfterValue, queryEndingAtValue, "
|
||||
@"queryEndingBeforeValue 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 >= 1ul << 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)getDataWithCompletionBlock:(void (^)(NSError *__nullable error,
|
||||
FIRDataSnapshot *snapshot))block {
|
||||
dispatch_async([FIRDatabaseQuery sharedQueue], ^{
|
||||
[self.repo getData:self withCompletionBlock:block];
|
||||
});
|
||||
}
|
||||
|
||||
- (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
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRMutableData.h"
|
||||
#import "FirebaseDatabase/Sources/Api/Private/FIRMutableData_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FSnapshotHolder.h"
|
||||
#import "FirebaseDatabase/Sources/FNamedNode.h"
|
||||
#import "FirebaseDatabase/Sources/FTransformedEnumerator.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FChildrenNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FIndexedNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FSnapshotUtilities.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<FIRMutableData *> *)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
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRServerValue.h"
|
||||
|
||||
@implementation FIRServerValue
|
||||
|
||||
+ (NSDictionary *)timestamp {
|
||||
static NSDictionary *timestamp = nil;
|
||||
if (timestamp == nil) {
|
||||
timestamp = @{@".sv" : @"timestamp"};
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)increment:(NSNumber *)delta {
|
||||
return @{@".sv" : @{@"increment" : delta}};
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -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 "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRTransactionResult.h"
|
||||
#import "FirebaseDatabase/Sources/Api/Private/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
|
||||
+28
@@ -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 "FirebaseDatabase/Sources/Api/Private/FTypedefs_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDataSnapshot.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FIndexedNode.h"
|
||||
|
||||
@interface FIRDataSnapshot ()
|
||||
|
||||
// in _Private for testing purposes
|
||||
@property(nonatomic, strong) FIndexedNode *node;
|
||||
|
||||
- (id)initWithRef:(FIRDatabaseReference *)ref indexedNode:(FIndexedNode *)node;
|
||||
|
||||
@end
|
||||
+43
@@ -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 "FirebaseDatabase/Sources/Api/Private/FTypedefs_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FQueryParams.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FRepo.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FRepoManager.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/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
|
||||
+27
@@ -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 "FirebaseDatabase/Sources/Api/FIRDatabaseConfig.h"
|
||||
#import "FirebaseDatabase/Sources/Api/Private/FTypedefs_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FRepo.h"
|
||||
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabaseReference.h"
|
||||
|
||||
@interface FIRDatabaseReference ()
|
||||
|
||||
- (id)initWithConfig:(FIRDatabaseConfig *)config;
|
||||
- (id)initWithRepo:(FRepo *)repo path:(FPath *)path;
|
||||
|
||||
@end
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabase.h"
|
||||
|
||||
@class FRepo;
|
||||
@class FRepoInfo;
|
||||
@class FIRDatabaseConfig;
|
||||
|
||||
@interface FIRDatabase ()
|
||||
|
||||
@property(nonatomic, strong) FRepoInfo *repoInfo;
|
||||
@property(nonatomic, strong) FIRDatabaseConfig *config;
|
||||
@property(nonatomic, strong) FRepo *repo;
|
||||
|
||||
- (id)initWithApp:(FIRApp *)app
|
||||
repoInfo:(FRepoInfo *)info
|
||||
config:(FIRDatabaseConfig *)config;
|
||||
|
||||
+ (NSString *)buildVersion;
|
||||
+ (FIRDatabase *)createDatabaseForTests:(FRepoInfo *)repoInfo
|
||||
config:(FIRDatabaseConfig *)config;
|
||||
|
||||
@end
|
||||
+26
@@ -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 "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRMutableData.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
|
||||
@interface FIRMutableData ()
|
||||
|
||||
- (id)initWithNode:(id<FNode>)node;
|
||||
- (id<FNode>)nodeValue;
|
||||
- (BOOL)isEqualToData:(FIRMutableData *)other;
|
||||
|
||||
@end
|
||||
+25
@@ -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 "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRMutableData.h"
|
||||
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRTransactionResult.h"
|
||||
|
||||
@interface FIRTransactionResult ()
|
||||
|
||||
@property(nonatomic) BOOL isSuccess;
|
||||
@property(nonatomic, strong) FIRMutableData *update;
|
||||
|
||||
@end
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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_id_nsstring)(NSString *str1, id dict1,
|
||||
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
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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_FConstants_h
|
||||
#define Firebase_FConstants_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Wire Protocol Envelope Constants
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestType;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestTypeData;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestDataPayload;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestNumber;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestPayloadBody;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestError;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestAction;
|
||||
FOUNDATION_EXPORT NSString *const kFWPResponseForRNData;
|
||||
FOUNDATION_EXPORT NSString *const kFWPResponseForActionStatus;
|
||||
FOUNDATION_EXPORT NSString *const kFWPResponseForActionStatusOk;
|
||||
FOUNDATION_EXPORT NSString *const kFWPResponseForActionStatusFailed;
|
||||
FOUNDATION_EXPORT NSString *const kFWPResponseForActionStatusDataStale;
|
||||
FOUNDATION_EXPORT NSString *const kFWPResponseForActionData;
|
||||
FOUNDATION_EXPORT NSString *const kFWPResponseDataWarnings;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerAction;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerPayloadBody;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerDataUpdate;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerDataMerge;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerDataRangeMerge;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerAuthRevoked;
|
||||
FOUNDATION_EXPORT NSString *const kFWPASyncServerListenCancelled;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerSecurityDebug;
|
||||
FOUNDATION_EXPORT NSString
|
||||
*const kFWPAsyncServerDataUpdateBodyPath; // {"a": "d", "b": {"p": "/", "d":
|
||||
// "<data>""}}
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerDataUpdateBodyData;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerDataUpdateStartPath;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerDataUpdateEndPath;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerDataUpdateRangeMerge;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerDataUpdateBodyTag;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerDataQueries;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerEnvelopeType;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerEnvelopeData;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerControlMessage;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerControlMessageType;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerControlMessageData;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerDataMessage;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerHello;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerHelloTimestamp;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerHelloVersion;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerHelloConnectedHost;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerHelloSession;
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerControlMessageShutdown;
|
||||
FOUNDATION_EXPORT NSString *const kFWPAsyncServerControlMessageReset;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Wire Protocol Payload Constants
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestActionPut;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestActionMerge;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestActionGet;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestActionTaggedListen;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestActionTaggedUnlisten;
|
||||
FOUNDATION_EXPORT NSString
|
||||
*const kFWPRequestActionListen; // {"t": "d", "d": {"r": 1, "a": "l", "b": {
|
||||
// "p": "/" } } }
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestActionUnlisten;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestActionStats;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestActionDisconnectPut;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestActionDisconnectMerge;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestActionDisconnectCancel;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestActionAppCheck;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestActionAuth;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestActionUnauth;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestAppCheckToken;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestCredential;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestPath;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestCounters;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestQueries;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestTag;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestData;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestHash;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestCompoundHash;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestCompoundHashPaths;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestCompoundHashHashes;
|
||||
FOUNDATION_EXPORT NSString *const kFWPRequestStatus;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Websock Transport Constants
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kWireProtocolVersionParam;
|
||||
FOUNDATION_EXPORT NSString *const kWebsocketProtocolVersion;
|
||||
FOUNDATION_EXPORT NSString *const kWebsocketServerKillPacket;
|
||||
FOUNDATION_EXPORT NSString *const kPersistentConnectionOffline;
|
||||
FOUNDATION_EXPORT const int kWebsocketMaxFrameSize;
|
||||
FOUNDATION_EXPORT NSUInteger const kWebsocketKeepaliveInterval;
|
||||
FOUNDATION_EXPORT NSUInteger const kWebsocketConnectTimeout;
|
||||
FOUNDATION_EXPORT UInt64 const kPersistentConnectionGetConnectTimeout;
|
||||
|
||||
FOUNDATION_EXPORT float const kPersistentConnReconnectMinDelay;
|
||||
FOUNDATION_EXPORT float const kPersistentConnReconnectMaxDelay;
|
||||
FOUNDATION_EXPORT float const kPersistentConnReconnectMultiplier;
|
||||
FOUNDATION_EXPORT float const
|
||||
kPersistentConnSuccessfulConnectionEstablishedDelay;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Query / QueryParams constants
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kQueryDefault;
|
||||
FOUNDATION_EXPORT NSString *const kQueryDefaultObject;
|
||||
FOUNDATION_EXPORT NSString *const kViewManagerDictConstView;
|
||||
FOUNDATION_EXPORT NSString *const kFQPIndexStartValue;
|
||||
FOUNDATION_EXPORT NSString *const kFQPIndexStartName;
|
||||
FOUNDATION_EXPORT NSString *const kFQPIndexEndValue;
|
||||
FOUNDATION_EXPORT NSString *const kFQPIndexEndName;
|
||||
FOUNDATION_EXPORT NSString *const kFQPLimit;
|
||||
FOUNDATION_EXPORT NSString *const kFQPViewFrom;
|
||||
FOUNDATION_EXPORT NSString *const kFQPViewFromLeft;
|
||||
FOUNDATION_EXPORT NSString *const kFQPViewFromRight;
|
||||
FOUNDATION_EXPORT NSString *const kFQPIndex;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Interrupt Reasons
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kFInterruptReasonServerKill;
|
||||
FOUNDATION_EXPORT NSString *const kFInterruptReasonWaitingForOpen;
|
||||
FOUNDATION_EXPORT NSString *const kFInterruptReasonRepoInterrupt;
|
||||
FOUNDATION_EXPORT NSString *const kFInterruptReasonAuthExpired;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Payload constants
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kPayloadPriority;
|
||||
FOUNDATION_EXPORT NSString *const kPayloadValue;
|
||||
FOUNDATION_EXPORT NSString *const kPayloadMetadataPrefix;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ServerValue constants
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kServerValueSubKey;
|
||||
FOUNDATION_EXPORT NSString *const kServerValuePriority;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark.info/ constants
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kDotInfoPrefix;
|
||||
FOUNDATION_EXPORT NSString *const kDotInfoConnected;
|
||||
FOUNDATION_EXPORT NSString *const kDotInfoServerTimeOffset;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ObjectiveC to JavaScript type constants
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kJavaScriptObject;
|
||||
FOUNDATION_EXPORT NSString *const kJavaScriptString;
|
||||
FOUNDATION_EXPORT NSString *const kJavaScriptBoolean;
|
||||
FOUNDATION_EXPORT NSString *const kJavaScriptNumber;
|
||||
FOUNDATION_EXPORT NSString *const kJavaScriptNull;
|
||||
FOUNDATION_EXPORT NSString *const kJavaScriptTrue;
|
||||
FOUNDATION_EXPORT NSString *const kJavaScriptFalse;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Error handling constants
|
||||
|
||||
FOUNDATION_EXPORT NSString *const kFErrorDomain;
|
||||
FOUNDATION_EXPORT NSUInteger const kFAuthError;
|
||||
FOUNDATION_EXPORT NSString *const kFErrorWriteCanceled;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Validation Constants
|
||||
|
||||
FOUNDATION_EXPORT NSUInteger const kFirebaseMaxObjectDepth;
|
||||
FOUNDATION_EXPORT const unsigned int kFirebaseMaxLeafSize;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Transaction Constants
|
||||
|
||||
FOUNDATION_EXPORT NSUInteger const kFTransactionMaxRetries;
|
||||
FOUNDATION_EXPORT NSString *const kFTransactionTooManyRetries;
|
||||
FOUNDATION_EXPORT NSString *const kFTransactionNoData;
|
||||
FOUNDATION_EXPORT NSString *const kFTransactionSet;
|
||||
FOUNDATION_EXPORT NSString *const kFTransactionDisconnect;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Constants/FConstants.h"
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Wire Protocol Envelope Constants
|
||||
|
||||
NSString *const kFWPRequestType = @"t";
|
||||
NSString *const kFWPRequestTypeData = @"d";
|
||||
NSString *const kFWPRequestDataPayload = @"d";
|
||||
NSString *const kFWPRequestNumber = @"r";
|
||||
NSString *const kFWPRequestPayloadBody = @"b";
|
||||
NSString *const kFWPRequestError = @"error";
|
||||
NSString *const kFWPRequestAction = @"a";
|
||||
NSString *const kFWPResponseForRNData = @"b";
|
||||
NSString *const kFWPResponseForActionStatus = @"s";
|
||||
NSString *const kFWPResponseForActionStatusOk = @"ok";
|
||||
NSString *const kFWPResponseForActionStatusFailed = @"failed";
|
||||
NSString *const kFWPResponseForActionStatusDataStale = @"datastale";
|
||||
NSString *const kFWPResponseForActionData = @"d";
|
||||
NSString *const kFWPResponseDataWarnings = @"w";
|
||||
NSString *const kFWPAsyncServerAction = @"a";
|
||||
NSString *const kFWPAsyncServerPayloadBody = @"b";
|
||||
NSString *const kFWPAsyncServerDataUpdate = @"d";
|
||||
NSString *const kFWPAsyncServerDataMerge = @"m";
|
||||
NSString *const kFWPAsyncServerDataRangeMerge = @"rm";
|
||||
NSString *const kFWPAsyncServerAuthRevoked = @"ac";
|
||||
NSString *const kFWPASyncServerListenCancelled = @"c";
|
||||
NSString *const kFWPAsyncServerSecurityDebug = @"sd";
|
||||
NSString *const kFWPAsyncServerDataUpdateBodyPath =
|
||||
@"p"; // {"a": "d", "b": {"p": "/", "d": "<data>"}}
|
||||
NSString *const kFWPAsyncServerDataUpdateBodyData = @"d";
|
||||
NSString *const kFWPAsyncServerDataUpdateStartPath = @"s";
|
||||
NSString *const kFWPAsyncServerDataUpdateEndPath = @"e";
|
||||
NSString *const kFWPAsyncServerDataUpdateRangeMerge = @"m";
|
||||
NSString *const kFWPAsyncServerDataUpdateBodyTag = @"t";
|
||||
NSString *const kFWPAsyncServerDataQueries = @"q";
|
||||
|
||||
NSString *const kFWPAsyncServerEnvelopeType = @"t";
|
||||
NSString *const kFWPAsyncServerEnvelopeData = @"d";
|
||||
NSString *const kFWPAsyncServerControlMessage = @"c";
|
||||
NSString *const kFWPAsyncServerControlMessageType = @"t";
|
||||
NSString *const kFWPAsyncServerControlMessageData = @"d";
|
||||
NSString *const kFWPAsyncServerDataMessage = @"d";
|
||||
|
||||
NSString *const kFWPAsyncServerHello = @"h";
|
||||
NSString *const kFWPAsyncServerHelloTimestamp = @"ts";
|
||||
NSString *const kFWPAsyncServerHelloVersion = @"v";
|
||||
NSString *const kFWPAsyncServerHelloConnectedHost = @"h";
|
||||
NSString *const kFWPAsyncServerHelloSession = @"s";
|
||||
|
||||
NSString *const kFWPAsyncServerControlMessageShutdown = @"s";
|
||||
NSString *const kFWPAsyncServerControlMessageReset = @"r";
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Wire Protocol Payload Constants
|
||||
|
||||
NSString *const kFWPRequestActionPut = @"p";
|
||||
NSString *const kFWPRequestActionMerge = @"m";
|
||||
NSString *const kFWPRequestActionGet = @"g";
|
||||
NSString *const kFWPRequestActionListen =
|
||||
@"l"; // {"t": "d", "d": {"r": 1, "a": "l", "b": { "p": "/" } } }
|
||||
NSString *const kFWPRequestActionUnlisten = @"u";
|
||||
NSString *const kFWPRequestActionStats = @"s";
|
||||
NSString *const kFWPRequestActionTaggedListen = @"q";
|
||||
NSString *const kFWPRequestActionTaggedUnlisten = @"n";
|
||||
NSString *const kFWPRequestActionDisconnectPut = @"o";
|
||||
NSString *const kFWPRequestActionDisconnectMerge = @"om";
|
||||
NSString *const kFWPRequestActionDisconnectCancel = @"oc";
|
||||
NSString *const kFWPRequestActionAuth = @"auth";
|
||||
NSString *const kFWPRequestActionAppCheck = @"appcheck";
|
||||
NSString *const kFWPRequestActionUnauth = @"unauth";
|
||||
NSString *const kFWPRequestAppCheckToken = @"token";
|
||||
NSString *const kFWPRequestCredential = @"cred";
|
||||
NSString *const kFWPRequestPath = @"p";
|
||||
NSString *const kFWPRequestCounters = @"c";
|
||||
NSString *const kFWPRequestQueries = @"q";
|
||||
NSString *const kFWPRequestTag = @"t";
|
||||
NSString *const kFWPRequestData = @"d";
|
||||
NSString *const kFWPRequestHash = @"h";
|
||||
NSString *const kFWPRequestCompoundHash = @"ch";
|
||||
NSString *const kFWPRequestCompoundHashPaths = @"ps";
|
||||
NSString *const kFWPRequestCompoundHashHashes = @"hs";
|
||||
NSString *const kFWPRequestStatus = @"s";
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Websock Transport Constants
|
||||
|
||||
NSString *const kWireProtocolVersionParam = @"v";
|
||||
NSString *const kWebsocketProtocolVersion = @"5";
|
||||
NSString *const kWebsocketServerKillPacket = @"kill";
|
||||
NSString *const kPersistentConnectionOffline = @"Client is offline.";
|
||||
const int kWebsocketMaxFrameSize = 16384;
|
||||
NSUInteger const kWebsocketKeepaliveInterval = 45;
|
||||
NSUInteger const kWebsocketConnectTimeout = 30;
|
||||
UInt64 const kPersistentConnectionGetConnectTimeout = 3 * NSEC_PER_SEC;
|
||||
|
||||
float const kPersistentConnReconnectMinDelay = 1.0;
|
||||
float const kPersistentConnReconnectMaxDelay = 30.0;
|
||||
float const kPersistentConnReconnectMultiplier = 1.3f;
|
||||
float const kPersistentConnSuccessfulConnectionEstablishedDelay = 30.0;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Query constants
|
||||
|
||||
NSString *const kQueryDefault = @"default";
|
||||
NSString *const kQueryDefaultObject = @"{}";
|
||||
NSString *const kViewManagerDictConstView = @"view";
|
||||
NSString *const kFQPIndexStartValue = @"sp";
|
||||
NSString *const kFQPIndexStartName = @"sn";
|
||||
NSString *const kFQPIndexEndValue = @"ep";
|
||||
NSString *const kFQPIndexEndName = @"en";
|
||||
NSString *const kFQPLimit = @"l";
|
||||
NSString *const kFQPViewFrom = @"vf";
|
||||
NSString *const kFQPViewFromLeft = @"l";
|
||||
NSString *const kFQPViewFromRight = @"r";
|
||||
NSString *const kFQPIndex = @"i";
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Interrupt Reasons
|
||||
|
||||
NSString *const kFInterruptReasonServerKill = @"server_kill";
|
||||
NSString *const kFInterruptReasonWaitingForOpen = @"waiting_for_open";
|
||||
NSString *const kFInterruptReasonRepoInterrupt = @"repo_interrupt";
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Payload constants
|
||||
|
||||
NSString *const kPayloadPriority = @".priority";
|
||||
NSString *const kPayloadValue = @".value";
|
||||
NSString *const kPayloadMetadataPrefix = @".";
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ServerValue constants
|
||||
|
||||
NSString *const kServerValueSubKey = @".sv";
|
||||
NSString *const kServerValuePriority = @"timestamp";
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark.info/ constants
|
||||
|
||||
NSString *const kDotInfoPrefix = @".info";
|
||||
NSString *const kDotInfoConnected = @"connected";
|
||||
NSString *const kDotInfoServerTimeOffset = @"serverTimeOffset";
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ObjectiveC to JavaScript type constants
|
||||
|
||||
NSString *const kJavaScriptObject = @"object";
|
||||
NSString *const kJavaScriptString = @"string";
|
||||
NSString *const kJavaScriptBoolean = @"boolean";
|
||||
NSString *const kJavaScriptNumber = @"number";
|
||||
NSString *const kJavaScriptNull = @"null";
|
||||
NSString *const kJavaScriptTrue = @"true";
|
||||
NSString *const kJavaScriptFalse = @"false";
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Error handling constants
|
||||
|
||||
NSString *const kFErrorDomain = @"com.firebase";
|
||||
NSUInteger const kFAuthError = 1;
|
||||
NSString *const kFErrorWriteCanceled = @"write_canceled";
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Validation Constants
|
||||
|
||||
NSUInteger const kFirebaseMaxObjectDepth = 1000;
|
||||
const unsigned int kFirebaseMaxLeafSize = 1024 * 1024 * 10; // 10 MB
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Transaction Constants
|
||||
|
||||
NSUInteger const kFTransactionMaxRetries = 25;
|
||||
NSString *const kFTransactionTooManyRetries = @"maxretry";
|
||||
NSString *const kFTransactionNoData = @"nodata";
|
||||
NSString *const kFTransactionSet = @"set";
|
||||
NSString *const kFTransactionDisconnect = @"disconnect";
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
|
||||
@interface FCompoundHashBuilder : NSObject
|
||||
|
||||
- (FPath *)currentPath;
|
||||
|
||||
@end
|
||||
|
||||
typedef BOOL (^FCompoundHashSplitStrategy)(FCompoundHashBuilder *builder);
|
||||
|
||||
@interface FCompoundHash : NSObject
|
||||
|
||||
@property(nonatomic, strong, readonly) NSArray *posts;
|
||||
@property(nonatomic, strong, readonly) NSArray *hashes;
|
||||
|
||||
+ (FCompoundHash *)fromNode:(id<FNode>)node;
|
||||
+ (FCompoundHash *)fromNode:(id<FNode>)node
|
||||
splitStrategy:(FCompoundHashSplitStrategy)strategy;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FCompoundHash.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FChildrenNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FLeafNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FSnapshotUtilities.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FStringUtilities.h"
|
||||
|
||||
@interface FCompoundHashBuilder ()
|
||||
|
||||
@property(nonatomic, strong) FCompoundHashSplitStrategy splitStrategy;
|
||||
|
||||
@property(nonatomic, strong) NSMutableArray *currentPaths;
|
||||
@property(nonatomic, strong) NSMutableArray *currentHashes;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FCompoundHashBuilder {
|
||||
|
||||
// NOTE: We use the existence of this to know if we've started building a
|
||||
// range (i.e. encountered a leaf node).
|
||||
NSMutableString *optHashValueBuilder;
|
||||
|
||||
// The current path as a stack. This is used in combination with
|
||||
// currentPathDepth to simultaneously store the last leaf node path. The
|
||||
// depth is changed when descending and ascending, at the same time the
|
||||
// current key is set for the current depth. Because the keys are left
|
||||
// unchanged for ascending the path will also contain the path of the last
|
||||
// visited leaf node (using lastLeafDepth elements)
|
||||
NSMutableArray *currentPath;
|
||||
NSInteger lastLeafDepth;
|
||||
NSInteger currentPathDepth;
|
||||
|
||||
BOOL needsComma;
|
||||
}
|
||||
|
||||
- (instancetype)initWithSplitStrategy:(FCompoundHashSplitStrategy)strategy {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
self->_splitStrategy = strategy;
|
||||
self->optHashValueBuilder = nil;
|
||||
self->currentPath = [NSMutableArray array];
|
||||
self->lastLeafDepth = -1;
|
||||
self->currentPathDepth = 0;
|
||||
self->needsComma = YES;
|
||||
self->_currentPaths = [NSMutableArray array];
|
||||
self->_currentHashes = [NSMutableArray array];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isBuildingRange {
|
||||
return self->optHashValueBuilder != nil;
|
||||
}
|
||||
|
||||
- (NSUInteger)currentHashLength {
|
||||
return self->optHashValueBuilder.length;
|
||||
}
|
||||
|
||||
- (FPath *)currentPath {
|
||||
return [self currentPathWithDepth:self->currentPathDepth];
|
||||
}
|
||||
|
||||
- (FPath *)currentPathWithDepth:(NSInteger)depth {
|
||||
NSArray *pieces =
|
||||
[self->currentPath subarrayWithRange:NSMakeRange(0, depth)];
|
||||
return [[FPath alloc] initWithPieces:pieces andPieceNum:0];
|
||||
}
|
||||
|
||||
- (void)enumerateCurrentPathToDepth:(NSInteger)depth
|
||||
withBlock:(void (^)(NSString *key))block {
|
||||
for (NSInteger i = 0; i < depth; i++) {
|
||||
block(self->currentPath[i]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)appendKey:(NSString *)key toString:(NSMutableString *)string {
|
||||
[FSnapshotUtilities appendHashV2RepresentationForString:key
|
||||
toString:string];
|
||||
}
|
||||
|
||||
- (void)ensureRange {
|
||||
if (![self isBuildingRange]) {
|
||||
optHashValueBuilder = [NSMutableString string];
|
||||
[optHashValueBuilder appendString:@"("];
|
||||
[self
|
||||
enumerateCurrentPathToDepth:self->currentPathDepth
|
||||
withBlock:^(NSString *key) {
|
||||
[self appendKey:key
|
||||
toString:self->optHashValueBuilder];
|
||||
[self->optHashValueBuilder appendString:@":("];
|
||||
}];
|
||||
self->needsComma = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)processLeaf:(FLeafNode *)leafNode {
|
||||
[self ensureRange];
|
||||
|
||||
self->lastLeafDepth = self->currentPathDepth;
|
||||
[FSnapshotUtilities
|
||||
appendHashRepresentationForLeafNode:leafNode
|
||||
toString:self->optHashValueBuilder
|
||||
hashVersion:FDataHashVersionV2];
|
||||
self->needsComma = YES;
|
||||
if (self.splitStrategy(self)) {
|
||||
[self endRange];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)startChild:(NSString *)key {
|
||||
[self ensureRange];
|
||||
|
||||
if (self->needsComma) {
|
||||
[self->optHashValueBuilder appendString:@","];
|
||||
}
|
||||
[self appendKey:key toString:self->optHashValueBuilder];
|
||||
[self->optHashValueBuilder appendString:@":("];
|
||||
if (self->currentPathDepth == currentPath.count) {
|
||||
[self->currentPath addObject:key];
|
||||
} else {
|
||||
self->currentPath[self->currentPathDepth] = key;
|
||||
}
|
||||
self->currentPathDepth++;
|
||||
self->needsComma = NO;
|
||||
}
|
||||
|
||||
- (void)endChild {
|
||||
self->currentPathDepth--;
|
||||
if ([self isBuildingRange]) {
|
||||
[self->optHashValueBuilder appendString:@")"];
|
||||
}
|
||||
self->needsComma = YES;
|
||||
}
|
||||
|
||||
- (void)finishHashing {
|
||||
NSAssert(self->currentPathDepth == 0,
|
||||
@"Can't finish hashing in the middle of processing a child");
|
||||
if ([self isBuildingRange]) {
|
||||
[self endRange];
|
||||
}
|
||||
|
||||
// Always close with the empty hash for the remaining range to allow simple
|
||||
// appending
|
||||
[self.currentHashes addObject:@""];
|
||||
}
|
||||
|
||||
- (void)endRange {
|
||||
NSAssert([self isBuildingRange],
|
||||
@"Can't end range without starting a range!");
|
||||
// Add closing parenthesis for current depth
|
||||
for (NSUInteger i = 0; i < currentPathDepth; i++) {
|
||||
[self->optHashValueBuilder appendString:@")"];
|
||||
}
|
||||
[self->optHashValueBuilder appendString:@")"];
|
||||
|
||||
FPath *lastLeafPath = [self currentPathWithDepth:self->lastLeafDepth];
|
||||
NSString *hash =
|
||||
[FStringUtilities base64EncodedSha1:self->optHashValueBuilder];
|
||||
[self.currentHashes addObject:hash];
|
||||
[self.currentPaths addObject:lastLeafPath];
|
||||
|
||||
self->optHashValueBuilder = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface FCompoundHash ()
|
||||
|
||||
@property(nonatomic, strong, readwrite) NSArray *posts;
|
||||
@property(nonatomic, strong, readwrite) NSArray *hashes;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FCompoundHash
|
||||
|
||||
- (id)initWithPosts:(NSArray *)posts hashes:(NSArray *)hashes {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
if (posts.count != hashes.count - 1) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Number of posts need to be n-1 for n hashes "
|
||||
@"in FCompoundHash"];
|
||||
}
|
||||
self.posts = posts;
|
||||
self.hashes = hashes;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (FCompoundHashSplitStrategy)simpleSizeSplitStrategyForNode:(id<FNode>)node {
|
||||
NSUInteger estimatedSize =
|
||||
[FSnapshotUtilities estimateSerializedNodeSize:node];
|
||||
|
||||
// Splits for
|
||||
// 1k -> 512 (2 parts)
|
||||
// 5k -> 715 (7 parts)
|
||||
// 100k -> 3.2k (32 parts)
|
||||
// 500k -> 7k (71 parts)
|
||||
// 5M -> 23k (228 parts)
|
||||
NSUInteger splitThreshold = MAX(512, (NSUInteger)sqrt(estimatedSize * 100));
|
||||
|
||||
return ^BOOL(FCompoundHashBuilder *builder) {
|
||||
// Never split on priorities
|
||||
return [builder currentHashLength] > splitThreshold &&
|
||||
![[[builder currentPath] getBack] isEqualToString:@".priority"];
|
||||
};
|
||||
}
|
||||
|
||||
+ (FCompoundHash *)fromNode:(id<FNode>)node {
|
||||
return [FCompoundHash
|
||||
fromNode:node
|
||||
splitStrategy:[FCompoundHash simpleSizeSplitStrategyForNode:node]];
|
||||
}
|
||||
|
||||
+ (FCompoundHash *)fromNode:(id<FNode>)node
|
||||
splitStrategy:(FCompoundHashSplitStrategy)strategy {
|
||||
if ([node isEmpty]) {
|
||||
return [[FCompoundHash alloc] initWithPosts:@[] hashes:@[ @"" ]];
|
||||
} else {
|
||||
FCompoundHashBuilder *builder =
|
||||
[[FCompoundHashBuilder alloc] initWithSplitStrategy:strategy];
|
||||
[FCompoundHash processNode:node builder:builder];
|
||||
[builder finishHashing];
|
||||
return [[FCompoundHash alloc] initWithPosts:builder.currentPaths
|
||||
hashes:builder.currentHashes];
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)processNode:(id<FNode>)node builder:(FCompoundHashBuilder *)builder {
|
||||
if ([node isLeafNode]) {
|
||||
[builder processLeaf:node];
|
||||
} else {
|
||||
NSAssert(![node isEmpty], @"Can't calculate hash on empty node!");
|
||||
FChildrenNode *childrenNode = (FChildrenNode *)node;
|
||||
[childrenNode enumerateChildrenAndPriorityUsingBlock:^(
|
||||
NSString *key, id<FNode> node, BOOL *stop) {
|
||||
[builder startChild:key];
|
||||
[self processNode:node builder:builder];
|
||||
[builder endChild];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Api/Private/FTypedefs_Private.h"
|
||||
|
||||
@class FQuerySpec;
|
||||
@protocol FSyncTreeHash;
|
||||
|
||||
typedef NSArray * (^fbt_startListeningBlock)(FQuerySpec *query, NSNumber *tagId,
|
||||
id<FSyncTreeHash> hash,
|
||||
fbt_nsarray_nsstring onComplete);
|
||||
typedef void (^fbt_stopListeningBlock)(FQuerySpec *query, NSNumber *tagId);
|
||||
|
||||
@interface FListenProvider : NSObject
|
||||
|
||||
@property(nonatomic, copy) fbt_startListeningBlock startListening;
|
||||
@property(nonatomic, copy) fbt_stopListeningBlock stopListening;
|
||||
|
||||
@end
|
||||
@@ -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 "FirebaseDatabase/Sources/Core/FListenProvider.h"
|
||||
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabaseQuery.h"
|
||||
|
||||
@implementation FListenProvider
|
||||
|
||||
@synthesize startListening;
|
||||
@synthesize stopListening;
|
||||
|
||||
@end
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Api/Private/FTypedefs_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FRepoInfo.h"
|
||||
#import "FirebaseDatabase/Sources/Realtime/FConnection.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FTypedefs.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol FPersistentConnectionDelegate;
|
||||
@protocol FSyncTreeHash;
|
||||
@class FQuerySpec;
|
||||
@class FIRDatabaseConfig;
|
||||
|
||||
@interface FPersistentConnection : NSObject <FConnectionDelegate>
|
||||
|
||||
@property(nonatomic, weak) id<FPersistentConnectionDelegate> delegate;
|
||||
@property(nonatomic) BOOL pauseWrites;
|
||||
|
||||
- (id)initWithRepoInfo:(FRepoInfo *)repoInfo
|
||||
dispatchQueue:(dispatch_queue_t)queue
|
||||
config:(FIRDatabaseConfig *)config;
|
||||
|
||||
- (void)open;
|
||||
|
||||
- (void)putData:(id)data
|
||||
forPath:(NSString *)pathString
|
||||
withHash:(NSString *)hash
|
||||
withCallback:(fbt_void_nsstring_nsstring)onComplete;
|
||||
- (void)mergeData:(id)data
|
||||
forPath:(NSString *)pathString
|
||||
withCallback:(fbt_void_nsstring_nsstring)onComplete;
|
||||
|
||||
- (void)listen:(FQuerySpec *)query
|
||||
tagId:(NSNumber *)tagId
|
||||
hash:(id<FSyncTreeHash>)hash
|
||||
onComplete:(fbt_void_nsstring)onComplete;
|
||||
|
||||
- (void)unlisten:(FQuerySpec *)query tagId:(NSNumber *)tagId;
|
||||
- (void)refreshAuthToken:(NSString *)token;
|
||||
- (void)refreshAppCheckToken:(NSString *)token;
|
||||
- (void)onDisconnectPutData:(id)data
|
||||
forPath:(FPath *)path
|
||||
withCallback:(fbt_void_nsstring_nsstring)callback;
|
||||
- (void)onDisconnectMergeData:(id)data
|
||||
forPath:(FPath *)path
|
||||
withCallback:(fbt_void_nsstring_nsstring)callback;
|
||||
- (void)onDisconnectCancelPath:(FPath *)path
|
||||
withCallback:(fbt_void_nsstring_nsstring)callback;
|
||||
- (void)ackPuts;
|
||||
- (void)getDataAtPath:(NSString *)pathString
|
||||
withParams:(NSDictionary *)queryWireProtocolParams
|
||||
withCallback:(fbt_void_nsstring_id_nsstring)onComplete;
|
||||
- (void)purgeOutstandingWrites;
|
||||
|
||||
- (void)interruptForReason:(NSString *)reason;
|
||||
- (void)resumeForReason:(NSString *)reason;
|
||||
- (BOOL)isInterruptedForReason:(NSString *)reason;
|
||||
|
||||
// FConnection delegate methods
|
||||
- (void)onReady:(FConnection *)fconnection
|
||||
atTime:(NSNumber *)timestamp
|
||||
sessionID:(NSString *)sessionID;
|
||||
- (void)onDataMessage:(FConnection *)fconnection
|
||||
withMessage:(NSDictionary *)message;
|
||||
- (void)onDisconnect:(FConnection *)fconnection
|
||||
withReason:(FDisconnectReason)reason;
|
||||
- (void)onKill:(FConnection *)fconnection withReason:(NSString *)reason;
|
||||
|
||||
// Testing methods
|
||||
- (NSDictionary *)dumpListens;
|
||||
|
||||
@end
|
||||
|
||||
@protocol FPersistentConnectionDelegate <NSObject>
|
||||
|
||||
- (void)onDataUpdate:(FPersistentConnection *)fpconnection
|
||||
forPath:(NSString *)pathString
|
||||
message:(id)message
|
||||
isMerge:(BOOL)isMerge
|
||||
tagId:(NSNumber *)tagId;
|
||||
- (void)onRangeMerge:(NSArray *)ranges
|
||||
forPath:(NSString *)path
|
||||
tagId:(NSNumber *)tag;
|
||||
- (void)onConnect:(FPersistentConnection *)fpconnection;
|
||||
- (void)onDisconnect:(FPersistentConnection *)fpconnection;
|
||||
- (void)onServerInfoUpdate:(FPersistentConnection *)fpconnection
|
||||
updates:(NSDictionary *)updates;
|
||||
|
||||
@end
|
||||
+1334
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 FIndex
|
||||
, FNodeFilter, FNode;
|
||||
|
||||
@interface FQueryParams : NSObject <NSCopying>
|
||||
|
||||
@property(nonatomic, readonly) BOOL limitSet;
|
||||
@property(nonatomic, readonly) NSInteger limit;
|
||||
|
||||
@property(nonatomic, strong, readonly) NSString *viewFrom;
|
||||
@property(nonatomic, strong, readonly) id<FNode> indexStartValue;
|
||||
@property(nonatomic, strong, readonly) NSString *indexStartKey;
|
||||
@property(nonatomic, strong, readonly) id<FNode> indexEndValue;
|
||||
@property(nonatomic, strong, readonly) NSString *indexEndKey;
|
||||
|
||||
@property(nonatomic, strong, readonly) id<FIndex> index;
|
||||
|
||||
- (BOOL)loadsAllData;
|
||||
- (BOOL)isDefault;
|
||||
- (BOOL)isValid;
|
||||
- (BOOL)hasAnchoredLimit;
|
||||
|
||||
- (FQueryParams *)limitTo:(NSInteger)limit;
|
||||
- (FQueryParams *)limitToFirst:(NSInteger)newLimit;
|
||||
- (FQueryParams *)limitToLast:(NSInteger)newLimit;
|
||||
|
||||
- (FQueryParams *)startAt:(id<FNode>)indexValue childKey:(NSString *)key;
|
||||
- (FQueryParams *)startAt:(id<FNode>)indexValue;
|
||||
- (FQueryParams *)endAt:(id<FNode>)indexValue childKey:(NSString *)key;
|
||||
- (FQueryParams *)endAt:(id<FNode>)indexValue;
|
||||
|
||||
- (FQueryParams *)orderBy:(id<FIndex>)index;
|
||||
|
||||
+ (FQueryParams *)defaultInstance;
|
||||
+ (FQueryParams *)fromQueryObject:(NSDictionary *)dict;
|
||||
|
||||
- (BOOL)hasStart;
|
||||
- (BOOL)hasEnd;
|
||||
|
||||
- (NSDictionary *)wireProtocolParams;
|
||||
- (BOOL)isViewFromLeft;
|
||||
- (id<FNodeFilter>)nodeFilter;
|
||||
@end
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FQueryParams.h"
|
||||
#import "FirebaseDatabase/Sources/Constants/FConstants.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/Filter/FIndexedFilter.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/Filter/FLimitedFilter.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/Filter/FNodeFilter.h"
|
||||
#import "FirebaseDatabase/Sources/FIndex.h"
|
||||
#import "FirebaseDatabase/Sources/FPriorityIndex.h"
|
||||
#import "FirebaseDatabase/Sources/FRangedFilter.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FSnapshotUtilities.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FUtilities.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FValidation.h"
|
||||
|
||||
@interface FQueryParams ()
|
||||
|
||||
@property(nonatomic, readwrite) BOOL limitSet;
|
||||
@property(nonatomic, readwrite) NSInteger limit;
|
||||
|
||||
@property(nonatomic, strong, readwrite) NSString *viewFrom;
|
||||
/**
|
||||
* indexStartValue is anything you can store as a priority / value.
|
||||
*/
|
||||
@property(nonatomic, strong, readwrite) id<FNode> indexStartValue;
|
||||
@property(nonatomic, strong, readwrite) NSString *indexStartKey;
|
||||
/**
|
||||
* indexStartValue is anything you can store as a priority / value.
|
||||
*/
|
||||
@property(nonatomic, strong, readwrite) id<FNode> indexEndValue;
|
||||
@property(nonatomic, strong, readwrite) NSString *indexEndKey;
|
||||
|
||||
@property(nonatomic, strong, readwrite) id<FIndex> index;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FQueryParams
|
||||
|
||||
+ (FQueryParams *)defaultInstance {
|
||||
static FQueryParams *defaultParams = nil;
|
||||
static dispatch_once_t defaultParamsToken;
|
||||
dispatch_once(&defaultParamsToken, ^{
|
||||
defaultParams = [[FQueryParams alloc] init];
|
||||
});
|
||||
return defaultParams;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self->_limitSet = NO;
|
||||
self->_limit = 0;
|
||||
|
||||
self->_viewFrom = nil;
|
||||
self->_indexStartValue = nil;
|
||||
self->_indexStartKey = nil;
|
||||
self->_indexEndValue = nil;
|
||||
self->_indexEndKey = nil;
|
||||
|
||||
self->_index = [FPriorityIndex priorityIndex];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only valid if hasStart is true
|
||||
*/
|
||||
- (id)indexStartValue {
|
||||
NSAssert([self hasStart], @"Only valid if start has been set");
|
||||
return _indexStartValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only valid if hasStart is true.
|
||||
* @return The starting key name for the range defined by these query parameters
|
||||
*/
|
||||
- (NSString *)indexStartKey {
|
||||
NSAssert([self hasStart], @"Only valid if start has been set");
|
||||
if (_indexStartKey == nil) {
|
||||
return [FUtilities minName];
|
||||
} else {
|
||||
return _indexStartKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only valid if hasEnd is true.
|
||||
*/
|
||||
- (id)indexEndValue {
|
||||
NSAssert([self hasEnd], @"Only valid if end has been set");
|
||||
return _indexEndValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only valid if hasEnd is true.
|
||||
* @return The end key name for the range defined by these query parameters
|
||||
*/
|
||||
- (NSString *)indexEndKey {
|
||||
NSAssert([self hasEnd], @"Only valid if end has been set");
|
||||
if (_indexEndKey == nil) {
|
||||
return [FUtilities maxName];
|
||||
} else {
|
||||
return _indexEndKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if a limit has been set and has been explicitly anchored
|
||||
*/
|
||||
- (BOOL)hasAnchoredLimit {
|
||||
return self.limitSet && self.viewFrom != nil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only valid to call if limitSet returns true
|
||||
*/
|
||||
- (NSInteger)limit {
|
||||
NSAssert(self.limitSet, @"Only valid if limit has been set");
|
||||
return _limit;
|
||||
}
|
||||
|
||||
- (BOOL)hasStart {
|
||||
return self->_indexStartValue != nil;
|
||||
}
|
||||
|
||||
- (BOOL)hasEnd {
|
||||
return self->_indexEndValue != nil;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
// Immutable
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)mutableCopy {
|
||||
FQueryParams *other = [[[self class] alloc] init];
|
||||
// Maybe need to do extra copying here
|
||||
other->_limitSet = _limitSet;
|
||||
other->_limit = _limit;
|
||||
other->_indexStartValue = _indexStartValue;
|
||||
other->_indexStartKey = _indexStartKey;
|
||||
other->_indexEndValue = _indexEndValue;
|
||||
other->_indexEndKey = _indexEndKey;
|
||||
other->_viewFrom = _viewFrom;
|
||||
other->_index = _index;
|
||||
return other;
|
||||
}
|
||||
|
||||
- (FQueryParams *)limitTo:(NSInteger)newLimit {
|
||||
FQueryParams *newParams = [self mutableCopy];
|
||||
newParams->_limitSet = YES;
|
||||
newParams->_limit = newLimit;
|
||||
newParams->_viewFrom = nil;
|
||||
return newParams;
|
||||
}
|
||||
|
||||
- (FQueryParams *)limitToFirst:(NSInteger)newLimit {
|
||||
FQueryParams *newParams = [self mutableCopy];
|
||||
newParams->_limitSet = YES;
|
||||
newParams->_limit = newLimit;
|
||||
newParams->_viewFrom = kFQPViewFromLeft;
|
||||
return newParams;
|
||||
}
|
||||
|
||||
- (FQueryParams *)limitToLast:(NSInteger)newLimit {
|
||||
FQueryParams *newParams = [self mutableCopy];
|
||||
newParams->_limitSet = YES;
|
||||
newParams->_limit = newLimit;
|
||||
newParams->_viewFrom = kFQPViewFromRight;
|
||||
return newParams;
|
||||
}
|
||||
|
||||
- (FQueryParams *)startAt:(id<FNode>)indexValue childKey:(NSString *)key {
|
||||
NSAssert([indexValue isLeafNode] || [indexValue isEmpty], nil);
|
||||
FQueryParams *newParams = [self mutableCopy];
|
||||
newParams->_indexStartValue = indexValue;
|
||||
newParams->_indexStartKey = key;
|
||||
return newParams;
|
||||
}
|
||||
|
||||
- (FQueryParams *)startAt:(id<FNode>)indexValue {
|
||||
return [self startAt:indexValue childKey:nil];
|
||||
}
|
||||
|
||||
- (FQueryParams *)endAt:(id<FNode>)indexValue childKey:(NSString *)key {
|
||||
NSAssert([indexValue isLeafNode] || [indexValue isEmpty], nil);
|
||||
FQueryParams *newParams = [self mutableCopy];
|
||||
newParams->_indexEndValue = indexValue;
|
||||
newParams->_indexEndKey = key;
|
||||
return newParams;
|
||||
}
|
||||
|
||||
- (FQueryParams *)endAt:(id<FNode>)indexValue {
|
||||
return [self endAt:indexValue childKey:nil];
|
||||
}
|
||||
|
||||
- (FQueryParams *)orderBy:(id)newIndex {
|
||||
FQueryParams *newParams = [self mutableCopy];
|
||||
newParams->_index = newIndex;
|
||||
return newParams;
|
||||
}
|
||||
|
||||
- (NSDictionary *)wireProtocolParams {
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
if ([self hasStart]) {
|
||||
[dict setObject:[self.indexStartValue valForExport:YES]
|
||||
forKey:kFQPIndexStartValue];
|
||||
|
||||
// Don't use property as it will be [MIN-NAME]
|
||||
if (self->_indexStartKey != nil) {
|
||||
[dict setObject:self->_indexStartKey forKey:kFQPIndexStartName];
|
||||
}
|
||||
}
|
||||
|
||||
if ([self hasEnd]) {
|
||||
[dict setObject:[self.indexEndValue valForExport:YES]
|
||||
forKey:kFQPIndexEndValue];
|
||||
|
||||
// Don't use property as it will be [MAX-NAME]
|
||||
if (self->_indexEndKey != nil) {
|
||||
[dict setObject:self->_indexEndKey forKey:kFQPIndexEndName];
|
||||
}
|
||||
}
|
||||
|
||||
if (self.limitSet) {
|
||||
[dict setObject:[NSNumber numberWithInteger:self.limit]
|
||||
forKey:kFQPLimit];
|
||||
NSString *vf = self.viewFrom;
|
||||
if (vf == nil) {
|
||||
// limit() rather than limitToFirst or limitToLast was called.
|
||||
// This means that only one of startSet or endSet is true. Use them
|
||||
// to calculate which side of the view to anchor to. If neither is
|
||||
// set, Anchor to end
|
||||
if ([self hasStart]) {
|
||||
vf = kFQPViewFromLeft;
|
||||
} else {
|
||||
vf = kFQPViewFromRight;
|
||||
}
|
||||
}
|
||||
[dict setObject:vf forKey:kFQPViewFrom];
|
||||
}
|
||||
|
||||
// For now, priority index is the default, so we only specify if it's some
|
||||
// other index.
|
||||
if (![self.index isEqual:[FPriorityIndex priorityIndex]]) {
|
||||
[dict setObject:[self.index queryDefinition] forKey:kFQPIndex];
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
+ (FQueryParams *)fromQueryObject:(NSDictionary *)dict {
|
||||
if (dict.count == 0) {
|
||||
return [FQueryParams defaultInstance];
|
||||
}
|
||||
|
||||
FQueryParams *params = [[FQueryParams alloc] init];
|
||||
if (dict[kFQPLimit] != nil) {
|
||||
params->_limitSet = YES;
|
||||
params->_limit = [dict[kFQPLimit] integerValue];
|
||||
}
|
||||
|
||||
if (dict[kFQPIndexStartValue] != nil) {
|
||||
params->_indexStartValue =
|
||||
[FSnapshotUtilities nodeFrom:dict[kFQPIndexStartValue]];
|
||||
if (dict[kFQPIndexStartName] != nil) {
|
||||
params->_indexStartKey = dict[kFQPIndexStartName];
|
||||
}
|
||||
}
|
||||
|
||||
if (dict[kFQPIndexEndValue] != nil) {
|
||||
params->_indexEndValue =
|
||||
[FSnapshotUtilities nodeFrom:dict[kFQPIndexEndValue]];
|
||||
if (dict[kFQPIndexEndName] != nil) {
|
||||
params->_indexEndKey = dict[kFQPIndexEndName];
|
||||
}
|
||||
}
|
||||
|
||||
if (dict[kFQPViewFrom] != nil) {
|
||||
NSString *viewFrom = dict[kFQPViewFrom];
|
||||
if (![viewFrom isEqualToString:kFQPViewFromLeft] &&
|
||||
![viewFrom isEqualToString:kFQPViewFromRight]) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Unknown view from paramter: %@", viewFrom];
|
||||
}
|
||||
params->_viewFrom = viewFrom;
|
||||
}
|
||||
|
||||
NSString *index = dict[kFQPIndex];
|
||||
if (index != nil) {
|
||||
params->_index = [FIndex indexFromQueryDefinition:index];
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
- (BOOL)isViewFromLeft {
|
||||
if (self.viewFrom != nil) {
|
||||
// Not null, we can just check
|
||||
return [self.viewFrom isEqualToString:kFQPViewFromLeft];
|
||||
} else {
|
||||
// If start is set, it's view from left. Otherwise not.
|
||||
return self.hasStart;
|
||||
}
|
||||
}
|
||||
|
||||
- (id<FNodeFilter>)nodeFilter {
|
||||
if (self.loadsAllData) {
|
||||
return [[FIndexedFilter alloc] initWithIndex:self.index];
|
||||
} else if (self.limitSet) {
|
||||
return [[FLimitedFilter alloc] initWithQueryParams:self];
|
||||
} else {
|
||||
return [[FRangedFilter alloc] initWithQueryParams:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isValid {
|
||||
return !(self.hasStart && self.hasEnd && self.limitSet &&
|
||||
!self.hasAnchoredLimit);
|
||||
}
|
||||
|
||||
- (BOOL)loadsAllData {
|
||||
return !(self.hasStart || self.hasEnd || self.limitSet);
|
||||
}
|
||||
|
||||
- (BOOL)isDefault {
|
||||
return [self loadsAllData] &&
|
||||
[self.index isEqual:[FPriorityIndex priorityIndex]];
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [[self wireProtocolParams] description];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)obj {
|
||||
if (self == obj) {
|
||||
return YES;
|
||||
}
|
||||
if (![obj isKindOfClass:[self class]]) {
|
||||
return NO;
|
||||
}
|
||||
FQueryParams *other = (FQueryParams *)obj;
|
||||
if (self->_limitSet != other->_limitSet)
|
||||
return NO;
|
||||
if (self->_limit != other->_limit)
|
||||
return NO;
|
||||
if ((self->_index != other->_index) &&
|
||||
![self->_index isEqual:other->_index])
|
||||
return NO;
|
||||
if ((self->_indexStartKey != other->_indexStartKey) &&
|
||||
![self->_indexStartKey isEqualToString:other->_indexStartKey])
|
||||
return NO;
|
||||
if ((self->_indexStartValue != other->_indexStartValue) &&
|
||||
![self->_indexStartValue isEqual:other->_indexStartValue])
|
||||
return NO;
|
||||
if ((self->_indexEndKey != other->_indexEndKey) &&
|
||||
![self->_indexEndKey isEqualToString:other->_indexEndKey])
|
||||
return NO;
|
||||
if ((self->_indexEndValue != other->_indexEndValue) &&
|
||||
![self->_indexEndValue isEqual:other->_indexEndValue])
|
||||
return NO;
|
||||
if ([self isViewFromLeft] != [other isViewFromLeft])
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
NSUInteger result = _limitSet ? _limit : 0;
|
||||
result = 31 * result + ([self isViewFromLeft] ? 1231 : 1237);
|
||||
result = 31 * result + [_indexStartKey hash];
|
||||
result = 31 * result + [_indexStartValue hash];
|
||||
result = 31 * result + [_indexEndKey hash];
|
||||
result = 31 * result + [_indexEndValue hash];
|
||||
result = 31 * result + [_index hash];
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FQueryParams.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/FIndex.h"
|
||||
|
||||
@interface FQuerySpec : NSObject <NSCopying>
|
||||
|
||||
@property(nonatomic, strong, readonly) FPath *path;
|
||||
@property(nonatomic, strong, readonly) FQueryParams *params;
|
||||
|
||||
- (id)initWithPath:(FPath *)path params:(FQueryParams *)params;
|
||||
|
||||
+ (FQuerySpec *)defaultQueryAtPath:(FPath *)path;
|
||||
|
||||
- (id<FIndex>)index;
|
||||
- (BOOL)isDefault;
|
||||
- (BOOL)loadsAllData;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FQuerySpec.h"
|
||||
|
||||
@interface FQuerySpec ()
|
||||
|
||||
@property(nonatomic, strong, readwrite) FPath *path;
|
||||
@property(nonatomic, strong, readwrite) FQueryParams *params;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FQuerySpec
|
||||
|
||||
- (id)initWithPath:(FPath *)path params:(FQueryParams *)params {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
self->_path = path;
|
||||
self->_params = params;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (FQuerySpec *)defaultQueryAtPath:(FPath *)path {
|
||||
return [[FQuerySpec alloc] initWithPath:path
|
||||
params:[FQueryParams defaultInstance]];
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
// Immutable
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id<FIndex>)index {
|
||||
return self.params.index;
|
||||
}
|
||||
|
||||
- (BOOL)isDefault {
|
||||
return self.params.isDefault;
|
||||
}
|
||||
|
||||
- (BOOL)loadsAllData {
|
||||
return self.params.loadsAllData;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if (self == object) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (![object isKindOfClass:[FQuerySpec class]]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
FQuerySpec *other = (FQuerySpec *)object;
|
||||
|
||||
if (![self.path isEqual:other.path]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [self.params isEqual:other.params];
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
return self.path.hash * 31 + self.params.hash;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"FQuerySpec (path: %@, params: %@)",
|
||||
self.path, self.params];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -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 <Foundation/Foundation.h>
|
||||
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
|
||||
/**
|
||||
* Applies a merge of a snap for a given interval of paths.
|
||||
* Each leaf in the current node which the relative path lies *after* (the
|
||||
* optional) start and lies *before or at* (the optional) end will be deleted.
|
||||
* Each leaf in snap that lies in the interval will be added to the resulting
|
||||
* node. Nodes outside of the range are ignored. nil for start and end are
|
||||
* sentinel values that represent -infinity and +infinity respectively (aka
|
||||
* includes any path). Priorities of children nodes are treated as leaf children
|
||||
* of that node.
|
||||
*/
|
||||
@interface FRangeMerge : NSObject
|
||||
|
||||
- (instancetype)initWithStart:(FPath *)start
|
||||
end:(FPath *)end
|
||||
updates:(id<FNode>)updates;
|
||||
|
||||
- (id<FNode>)applyToNode:(id<FNode>)node;
|
||||
|
||||
@end
|
||||
@@ -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 "FirebaseDatabase/Sources/Core/FRangeMerge.h"
|
||||
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FEmptyNode.h"
|
||||
|
||||
@interface FRangeMerge ()
|
||||
|
||||
@property(nonatomic, strong) FPath *optExclusiveStart;
|
||||
@property(nonatomic, strong) FPath *optInclusiveEnd;
|
||||
@property(nonatomic, strong) id<FNode> updates;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FRangeMerge
|
||||
|
||||
- (instancetype)initWithStart:(FPath *)start
|
||||
end:(FPath *)end
|
||||
updates:(id<FNode>)updates {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
self->_optExclusiveStart = start;
|
||||
self->_optInclusiveEnd = end;
|
||||
self->_updates = updates;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id<FNode>)applyToNode:(id<FNode>)node {
|
||||
return [self updateRangeInNode:[FPath empty]
|
||||
node:node
|
||||
updates:self.updates];
|
||||
}
|
||||
|
||||
- (id<FNode>)updateRangeInNode:(FPath *)currentPath
|
||||
node:(id<FNode>)node
|
||||
updates:(id<FNode>)updates {
|
||||
NSComparisonResult startComparison =
|
||||
(self.optExclusiveStart == nil)
|
||||
? NSOrderedDescending
|
||||
: [currentPath compare:self.optExclusiveStart];
|
||||
NSComparisonResult endComparison =
|
||||
(self.optInclusiveEnd == nil)
|
||||
? NSOrderedAscending
|
||||
: [currentPath compare:self.optInclusiveEnd];
|
||||
BOOL startInNode = self.optExclusiveStart != nil &&
|
||||
[currentPath contains:self.optExclusiveStart];
|
||||
BOOL endInNode = self.optInclusiveEnd != nil &&
|
||||
[currentPath contains:self.optInclusiveEnd];
|
||||
if (startComparison == NSOrderedDescending &&
|
||||
endComparison == NSOrderedAscending && !endInNode) {
|
||||
// child is completly contained
|
||||
return updates;
|
||||
} else if (startComparison == NSOrderedDescending && endInNode &&
|
||||
[updates isLeafNode]) {
|
||||
return updates;
|
||||
} else if (startComparison == NSOrderedDescending &&
|
||||
endComparison == NSOrderedSame) {
|
||||
NSAssert(endInNode, @"End not in node");
|
||||
NSAssert(![updates isLeafNode], @"Found leaf node update, this case "
|
||||
@"should have been handled above.");
|
||||
if ([node isLeafNode]) {
|
||||
// Update node was not a leaf node, so we can delete it
|
||||
return [FEmptyNode emptyNode];
|
||||
} else {
|
||||
// Unaffected by range, ignore
|
||||
return node;
|
||||
}
|
||||
} else if (startInNode || endInNode) {
|
||||
// There is a partial update we need to do, so collect all relevant
|
||||
// children
|
||||
NSMutableSet *allChildren = [NSMutableSet set];
|
||||
[node enumerateChildrenUsingBlock:^(NSString *key, id<FNode> node,
|
||||
BOOL *stop) {
|
||||
[allChildren addObject:key];
|
||||
}];
|
||||
[updates enumerateChildrenUsingBlock:^(NSString *key, id<FNode> node,
|
||||
BOOL *stop) {
|
||||
[allChildren addObject:key];
|
||||
}];
|
||||
|
||||
__block id<FNode> newNode = node;
|
||||
void (^action)(id, BOOL *) = ^void(NSString *key, BOOL *stop) {
|
||||
id<FNode> currentChild = [node getImmediateChild:key];
|
||||
id<FNode> updatedChild =
|
||||
[self updateRangeInNode:[currentPath childFromString:key]
|
||||
node:currentChild
|
||||
updates:[updates getImmediateChild:key]];
|
||||
// Only need to update if the node changed
|
||||
if (updatedChild != currentChild) {
|
||||
newNode = [newNode updateImmediateChild:key
|
||||
withNewChild:updatedChild];
|
||||
}
|
||||
};
|
||||
|
||||
[allChildren enumerateObjectsUsingBlock:action];
|
||||
|
||||
// Add priority last, so the node is not empty when applying
|
||||
if (!updates.getPriority.isEmpty || !node.getPriority.isEmpty) {
|
||||
BOOL stop = NO;
|
||||
action(@".priority", &stop);
|
||||
}
|
||||
return newNode;
|
||||
} else {
|
||||
// Unaffected by this range
|
||||
NSAssert(endComparison == NSOrderedDescending ||
|
||||
startComparison <= NSOrderedSame,
|
||||
@"Invalid range for update");
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"RangeMerge (optExclusiveStart = %@, "
|
||||
@"optExclusiveEng = %@, updates = %@)",
|
||||
self.optExclusiveStart,
|
||||
self.optInclusiveEnd, self.updates];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FPersistentConnection.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FRepoInfo.h"
|
||||
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDataEventType.h"
|
||||
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabaseQuery.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/Tuples/FTupleUserCallback.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class FQuerySpec;
|
||||
@class FPersistence;
|
||||
@class FAuthenticationManager;
|
||||
@class FIRDatabaseConfig;
|
||||
@protocol FEventRegistration;
|
||||
@class FCompoundWrite;
|
||||
@protocol FClock;
|
||||
@class FIRDatabase;
|
||||
|
||||
@interface FRepo : NSObject <FPersistentConnectionDelegate>
|
||||
|
||||
@property(nonatomic, strong) FIRDatabaseConfig *_Nullable config;
|
||||
|
||||
- (id _Nonnull)initWithRepoInfo:(FRepoInfo *_Nullable)info
|
||||
config:(FIRDatabaseConfig *_Nullable)config
|
||||
database:(FIRDatabase *_Nullable)database;
|
||||
|
||||
- (void)set:(FPath *_Nullable)path
|
||||
withNode:(id _Nullable)node
|
||||
withCallback:(fbt_void_nserror_ref _Nullable)onComplete;
|
||||
- (void)update:(FPath *_Nullable)path
|
||||
withNodes:(FCompoundWrite *_Nullable)compoundWrite
|
||||
withCallback:(fbt_void_nserror_ref _Nullable)callback;
|
||||
- (void)purgeOutstandingWrites;
|
||||
|
||||
- (void)getData:(FIRDatabaseQuery *_Nullable)query
|
||||
withCompletionBlock:
|
||||
(void (^_Nonnull)(NSError *_Nullable error,
|
||||
FIRDataSnapshot *_Nullable snapshot))block;
|
||||
|
||||
- (void)addEventRegistration:(id<FEventRegistration> _Nullable)eventRegistration
|
||||
forQuery:(FQuerySpec *_Nullable)query;
|
||||
- (void)removeEventRegistration:
|
||||
(id<FEventRegistration> _Nullable)eventRegistration
|
||||
forQuery:(FQuerySpec *_Nullable)query;
|
||||
- (void)keepQuery:(FQuerySpec *_Nullable)query synced:(BOOL)synced;
|
||||
|
||||
- (NSString *_Nullable)name;
|
||||
- (NSTimeInterval)serverTime;
|
||||
|
||||
- (void)onDataUpdate:(FPersistentConnection *_Nullable)fpconnection
|
||||
forPath:(NSString *_Nullable)pathString
|
||||
message:(id _Nullable)message
|
||||
isMerge:(BOOL)isMerge
|
||||
tagId:(NSNumber *_Nullable)tagId;
|
||||
- (void)onConnect:(FPersistentConnection *_Nullable)fpconnection;
|
||||
- (void)onDisconnect:(FPersistentConnection *_Nullable)fpconnection;
|
||||
|
||||
// Disconnect methods
|
||||
- (void)onDisconnectCancel:(FPath *_Nullable)path
|
||||
withCallback:(fbt_void_nserror_ref _Nullable)callback;
|
||||
- (void)onDisconnectSet:(FPath *_Nullable)path
|
||||
withNode:(id<FNode> _Nullable)node
|
||||
withCallback:(fbt_void_nserror_ref _Nullable)callback;
|
||||
- (void)onDisconnectUpdate:(FPath *_Nullable)path
|
||||
withNodes:(FCompoundWrite *_Nullable)compoundWrite
|
||||
withCallback:(fbt_void_nserror_ref _Nullable)callback;
|
||||
|
||||
// Connection Management.
|
||||
- (void)interrupt;
|
||||
- (void)resume;
|
||||
|
||||
// Transactions
|
||||
- (void)startTransactionOnPath:(FPath *_Nullable)path
|
||||
update:
|
||||
(fbt_transactionresult_mutabledata _Nullable)update
|
||||
onComplete:
|
||||
(fbt_void_nserror_bool_datasnapshot _Nullable)onComplete
|
||||
withLocalEvents:(BOOL)applyLocally;
|
||||
|
||||
// Testing methods
|
||||
- (NSDictionary *_Nullable)dumpListens;
|
||||
- (void)dispose;
|
||||
- (void)setHijackHash:(BOOL)hijack;
|
||||
|
||||
@property(nonatomic, strong, readonly) FAuthenticationManager *_Nullable auth;
|
||||
@property(nonatomic, strong, readonly) FIRDatabase *_Nullable database;
|
||||
|
||||
@end
|
||||
+1542
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface FRepoInfo : NSObject <NSCopying>
|
||||
|
||||
/// The host that the database should connect to.
|
||||
@property(nonatomic, readonly, copy) NSString *host;
|
||||
|
||||
@property(nonatomic, readonly, copy) NSString *namespace;
|
||||
@property(nonatomic, readwrite, copy) NSString *internalHost;
|
||||
@property(nonatomic, readonly, assign) BOOL secure;
|
||||
|
||||
/// Returns YES if the host is not a *.firebaseio.com host.
|
||||
@property(nonatomic, readonly) BOOL isCustomHost;
|
||||
|
||||
- (instancetype)initWithHost:(NSString *)host
|
||||
isSecure:(BOOL)secure
|
||||
withNamespace:(NSString *)namespace NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)initWithInfo:(FRepoInfo *)info emulatedHost:(NSString *)host;
|
||||
|
||||
- (NSString *)connectionURLWithLastSessionID:(NSString *_Nullable)lastSessionID;
|
||||
- (NSString *)connectionURL;
|
||||
- (void)clearInternalHostCache;
|
||||
- (BOOL)isDemoHost;
|
||||
- (BOOL)isCustomHost;
|
||||
|
||||
- (id)copyWithZone:(NSZone *_Nullable)zone;
|
||||
- (NSUInteger)hash;
|
||||
- (BOOL)isEqual:(id)anObject;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FRepoInfo.h"
|
||||
#import "FirebaseDatabase/Sources/Constants/FConstants.h"
|
||||
|
||||
@interface FRepoInfo ()
|
||||
|
||||
@property(nonatomic, strong) NSString *domain;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FRepoInfo
|
||||
|
||||
@synthesize internalHost;
|
||||
|
||||
- (instancetype)init {
|
||||
[NSException
|
||||
raise:@"FIRDatabaseInvalidInitializer"
|
||||
format:@"Invalid initializer invoked. This is probably a bug in RTDB."];
|
||||
abort();
|
||||
}
|
||||
|
||||
- (instancetype)initWithHost:(NSString *)aHost
|
||||
isSecure:(BOOL)isSecure
|
||||
withNamespace:(NSString *)aNamespace {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_host = [aHost copy];
|
||||
_domain =
|
||||
[_host containsString:@"."]
|
||||
? [_host
|
||||
substringFromIndex:[_host rangeOfString:@"."].location +
|
||||
1]
|
||||
: _host;
|
||||
_secure = isSecure;
|
||||
_namespace = aNamespace;
|
||||
|
||||
// Get cached internal host if it exists
|
||||
NSString *internalHostKey =
|
||||
[NSString stringWithFormat:@"firebase:host:%@", _host];
|
||||
NSString *cachedInternalHost = [[NSUserDefaults standardUserDefaults]
|
||||
stringForKey:internalHostKey];
|
||||
if (cachedInternalHost != nil) {
|
||||
internalHost = cachedInternalHost;
|
||||
} else {
|
||||
internalHost = [_host copy];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithInfo:(FRepoInfo *)info emulatedHost:(NSString *)host {
|
||||
self = [self initWithHost:host isSecure:NO withNamespace:info.namespace];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
// The namespace is encoded in the hostname, so we can just return this.
|
||||
return [NSString
|
||||
stringWithFormat:@"http%@://%@", (_secure ? @"s" : @""), _host];
|
||||
}
|
||||
|
||||
- (void)setInternalHost:(NSString *)newHost {
|
||||
if (![internalHost isEqualToString:newHost]) {
|
||||
internalHost = newHost;
|
||||
|
||||
// Cache the internal host so we don't need to redirect later on
|
||||
NSString *internalHostKey =
|
||||
[NSString stringWithFormat:@"firebase:host:%@", self.host];
|
||||
NSUserDefaults *cache = [NSUserDefaults standardUserDefaults];
|
||||
[cache setObject:internalHost forKey:internalHostKey];
|
||||
[cache synchronize];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clearInternalHostCache {
|
||||
self.internalHost = self.host;
|
||||
|
||||
// Remove the cached entry
|
||||
NSString *internalHostKey =
|
||||
[NSString stringWithFormat:@"firebase:host:%@", self.host];
|
||||
NSUserDefaults *cache = [NSUserDefaults standardUserDefaults];
|
||||
[cache removeObjectForKey:internalHostKey];
|
||||
[cache synchronize];
|
||||
}
|
||||
|
||||
- (BOOL)isDemoHost {
|
||||
return [self.domain isEqualToString:@"firebaseio-demo.com"];
|
||||
}
|
||||
|
||||
- (BOOL)isCustomHost {
|
||||
return ![self.domain isEqualToString:@"firebaseio-demo.com"] &&
|
||||
![self.domain isEqualToString:@"firebaseio.com"];
|
||||
}
|
||||
|
||||
- (NSString *)connectionURL {
|
||||
return [self connectionURLWithLastSessionID:nil];
|
||||
}
|
||||
|
||||
- (NSString *)connectionURLWithLastSessionID:(NSString *)lastSessionID {
|
||||
NSString *scheme;
|
||||
if (self.secure) {
|
||||
scheme = @"wss";
|
||||
} else {
|
||||
scheme = @"ws";
|
||||
}
|
||||
NSString *url =
|
||||
[NSString stringWithFormat:@"%@://%@/.ws?%@=%@&ns=%@", scheme,
|
||||
self.internalHost, kWireProtocolVersionParam,
|
||||
kWebsocketProtocolVersion, self.namespace];
|
||||
|
||||
if (lastSessionID != nil) {
|
||||
url = [NSString stringWithFormat:@"%@&ls=%@", url, lastSessionID];
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
return self; // Immutable
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
NSUInteger result = _host.hash;
|
||||
result = 31 * result + (_secure ? 1 : 0);
|
||||
result = 31 * result + _namespace.hash;
|
||||
result = 31 * result + _host.hash;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)anObject {
|
||||
if (![anObject isKindOfClass:[FRepoInfo class]]) {
|
||||
return NO;
|
||||
}
|
||||
FRepoInfo *other = (FRepoInfo *)anObject;
|
||||
return _secure == other.secure && [_host isEqualToString:other.host] &&
|
||||
[_namespace isEqualToString:other.namespace];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Api/FIRDatabaseConfig.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FRepo.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FRepoInfo.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FRepoManager : NSObject
|
||||
|
||||
+ (FRepo *)getRepo:(FRepoInfo *)repoInfo config:(FIRDatabaseConfig *)config;
|
||||
+ (FRepo *)createRepo:(FRepoInfo *)repoInfo
|
||||
config:(FIRDatabaseConfig *)config
|
||||
database:(FIRDatabase *)database;
|
||||
+ (void)interruptAll;
|
||||
+ (void)interrupt:(FIRDatabaseConfig *)config;
|
||||
+ (void)resumeAll;
|
||||
+ (void)resume:(FIRDatabaseConfig *)config;
|
||||
+ (void)disposeRepos:(FIRDatabaseConfig *)config;
|
||||
|
||||
@end
|
||||
@@ -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 "FirebaseDatabase/Sources/Core/FRepoManager.h"
|
||||
#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
|
||||
#import "FirebaseDatabase/Sources/Api/Private/FIRDatabaseQuery_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Api/Private/FIRDatabase_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FRepo.h"
|
||||
#import "FirebaseDatabase/Sources/FIRDatabaseConfig_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FAtomicNumber.h"
|
||||
|
||||
@implementation FRepoManager
|
||||
|
||||
typedef NSMutableDictionary<NSString *,
|
||||
NSMutableDictionary<FRepoInfo *, FRepo *> *>
|
||||
FRepoDictionary;
|
||||
|
||||
+ (FRepoDictionary *)configs {
|
||||
static dispatch_once_t pred = 0;
|
||||
static FRepoDictionary *configs;
|
||||
dispatch_once(&pred, ^{
|
||||
configs = [NSMutableDictionary dictionary];
|
||||
});
|
||||
return configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for legacy unit tests. The public API should go through
|
||||
* FirebaseDatabase which calls createRepo.
|
||||
*/
|
||||
+ (FRepo *)getRepo:(FRepoInfo *)repoInfo config:(FIRDatabaseConfig *)config {
|
||||
[config freeze];
|
||||
FRepoDictionary *configs = [FRepoManager configs];
|
||||
@synchronized(configs) {
|
||||
NSMutableDictionary<FRepoInfo *, FRepo *> *repos =
|
||||
configs[config.sessionIdentifier];
|
||||
if (!repos || repos[repoInfo] == nil) {
|
||||
// Calling this should create the repo.
|
||||
[FIRDatabase createDatabaseForTests:repoInfo config:config];
|
||||
}
|
||||
|
||||
return configs[config.sessionIdentifier][repoInfo];
|
||||
}
|
||||
}
|
||||
|
||||
+ (FRepo *)createRepo:(FRepoInfo *)repoInfo
|
||||
config:(FIRDatabaseConfig *)config
|
||||
database:(FIRDatabase *)database {
|
||||
[config freeze];
|
||||
FRepoDictionary *configs = [FRepoManager configs];
|
||||
@synchronized(configs) {
|
||||
NSMutableDictionary<FRepoInfo *, FRepo *> *repos =
|
||||
configs[config.sessionIdentifier];
|
||||
if (!repos) {
|
||||
repos = [NSMutableDictionary dictionary];
|
||||
configs[config.sessionIdentifier] = repos;
|
||||
}
|
||||
FRepo *repo = repos[repoInfo];
|
||||
if (repo == nil) {
|
||||
repo = [[FRepo alloc] initWithRepoInfo:repoInfo
|
||||
config:config
|
||||
database:database];
|
||||
repos[repoInfo] = repo;
|
||||
return repo;
|
||||
} else {
|
||||
[NSException
|
||||
raise:@"RepoExists"
|
||||
format:@"createRepo called for Repo that already exists."];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)interrupt:(FIRDatabaseConfig *)config {
|
||||
dispatch_async([FIRDatabaseQuery sharedQueue], ^{
|
||||
FRepoDictionary *configs = [FRepoManager configs];
|
||||
NSMutableDictionary<FRepoInfo *, FRepo *> *repos =
|
||||
configs[config.sessionIdentifier];
|
||||
for (FRepo *repo in [repos allValues]) {
|
||||
[repo interrupt];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)interruptAll {
|
||||
dispatch_async([FIRDatabaseQuery sharedQueue], ^{
|
||||
FRepoDictionary *configs = [FRepoManager configs];
|
||||
for (NSMutableDictionary<FRepoInfo *, FRepo *> *repos in
|
||||
[configs allValues]) {
|
||||
for (FRepo *repo in [repos allValues]) {
|
||||
[repo interrupt];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)resume:(FIRDatabaseConfig *)config {
|
||||
dispatch_async([FIRDatabaseQuery sharedQueue], ^{
|
||||
FRepoDictionary *configs = [FRepoManager configs];
|
||||
NSMutableDictionary<FRepoInfo *, FRepo *> *repos =
|
||||
configs[config.sessionIdentifier];
|
||||
for (FRepo *repo in [repos allValues]) {
|
||||
[repo resume];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)resumeAll {
|
||||
dispatch_async([FIRDatabaseQuery sharedQueue], ^{
|
||||
FRepoDictionary *configs = [FRepoManager configs];
|
||||
for (NSMutableDictionary<FRepoInfo *, FRepo *> *repos in
|
||||
[configs allValues]) {
|
||||
for (FRepo *repo in [repos allValues]) {
|
||||
[repo resume];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)disposeRepos:(FIRDatabaseConfig *)config {
|
||||
// Do this synchronously to make sure we release our references to LevelDB
|
||||
// before returning, allowing LevelDB to close and release its exclusive
|
||||
// locks.
|
||||
dispatch_sync([FIRDatabaseQuery sharedQueue], ^{
|
||||
FFLog(@"I-RDB040001", @"Disposing all repos for Config with name %@",
|
||||
config.sessionIdentifier);
|
||||
NSMutableDictionary *configs = [FRepoManager configs];
|
||||
for (FRepo *repo in [configs[config.sessionIdentifier] allValues]) {
|
||||
[repo dispose];
|
||||
}
|
||||
[configs removeObjectForKey:config.sessionIdentifier];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FRepo.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FSparseSnapshotTree.h"
|
||||
|
||||
@class FSyncTree;
|
||||
@class FAtomicNumber;
|
||||
@class FEventRaiser;
|
||||
@class FSnapshotHolder;
|
||||
|
||||
@interface FRepo ()
|
||||
|
||||
- (void)runOnDisconnectEvents;
|
||||
|
||||
@property(nonatomic, strong) FRepoInfo *repoInfo;
|
||||
@property(nonatomic, strong) FPersistentConnection *connection;
|
||||
@property(nonatomic, strong) FSnapshotHolder *infoData;
|
||||
@property(nonatomic, strong) FSparseSnapshotTree *onDisconnect;
|
||||
@property(nonatomic, strong) FEventRaiser *eventRaiser;
|
||||
@property(nonatomic, strong) FSyncTree *serverSyncTree;
|
||||
|
||||
// For testing.
|
||||
@property(nonatomic) long dataUpdateCount;
|
||||
@property(nonatomic) long rangeMergeUpdateCount;
|
||||
|
||||
- (NSInteger)nextWriteId;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FSparseSnapshotTree.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FSyncTree.h"
|
||||
#import "FirebaseDatabase/Sources/FClock.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FCompoundWrite.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FServerValues : NSObject
|
||||
|
||||
+ (NSDictionary *)generateServerValues:(id<FClock>)clock;
|
||||
|
||||
+ (FCompoundWrite *)resolveDeferredValueCompoundWrite:(FCompoundWrite *)write
|
||||
withSyncTree:(FSyncTree *)tree
|
||||
atPath:(FPath *)path
|
||||
serverValues:
|
||||
(NSDictionary *)serverValues;
|
||||
+ (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
|
||||
withSyncTree:(FSyncTree *)existing
|
||||
atPath:(FPath *)path
|
||||
serverValues:(NSDictionary *)serverValues;
|
||||
+ (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
|
||||
withExisting:(id<FNode>)existing
|
||||
serverValues:(NSDictionary *)serverValues;
|
||||
@end
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FServerValues.h"
|
||||
#import "FirebaseDatabase/Sources/Constants/FConstants.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FChildrenNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FLeafNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FSnapshotUtilities.h"
|
||||
|
||||
const NSString *kTimestamp = @"timestamp";
|
||||
const NSString *kIncrement = @"increment";
|
||||
|
||||
BOOL canBeRepresentedAsLong(NSNumber *num) {
|
||||
switch (num.objCType[0]) {
|
||||
case 'f': // float; fallthrough
|
||||
case 'd': // double
|
||||
return NO;
|
||||
case 'L': // unsigned long; fallthrough
|
||||
case 'Q': // unsigned long long; fallthrough
|
||||
// Only use ulong(long) if there isn't an overflow.
|
||||
if (num.unsignedLongLongValue > LONG_MAX) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Running through CompoundWrites for all update paths has been shown to
|
||||
// be a 20% pessimization in microbenchmarks. This is because it slows
|
||||
// down by O(N) of the write queue length. To eliminate the performance
|
||||
// hit, we wrap around existing data of either snapshot or CompoundWrite
|
||||
// (allowing us to share code) and read from the CompoundWrite only when/where
|
||||
// we need to calculate an incremented value's prior state.
|
||||
@protocol ValueProvider <NSObject>
|
||||
- (id<ValueProvider>)getChild:(NSString *)pathSegment;
|
||||
- (id<FNode>)value;
|
||||
@end
|
||||
|
||||
@interface DeferredValueProvider : NSObject <ValueProvider>
|
||||
- (instancetype)initWithSyncTree:(FSyncTree *)tree atPath:(FPath *)path;
|
||||
- (id<ValueProvider>)getChild:(NSString *)pathSegment;
|
||||
- (id<FNode>)value;
|
||||
@property FPath *path;
|
||||
@property FSyncTree *tree;
|
||||
@end
|
||||
|
||||
@interface ExistingValueProvider : NSObject <ValueProvider>
|
||||
- (instancetype)initWithSnapshot:(id<FNode>)snapshot;
|
||||
- (id<ValueProvider>)getChild:(NSString *)pathSegment;
|
||||
- (id<FNode>)value;
|
||||
@property id<FNode> snapshot;
|
||||
@end
|
||||
|
||||
@implementation DeferredValueProvider
|
||||
- (instancetype)initWithSyncTree:(FSyncTree *)tree atPath:(FPath *)path {
|
||||
self.tree = tree;
|
||||
self.path = path;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id<ValueProvider>)getChild:(NSString *)pathSegment {
|
||||
FPath *child = [self.path childFromString:pathSegment];
|
||||
return [[DeferredValueProvider alloc] initWithSyncTree:self.tree
|
||||
atPath:child];
|
||||
}
|
||||
|
||||
- (id<FNode>)value {
|
||||
return [self.tree calcCompleteEventCacheAtPath:self.path
|
||||
excludeWriteIds:@[]];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ExistingValueProvider
|
||||
- (instancetype)initWithSnapshot:(id<FNode>)snapshot {
|
||||
self.snapshot = snapshot;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id<ValueProvider>)getChild:(NSString *)pathSegment {
|
||||
return [[ExistingValueProvider alloc]
|
||||
initWithSnapshot:[self.snapshot getImmediateChild:pathSegment]];
|
||||
}
|
||||
|
||||
- (id<FNode>)value {
|
||||
return self.snapshot;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface FServerValues ()
|
||||
+ (id)resolveScalarServerOp:(NSString *)op
|
||||
withServerValues:(NSDictionary *)serverValues;
|
||||
+ (id)resolveComplexServerOp:(NSDictionary *)op
|
||||
withValueProvider:(id<ValueProvider>)existing
|
||||
serverValues:(NSDictionary *)serverValues;
|
||||
+ (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
|
||||
withValueProvider:(id<ValueProvider>)existing
|
||||
serverValues:(NSDictionary *)serverValues;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FServerValues
|
||||
|
||||
+ (NSDictionary *)generateServerValues:(id<FClock>)clock {
|
||||
long long millis = (long long)([clock currentTime] * 1000);
|
||||
return @{kTimestamp : [NSNumber numberWithLongLong:millis]};
|
||||
}
|
||||
|
||||
+ (id)resolveDeferredValue:(id)val
|
||||
withExisting:(id<ValueProvider>)existing
|
||||
serverValues:(NSDictionary *)serverValues {
|
||||
if (![val isKindOfClass:[NSDictionary class]]) {
|
||||
return val;
|
||||
}
|
||||
NSDictionary *dict = val;
|
||||
id op = dict[kServerValueSubKey];
|
||||
|
||||
if (op == nil) {
|
||||
return val;
|
||||
} else if ([op isKindOfClass:NSString.class]) {
|
||||
return [FServerValues resolveScalarServerOp:op
|
||||
withServerValues:serverValues];
|
||||
} else if ([op isKindOfClass:NSDictionary.class]) {
|
||||
return [FServerValues resolveComplexServerOp:op
|
||||
withValueProvider:existing
|
||||
serverValues:serverValues];
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
+ (id)resolveScalarServerOp:(NSString *)op
|
||||
withServerValues:(NSDictionary *)serverValues {
|
||||
return serverValues[op];
|
||||
}
|
||||
|
||||
+ (id)resolveComplexServerOp:(NSDictionary *)op
|
||||
withValueProvider:(id<ValueProvider>)jitExisting
|
||||
serverValues:(NSDictionary *)serverValues {
|
||||
// Only increment is supported as of now
|
||||
if (op[kIncrement] == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Incrementing a non-number sets the value to the incremented amount
|
||||
NSNumber *delta = op[kIncrement];
|
||||
id<FNode> existing = jitExisting.value;
|
||||
if (![existing isLeafNode]) {
|
||||
return delta;
|
||||
}
|
||||
FLeafNode *existingLeaf = existing;
|
||||
if (![existingLeaf.value isKindOfClass:NSNumber.class]) {
|
||||
return delta;
|
||||
}
|
||||
|
||||
NSNumber *existingNum = existingLeaf.value;
|
||||
BOOL incrLong = canBeRepresentedAsLong(delta);
|
||||
BOOL baseLong = canBeRepresentedAsLong(existingNum);
|
||||
|
||||
if (incrLong && baseLong) {
|
||||
long x = delta.longValue;
|
||||
long y = existingNum.longValue;
|
||||
long r = x + y;
|
||||
|
||||
// See "Hacker's Delight" 2-12: Overflow if both arguments have the
|
||||
// opposite sign of the result
|
||||
if (((x ^ r) & (y ^ r)) >= 0) {
|
||||
return @(r);
|
||||
}
|
||||
}
|
||||
return @(delta.doubleValue + existingNum.doubleValue);
|
||||
}
|
||||
|
||||
+ (FCompoundWrite *)resolveDeferredValueCompoundWrite:(FCompoundWrite *)write
|
||||
withSyncTree:(FSyncTree *)tree
|
||||
atPath:(FPath *)path
|
||||
serverValues:
|
||||
(NSDictionary *)serverValues {
|
||||
__block FCompoundWrite *resolved = write;
|
||||
[write enumerateWrites:^(FPath *subPath, id<FNode> node, BOOL *stop) {
|
||||
id<ValueProvider> existing =
|
||||
[[DeferredValueProvider alloc] initWithSyncTree:tree
|
||||
atPath:[path child:subPath]];
|
||||
id<FNode> resolvedNode =
|
||||
[FServerValues resolveDeferredValueSnapshot:node
|
||||
withValueProvider:existing
|
||||
serverValues:serverValues];
|
||||
// Node actually changed, use pointer inequality here
|
||||
if (resolvedNode != node) {
|
||||
resolved = [resolved addWrite:resolvedNode atPath:subPath];
|
||||
}
|
||||
}];
|
||||
return resolved;
|
||||
}
|
||||
|
||||
+ (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
|
||||
withSyncTree:(FSyncTree *)tree
|
||||
atPath:(FPath *)path
|
||||
serverValues:(NSDictionary *)serverValues {
|
||||
id<ValueProvider> jitExisting =
|
||||
[[DeferredValueProvider alloc] initWithSyncTree:tree atPath:path];
|
||||
return [FServerValues resolveDeferredValueSnapshot:node
|
||||
withValueProvider:jitExisting
|
||||
serverValues:serverValues];
|
||||
}
|
||||
|
||||
+ (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
|
||||
withExisting:(id<FNode>)existing
|
||||
serverValues:(NSDictionary *)serverValues {
|
||||
id<ValueProvider> jitExisting =
|
||||
[[ExistingValueProvider alloc] initWithSnapshot:existing];
|
||||
return [FServerValues resolveDeferredValueSnapshot:node
|
||||
withValueProvider:jitExisting
|
||||
serverValues:serverValues];
|
||||
}
|
||||
|
||||
+ (id<FNode>)resolveDeferredValueSnapshot:(id<FNode>)node
|
||||
withValueProvider:(id<ValueProvider>)existing
|
||||
serverValues:(NSDictionary *)serverValues {
|
||||
id priorityVal =
|
||||
[FServerValues resolveDeferredValue:[[node getPriority] val]
|
||||
withExisting:[existing getChild:@".priority"]
|
||||
serverValues:serverValues];
|
||||
id<FNode> priority = [FSnapshotUtilities nodeFrom:priorityVal];
|
||||
|
||||
if ([node isLeafNode]) {
|
||||
id value = [self resolveDeferredValue:[node val]
|
||||
withExisting:existing
|
||||
serverValues:serverValues];
|
||||
if (![value isEqual:[node val]] ||
|
||||
![priority isEqual:[node getPriority]]) {
|
||||
return [[FLeafNode alloc] initWithValue:value
|
||||
withPriority:priority];
|
||||
} else {
|
||||
return node;
|
||||
}
|
||||
} else {
|
||||
__block FChildrenNode *newNode = node;
|
||||
if (![priority isEqual:[node getPriority]]) {
|
||||
newNode = [newNode updatePriority:priority];
|
||||
}
|
||||
|
||||
[node enumerateChildrenUsingBlock:^(NSString *childKey,
|
||||
id<FNode> childNode, BOOL *stop) {
|
||||
id newChildNode = [FServerValues
|
||||
resolveDeferredValueSnapshot:childNode
|
||||
withValueProvider:[existing getChild:childKey]
|
||||
serverValues:serverValues];
|
||||
if (![newChildNode isEqual:childNode]) {
|
||||
newNode = [newNode updateImmediateChild:childKey
|
||||
withNewChild:newChildNode];
|
||||
}
|
||||
}];
|
||||
return newNode;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -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 "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FSnapshotHolder : NSObject
|
||||
|
||||
- (id<FNode>)getNode:(FPath *)path;
|
||||
- (void)updateSnapshot:(FPath *)path withNewSnapshot:(id<FNode>)newSnapshotNode;
|
||||
|
||||
@property(nonatomic, strong) id<FNode> rootNode;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FSnapshotHolder.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FEmptyNode.h"
|
||||
|
||||
@interface FSnapshotHolder ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation FSnapshotHolder
|
||||
|
||||
@synthesize rootNode;
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.rootNode = [FEmptyNode emptyNode];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id<FNode>)getNode:(FPath *)path {
|
||||
return [self.rootNode getChild:path];
|
||||
}
|
||||
|
||||
- (void)updateSnapshot:(FPath *)path
|
||||
withNewSnapshot:(id<FNode>)newSnapshotNode {
|
||||
self.rootNode = [self.rootNode updateChild:path
|
||||
withNewChild:newSnapshotNode];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Api/Private/FTypedefs_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class FSparseSnapshotTree;
|
||||
|
||||
typedef void (^fbt_void_nsstring_sstree)(NSString *, FSparseSnapshotTree *);
|
||||
|
||||
@interface FSparseSnapshotTree : NSObject
|
||||
|
||||
- (id<FNode>)findPath:(FPath *)path;
|
||||
- (void)rememberData:(id<FNode>)data onPath:(FPath *)path;
|
||||
- (BOOL)forgetPath:(FPath *)path;
|
||||
- (void)forEachTreeAtPath:(FPath *)prefixPath do:(fbt_void_path_node)func;
|
||||
- (void)forEachChild:(fbt_void_nsstring_sstree)func;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FSparseSnapshotTree.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FChildrenNode.h"
|
||||
|
||||
@interface FSparseSnapshotTree () {
|
||||
id<FNode> value;
|
||||
NSMutableDictionary *children;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation FSparseSnapshotTree
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
value = nil;
|
||||
children = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id<FNode>)findPath:(FPath *)path {
|
||||
if (value != nil) {
|
||||
return [value getChild:path];
|
||||
} else if (![path isEmpty] && children != nil) {
|
||||
NSString *childKey = [path getFront];
|
||||
path = [path popFront];
|
||||
FSparseSnapshotTree *childTree = children[childKey];
|
||||
if (childTree != nil) {
|
||||
return [childTree findPath:path];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)rememberData:(id<FNode>)data onPath:(FPath *)path {
|
||||
if ([path isEmpty]) {
|
||||
value = data;
|
||||
children = nil;
|
||||
} else if (value != nil) {
|
||||
value = [value updateChild:path withNewChild:data];
|
||||
} else {
|
||||
if (children == nil) {
|
||||
children = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
|
||||
NSString *childKey = [path getFront];
|
||||
if (children[childKey] == nil) {
|
||||
children[childKey] = [[FSparseSnapshotTree alloc] init];
|
||||
}
|
||||
|
||||
FSparseSnapshotTree *child = children[childKey];
|
||||
path = [path popFront];
|
||||
[child rememberData:data onPath:path];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)forgetPath:(FPath *)path {
|
||||
if ([path isEmpty]) {
|
||||
value = nil;
|
||||
children = nil;
|
||||
return YES;
|
||||
} else {
|
||||
if (value != nil) {
|
||||
if ([value isLeafNode]) {
|
||||
// non-empty path at leaf. the path leads to nowhere
|
||||
return NO;
|
||||
} else {
|
||||
id<FNode> tmp = value;
|
||||
value = nil;
|
||||
|
||||
[tmp enumerateChildrenUsingBlock:^(NSString *key,
|
||||
id<FNode> node, BOOL *stop) {
|
||||
[self rememberData:node onPath:[[FPath alloc] initWith:key]];
|
||||
}];
|
||||
|
||||
// we've cleared out the value and set children. Call ourself
|
||||
// again to hit the next case
|
||||
return [self forgetPath:path];
|
||||
}
|
||||
} else if (children != nil) {
|
||||
NSString *childKey = [path getFront];
|
||||
path = [path popFront];
|
||||
|
||||
if (children[childKey] != nil) {
|
||||
FSparseSnapshotTree *child = children[childKey];
|
||||
BOOL safeToRemove = [child forgetPath:path];
|
||||
if (safeToRemove) {
|
||||
[children removeObjectForKey:childKey];
|
||||
}
|
||||
}
|
||||
|
||||
if ([children count] == 0) {
|
||||
children = nil;
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)forEachTreeAtPath:(FPath *)prefixPath do:(fbt_void_path_node)func {
|
||||
if (value != nil) {
|
||||
func(prefixPath, value);
|
||||
} else {
|
||||
[self forEachChild:^(NSString *key, FSparseSnapshotTree *tree) {
|
||||
FPath *path = [prefixPath childFromString:key];
|
||||
[tree forEachTreeAtPath:path do:func];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)forEachChild:(fbt_void_nsstring_sstree)func {
|
||||
if (children != nil) {
|
||||
for (NSString *key in children) {
|
||||
FSparseSnapshotTree *tree = [children objectForKey:key];
|
||||
func(key, tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 FOperation;
|
||||
@class FWriteTreeRef;
|
||||
@protocol FNode;
|
||||
@protocol FEventRegistration;
|
||||
@class FQuerySpec;
|
||||
@class FChildrenNode;
|
||||
@class FTupleRemovedQueriesEvents;
|
||||
@class FView;
|
||||
@class FPath;
|
||||
@class FCacheNode;
|
||||
@class FPersistenceManager;
|
||||
|
||||
@interface FSyncPoint : NSObject
|
||||
|
||||
- (id)initWithPersistenceManager:(FPersistenceManager *)persistence;
|
||||
|
||||
- (BOOL)isEmpty;
|
||||
|
||||
/**
|
||||
* Returns array of FEvent
|
||||
*/
|
||||
- (NSArray *)applyOperation:(id<FOperation>)operation
|
||||
writesCache:(FWriteTreeRef *)writesCache
|
||||
serverCache:(id<FNode>)optCompleteServerCache;
|
||||
|
||||
- (FView *)getView:(FQuerySpec *)query
|
||||
writesCache:(FWriteTreeRef *)writesCache
|
||||
serverCache:(FCacheNode *)serverCache;
|
||||
|
||||
/**
|
||||
* Returns array of FEvent
|
||||
*/
|
||||
- (NSArray *)addEventRegistration:(id<FEventRegistration>)eventRegistration
|
||||
forNonExistingViewForQuery:(FQuerySpec *)query
|
||||
writesCache:(FWriteTreeRef *)writesCache
|
||||
serverCache:(FCacheNode *)serverCache;
|
||||
|
||||
- (NSArray *)addEventRegistration:(id<FEventRegistration>)eventRegistration
|
||||
forExistingViewForQuery:(FQuerySpec *)query;
|
||||
|
||||
- (FTupleRemovedQueriesEvents *)removeEventRegistration:
|
||||
(id<FEventRegistration>)eventRegistration
|
||||
forQuery:(FQuerySpec *)query
|
||||
cancelError:(NSError *)cancelError;
|
||||
/**
|
||||
* Returns array of FViews
|
||||
*/
|
||||
- (NSArray *)queryViews;
|
||||
- (id<FNode>)completeServerCacheAtPath:(FPath *)path;
|
||||
- (id<FNode>)completeEventCacheAtPath:(FPath *)path;
|
||||
- (FView *)viewForQuery:(FQuerySpec *)query;
|
||||
- (BOOL)viewExistsForQuery:(FQuerySpec *)query;
|
||||
- (BOOL)hasCompleteView;
|
||||
- (FView *)completeView;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FSyncPoint.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FQueryParams.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FQuerySpec.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FWriteTreeRef.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Operation/FOperation.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Operation/FOperationSource.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FCacheNode.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FDataEvent.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FEventRegistration.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FView.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FViewCache.h"
|
||||
#import "FirebaseDatabase/Sources/Persistence/FPersistenceManager.h"
|
||||
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabaseQuery.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FChildrenNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FEmptyNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/Tuples/FTupleRemovedQueriesEvents.h"
|
||||
|
||||
/**
|
||||
* SyncPoint represents a single location in a SyncTree with 1 or more event
|
||||
* registrations, meaning we need to maintain 1 or more Views at this location
|
||||
* to cache server data and raise appropriate events for server changes and user
|
||||
* writes (set, transaction, update).
|
||||
*
|
||||
* It's responsible for:
|
||||
* - Maintaining the set of 1 or more views necessary at this location (a
|
||||
* SyncPoint with 0 views should be removed).
|
||||
* - Proxying user / server operations to the views as appropriate (i.e.
|
||||
* applyServerOverwrite, applyUserOverwrite, etc.)
|
||||
*/
|
||||
@interface FSyncPoint ()
|
||||
/**
|
||||
* The Views being tracked at this location in the tree, stored as a map where
|
||||
* the key is a queryParams and the value is the View for that query.
|
||||
*
|
||||
* NOTE: This list will be quite small (usually 1, but perhaps 2 or 3; any more
|
||||
* is an odd use case).
|
||||
*
|
||||
* Maps NSString -> FView
|
||||
*/
|
||||
@property(nonatomic, strong) NSMutableDictionary *views;
|
||||
|
||||
@property(nonatomic, strong) FPersistenceManager *persistenceManager;
|
||||
@end
|
||||
|
||||
@implementation FSyncPoint
|
||||
|
||||
- (id)initWithPersistenceManager:(FPersistenceManager *)persistence {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.persistenceManager = persistence;
|
||||
self.views = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isEmpty {
|
||||
return [self.views count] == 0;
|
||||
}
|
||||
|
||||
- (NSArray *)applyOperation:(id<FOperation>)operation
|
||||
toView:(FView *)view
|
||||
writesCache:(FWriteTreeRef *)writesCache
|
||||
serverCache:(id<FNode>)optCompleteServerCache {
|
||||
FViewOperationResult *result = [view applyOperation:operation
|
||||
writesCache:writesCache
|
||||
serverCache:optCompleteServerCache];
|
||||
if (!view.query.loadsAllData) {
|
||||
NSMutableSet *removed = [NSMutableSet set];
|
||||
NSMutableSet *added = [NSMutableSet set];
|
||||
[result.changes enumerateObjectsUsingBlock:^(
|
||||
FChange *change, NSUInteger idx, BOOL *stop) {
|
||||
if (change.type == FIRDataEventTypeChildAdded) {
|
||||
[added addObject:change.childKey];
|
||||
} else if (change.type == FIRDataEventTypeChildRemoved) {
|
||||
[removed addObject:change.childKey];
|
||||
}
|
||||
}];
|
||||
if ([removed count] > 0 || [added count] > 0) {
|
||||
[self.persistenceManager
|
||||
updateTrackedQueryKeysWithAddedKeys:added
|
||||
removedKeys:removed
|
||||
forQuery:view.query];
|
||||
}
|
||||
}
|
||||
return result.events;
|
||||
}
|
||||
|
||||
- (NSArray *)applyOperation:(id<FOperation>)operation
|
||||
writesCache:(FWriteTreeRef *)writesCache
|
||||
serverCache:(id<FNode>)optCompleteServerCache {
|
||||
FQueryParams *queryParams = operation.source.queryParams;
|
||||
if (queryParams != nil) {
|
||||
FView *view = [self.views objectForKey:queryParams];
|
||||
NSAssert(view != nil, @"SyncTree gave us an op for an invalid query.");
|
||||
return [self applyOperation:operation
|
||||
toView:view
|
||||
writesCache:writesCache
|
||||
serverCache:optCompleteServerCache];
|
||||
} else {
|
||||
NSMutableArray *events = [[NSMutableArray alloc] init];
|
||||
[self.views enumerateKeysAndObjectsUsingBlock:^(
|
||||
FQueryParams *key, FView *view, BOOL *stop) {
|
||||
NSArray *eventsForView = [self applyOperation:operation
|
||||
toView:view
|
||||
writesCache:writesCache
|
||||
serverCache:optCompleteServerCache];
|
||||
[events addObjectsFromArray:eventsForView];
|
||||
}];
|
||||
return events;
|
||||
}
|
||||
}
|
||||
|
||||
- (FView *)getView:(FQuerySpec *)query
|
||||
writesCache:(FWriteTreeRef *)writesCache
|
||||
serverCache:(FCacheNode *)serverCache {
|
||||
FView *view = self.views[query.params];
|
||||
if (view != nil) {
|
||||
return view;
|
||||
}
|
||||
id<FNode> eventCache = [writesCache
|
||||
calculateCompleteEventCacheWithCompleteServerCache:
|
||||
serverCache.isFullyInitialized ? serverCache.node : nil];
|
||||
BOOL eventCacheComplete;
|
||||
if (eventCache != nil) {
|
||||
eventCacheComplete = YES;
|
||||
} else {
|
||||
eventCache = [writesCache
|
||||
calculateCompleteEventChildrenWithCompleteServerChildren:
|
||||
serverCache.node != nil ? serverCache.node
|
||||
: [FEmptyNode emptyNode]];
|
||||
eventCacheComplete = NO;
|
||||
}
|
||||
|
||||
FIndexedNode *indexed = [FIndexedNode indexedNodeWithNode:eventCache
|
||||
index:query.index];
|
||||
FCacheNode *eventCacheNode =
|
||||
[[FCacheNode alloc] initWithIndexedNode:indexed
|
||||
isFullyInitialized:eventCacheComplete
|
||||
isFiltered:NO];
|
||||
FViewCache *viewCache =
|
||||
[[FViewCache alloc] initWithEventCache:eventCacheNode
|
||||
serverCache:serverCache];
|
||||
return [[FView alloc] initWithQuery:query initialViewCache:viewCache];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an event callback for the specified query
|
||||
* Returns an array of events to raise.
|
||||
*/
|
||||
- (NSArray *)addEventRegistration:(id<FEventRegistration>)eventRegistration
|
||||
forNonExistingViewForQuery:(FQuerySpec *)query
|
||||
writesCache:(FWriteTreeRef *)writesCache
|
||||
serverCache:(FCacheNode *)serverCache {
|
||||
NSAssert(self.views[query.params] == nil, @"Found view for query: %@",
|
||||
query.params);
|
||||
// TODO: make writesCache take flag for complete server node
|
||||
FView *view = [self getView:query
|
||||
writesCache:writesCache
|
||||
serverCache:serverCache];
|
||||
|
||||
// If this is a non-default query we need to tell persistence our current
|
||||
// view of the data
|
||||
if (!query.loadsAllData) {
|
||||
NSMutableSet *allKeys = [NSMutableSet set];
|
||||
[view.eventCache enumerateChildrenUsingBlock:^(
|
||||
NSString *key, id<FNode> node, BOOL *stop) {
|
||||
[allKeys addObject:key];
|
||||
}];
|
||||
[self.persistenceManager setTrackedQueryKeys:allKeys forQuery:query];
|
||||
}
|
||||
self.views[query.params] = view;
|
||||
return [self addEventRegistration:eventRegistration
|
||||
forExistingViewForQuery:query];
|
||||
}
|
||||
|
||||
- (NSArray *)addEventRegistration:(id<FEventRegistration>)eventRegistration
|
||||
forExistingViewForQuery:(FQuerySpec *)query {
|
||||
FView *view = self.views[query.params];
|
||||
NSAssert(view != nil, @"No view for query: %@", query);
|
||||
[view addEventRegistration:eventRegistration];
|
||||
return [view initialEvents:eventRegistration];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove event callback(s). Return cancelEvents if a cancelError is specified.
|
||||
*
|
||||
* If query is the default query, we'll check all views for the specified
|
||||
* eventRegistration. If eventRegistration is nil, we'll remove all callbacks
|
||||
* for the specified view(s).
|
||||
*
|
||||
* @return FTupleRemovedQueriesEvents removed queries and any cancel events
|
||||
*/
|
||||
- (FTupleRemovedQueriesEvents *)removeEventRegistration:
|
||||
(id<FEventRegistration>)eventRegistration
|
||||
forQuery:(FQuerySpec *)query
|
||||
cancelError:(NSError *)cancelError {
|
||||
NSMutableArray *removedQueries = [[NSMutableArray alloc] init];
|
||||
__block NSMutableArray *cancelEvents = [[NSMutableArray alloc] init];
|
||||
BOOL hadCompleteView = [self hasCompleteView];
|
||||
if ([query isDefault]) {
|
||||
// When you do [ref removeObserverWithHandle:], we search all views for
|
||||
// the registration to remove.
|
||||
[self.views enumerateKeysAndObjectsUsingBlock:^(
|
||||
FQueryParams *viewQueryParams, FView *view,
|
||||
BOOL *stop) {
|
||||
[cancelEvents
|
||||
addObjectsFromArray:[view
|
||||
removeEventRegistration:eventRegistration
|
||||
cancelError:cancelError]];
|
||||
if ([view isEmpty]) {
|
||||
[self.views removeObjectForKey:viewQueryParams];
|
||||
|
||||
// We'll deal with complete views later
|
||||
if (![view.query loadsAllData]) {
|
||||
[removedQueries addObject:view.query];
|
||||
}
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
// remove the callback from the specific view
|
||||
FView *view = [self.views objectForKey:query.params];
|
||||
if (view != nil) {
|
||||
[cancelEvents addObjectsFromArray:
|
||||
[view removeEventRegistration:eventRegistration
|
||||
cancelError:cancelError]];
|
||||
|
||||
if ([view isEmpty]) {
|
||||
[self.views removeObjectForKey:query.params];
|
||||
|
||||
// We'll deal with complete views later
|
||||
if (![view.query loadsAllData]) {
|
||||
[removedQueries addObject:view.query];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hadCompleteView && ![self hasCompleteView]) {
|
||||
// We removed our last complete view
|
||||
[removedQueries addObject:[FQuerySpec defaultQueryAtPath:query.path]];
|
||||
}
|
||||
|
||||
return [[FTupleRemovedQueriesEvents alloc]
|
||||
initWithRemovedQueries:removedQueries
|
||||
cancelEvents:cancelEvents];
|
||||
}
|
||||
|
||||
- (NSArray *)queryViews {
|
||||
__block NSMutableArray *filteredViews = [[NSMutableArray alloc] init];
|
||||
|
||||
[self.views enumerateKeysAndObjectsUsingBlock:^(FQueryParams *key,
|
||||
FView *view, BOOL *stop) {
|
||||
if (![view.query loadsAllData]) {
|
||||
[filteredViews addObject:view];
|
||||
}
|
||||
}];
|
||||
|
||||
return filteredViews;
|
||||
}
|
||||
|
||||
- (id<FNode>)completeServerCacheAtPath:(FPath *)path {
|
||||
__block id<FNode> serverCache = nil;
|
||||
[self.views enumerateKeysAndObjectsUsingBlock:^(FQueryParams *key,
|
||||
FView *view, BOOL *stop) {
|
||||
serverCache = [view completeServerCacheFor:path];
|
||||
*stop = (serverCache != nil);
|
||||
}];
|
||||
return serverCache;
|
||||
}
|
||||
|
||||
- (id<FNode>)completeEventCacheAtPath:(FPath *)path {
|
||||
__block id<FNode> eventCache = nil;
|
||||
[self.views enumerateKeysAndObjectsUsingBlock:^(FQueryParams *key,
|
||||
FView *view, BOOL *stop) {
|
||||
eventCache = [view completeEventCacheFor:path];
|
||||
*stop = (eventCache != nil);
|
||||
}];
|
||||
return eventCache;
|
||||
}
|
||||
|
||||
- (FView *)viewForQuery:(FQuerySpec *)query {
|
||||
return [self.views objectForKey:query.params];
|
||||
}
|
||||
|
||||
- (BOOL)viewExistsForQuery:(FQuerySpec *)query {
|
||||
return [self viewForQuery:query] != nil;
|
||||
}
|
||||
|
||||
- (BOOL)hasCompleteView {
|
||||
return [self completeView] != nil;
|
||||
}
|
||||
|
||||
- (FView *)completeView {
|
||||
__block FView *completeView = nil;
|
||||
|
||||
[self.views enumerateKeysAndObjectsUsingBlock:^(FQueryParams *key,
|
||||
FView *view, BOOL *stop) {
|
||||
if ([view.query loadsAllData]) {
|
||||
completeView = view;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
|
||||
return completeView;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
@class FIndexedNode;
|
||||
@class FListenProvider;
|
||||
@protocol FNode;
|
||||
@class FPath;
|
||||
@protocol FEventRegistration;
|
||||
@protocol FPersistedServerCache;
|
||||
@class FQuerySpec;
|
||||
@class FCompoundWrite;
|
||||
@class FPersistenceManager;
|
||||
@class FCompoundHash;
|
||||
@protocol FClock;
|
||||
|
||||
@protocol FSyncTreeHash <NSObject>
|
||||
|
||||
- (NSString *)simpleHash;
|
||||
- (FCompoundHash *)compoundHash;
|
||||
- (BOOL)includeCompoundHash;
|
||||
|
||||
@end
|
||||
|
||||
@interface FSyncTree : NSObject
|
||||
|
||||
- (id)initWithListenProvider:(FListenProvider *)provider;
|
||||
- (id)initWithPersistenceManager:(FPersistenceManager *)persistenceManager
|
||||
listenProvider:(FListenProvider *)provider;
|
||||
|
||||
// These methods all return NSArray of FEvent
|
||||
- (NSArray *)applyUserOverwriteAtPath:(FPath *)path
|
||||
newData:(id<FNode>)newData
|
||||
writeId:(NSInteger)writeId
|
||||
isVisible:(BOOL)visible;
|
||||
- (NSArray *)applyUserMergeAtPath:(FPath *)path
|
||||
changedChildren:(FCompoundWrite *)changedChildren
|
||||
writeId:(NSInteger)writeId;
|
||||
- (NSArray *)ackUserWriteWithWriteId:(NSInteger)writeId
|
||||
revert:(BOOL)revert
|
||||
persist:(BOOL)persist
|
||||
clock:(id<FClock>)clock;
|
||||
- (NSArray *)applyServerOverwriteAtPath:(FPath *)path
|
||||
newData:(id<FNode>)newData;
|
||||
- (NSArray *)applyServerMergeAtPath:(FPath *)path
|
||||
changedChildren:(FCompoundWrite *)changedChildren;
|
||||
- (NSArray *)applyServerRangeMergeAtPath:(FPath *)path
|
||||
updates:(NSArray *)ranges;
|
||||
- (NSArray *)applyTaggedQueryOverwriteAtPath:(FPath *)path
|
||||
newData:(id<FNode>)newData
|
||||
tagId:(NSNumber *)tagId;
|
||||
- (NSArray *)applyTaggedQueryMergeAtPath:(FPath *)path
|
||||
changedChildren:(FCompoundWrite *)changedChildren
|
||||
tagId:(NSNumber *)tagId;
|
||||
- (NSArray *)applyTaggedServerRangeMergeAtPath:(FPath *)path
|
||||
updates:(NSArray *)ranges
|
||||
tagId:(NSNumber *)tagId;
|
||||
- (NSArray *)addEventRegistration:(id<FEventRegistration>)eventRegistration
|
||||
forQuery:(FQuerySpec *)query;
|
||||
- (NSArray *)removeEventRegistration:(id<FEventRegistration>)eventRegistration
|
||||
forQuery:(FQuerySpec *)query
|
||||
cancelError:(NSError *)cancelError;
|
||||
- (void)keepQuery:(FQuerySpec *)query synced:(BOOL)keepSynced;
|
||||
- (NSArray *)removeAllWrites;
|
||||
|
||||
- (FIndexedNode *)persistenceServerCache:(FQuerySpec *)querySpec;
|
||||
- (id<FNode>)getServerValue:(FQuerySpec *)query;
|
||||
- (id<FNode>)calcCompleteEventCacheAtPath:(FPath *)path
|
||||
excludeWriteIds:(NSArray *)writeIdsToExclude;
|
||||
|
||||
@end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
@class FPath;
|
||||
@class FCompoundWrite;
|
||||
@protocol FNode;
|
||||
|
||||
@interface FWriteRecord : NSObject
|
||||
|
||||
- initWithPath:(FPath *)path
|
||||
overwrite:(id<FNode>)overwrite
|
||||
writeId:(NSInteger)writeId
|
||||
visible:(BOOL)isVisible;
|
||||
- initWithPath:(FPath *)path
|
||||
merge:(FCompoundWrite *)merge
|
||||
writeId:(NSInteger)writeId;
|
||||
|
||||
@property(nonatomic, readonly) NSInteger writeId;
|
||||
@property(nonatomic, strong, readonly) FPath *path;
|
||||
@property(nonatomic, strong, readonly) id<FNode> overwrite;
|
||||
/**
|
||||
* Maps NSString -> id<FNode>
|
||||
*/
|
||||
@property(nonatomic, strong, readonly) FCompoundWrite *merge;
|
||||
@property(nonatomic, readonly) BOOL visible;
|
||||
|
||||
- (BOOL)isMerge;
|
||||
- (BOOL)isOverwrite;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FWriteRecord.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FCompoundWrite.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
|
||||
@interface FWriteRecord ()
|
||||
@property(nonatomic, readwrite) NSInteger writeId;
|
||||
@property(nonatomic, strong, readwrite) FPath *path;
|
||||
@property(nonatomic, strong, readwrite) id<FNode> overwrite;
|
||||
@property(nonatomic, strong, readwrite) FCompoundWrite *merge;
|
||||
@property(nonatomic, readwrite) BOOL visible;
|
||||
@end
|
||||
|
||||
@implementation FWriteRecord
|
||||
|
||||
- (id)initWithPath:(FPath *)path
|
||||
overwrite:(id<FNode>)overwrite
|
||||
writeId:(NSInteger)writeId
|
||||
visible:(BOOL)isVisible {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.path = path;
|
||||
if (overwrite == nil) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Can't pass nil as overwrite parameter to an "
|
||||
@"overwrite write record"];
|
||||
}
|
||||
self.overwrite = overwrite;
|
||||
self.merge = nil;
|
||||
self.writeId = writeId;
|
||||
self.visible = isVisible;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithPath:(FPath *)path
|
||||
merge:(FCompoundWrite *)merge
|
||||
writeId:(NSInteger)writeId {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.path = path;
|
||||
if (merge == nil) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Can't pass nil as merge parameter to an merge "
|
||||
@"write record"];
|
||||
}
|
||||
self.overwrite = nil;
|
||||
self.merge = merge;
|
||||
self.writeId = writeId;
|
||||
self.visible = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id<FNode>)overwrite {
|
||||
if (self->_overwrite == nil) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Can't get overwrite for merge write record!"];
|
||||
}
|
||||
return self->_overwrite;
|
||||
}
|
||||
|
||||
- (FCompoundWrite *)compoundWrite {
|
||||
if (self->_merge == nil) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Can't get merge for overwrite write record!"];
|
||||
}
|
||||
return self->_merge;
|
||||
}
|
||||
|
||||
- (BOOL)isMerge {
|
||||
return self->_merge != nil;
|
||||
}
|
||||
|
||||
- (BOOL)isOverwrite {
|
||||
return self->_overwrite != nil;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
if (self.isOverwrite) {
|
||||
return
|
||||
[NSString stringWithFormat:@"FWriteRecord { writeId = %lu, path = "
|
||||
@"%@, overwrite = %@, visible = %d }",
|
||||
(unsigned long)self.writeId, self.path,
|
||||
self.overwrite, self.visible];
|
||||
} else {
|
||||
return [NSString
|
||||
stringWithFormat:
|
||||
@"FWriteRecord { writeId = %lu, path = %@, merge = %@ }",
|
||||
(unsigned long)self.writeId, self.path, self.merge];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if (![object isKindOfClass:[self class]]) {
|
||||
return NO;
|
||||
}
|
||||
FWriteRecord *other = (FWriteRecord *)object;
|
||||
if (self->_writeId != other->_writeId)
|
||||
return NO;
|
||||
if (self->_path != other->_path && ![self->_path isEqual:other->_path])
|
||||
return NO;
|
||||
if (self->_overwrite != other->_overwrite &&
|
||||
![self->_overwrite isEqual:other->_overwrite])
|
||||
return NO;
|
||||
if (self->_merge != other->_merge && ![self->_merge isEqual:other->_merge])
|
||||
return NO;
|
||||
if (self->_visible != other->_visible)
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
NSUInteger hash = self->_writeId * 17;
|
||||
hash = hash * 31 + self->_path.hash;
|
||||
hash = hash * 31 + self->_overwrite.hash;
|
||||
hash = hash * 31 + self->_merge.hash;
|
||||
hash = hash * 31 + ((self->_visible) ? 1 : 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
@class FPath;
|
||||
@protocol FNode;
|
||||
@class FCompoundWrite;
|
||||
@class FWriteTreeRef;
|
||||
@class FChildrenNode;
|
||||
@class FNamedNode;
|
||||
@class FWriteRecord;
|
||||
@protocol FIndex;
|
||||
@class FCacheNode;
|
||||
|
||||
@interface FWriteTree : NSObject
|
||||
|
||||
- (FWriteTreeRef *)childWritesForPath:(FPath *)path;
|
||||
- (void)addOverwriteAtPath:(FPath *)path
|
||||
newData:(id<FNode>)newData
|
||||
writeId:(NSInteger)writeId
|
||||
isVisible:(BOOL)visible;
|
||||
- (void)addMergeAtPath:(FPath *)path
|
||||
changedChildren:(FCompoundWrite *)changedChildren
|
||||
writeId:(NSInteger)writeId;
|
||||
- (BOOL)removeWriteId:(NSInteger)writeId;
|
||||
- (NSArray *)removeAllWrites;
|
||||
- (FWriteRecord *)writeForId:(NSInteger)writeId;
|
||||
|
||||
- (id<FNode>)calculateCompleteEventCacheAtPath:(FPath *)treePath
|
||||
completeServerCache:(id<FNode>)completeServerCache
|
||||
excludeWriteIds:(NSArray *)writeIdsToExclude
|
||||
includeHiddenWrites:(BOOL)includeHiddenWrites;
|
||||
|
||||
- (id<FNode>)calculateCompleteEventChildrenAtPath:(FPath *)treePath
|
||||
completeServerChildren:
|
||||
(id<FNode>)completeServerChildren;
|
||||
|
||||
- (id<FNode>)
|
||||
calculateEventCacheAfterServerOverwriteAtPath:(FPath *)treePath
|
||||
childPath:(FPath *)childPath
|
||||
existingEventSnap:(id<FNode>)existingEventSnap
|
||||
existingServerSnap:(id<FNode>)existingServerSnap;
|
||||
|
||||
- (id<FNode>)calculateCompleteChildAtPath:(FPath *)treePath
|
||||
childKey:(NSString *)childKey
|
||||
cache:(FCacheNode *)existingServerCache;
|
||||
|
||||
- (id<FNode>)shadowingWriteAtPath:(FPath *)path;
|
||||
|
||||
- (FNamedNode *)calculateNextNodeAfterPost:(FNamedNode *)post
|
||||
atPath:(FPath *)path
|
||||
completeServerData:(id<FNode>)completeServerData
|
||||
reverse:(BOOL)reverse
|
||||
index:(id<FIndex>)index;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FWriteTree.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FWriteRecord.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FWriteTreeRef.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FImmutableTree.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FCacheNode.h"
|
||||
#import "FirebaseDatabase/Sources/FIndex.h"
|
||||
#import "FirebaseDatabase/Sources/FNamedNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FChildrenNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FCompoundWrite.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FEmptyNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
|
||||
@interface FWriteTree ()
|
||||
/**
|
||||
* A tree tracking the results of applying all visible writes. This does not
|
||||
* include transactions with applyLocally=false or writes that are completely
|
||||
* shadowed by other writes. Contains id<FNode> as values.
|
||||
*/
|
||||
@property(nonatomic, strong) FCompoundWrite *visibleWrites;
|
||||
/**
|
||||
* A list of pending writes, regardless of visibility and shadowed-ness. Used to
|
||||
* calcuate arbitrary sets of the changed data, such as hidden writes (from
|
||||
* transactions) or changes with certain writes excluded (also used by
|
||||
* transactions). Contains FWriteRecords.
|
||||
*/
|
||||
@property(nonatomic, strong) NSMutableArray *allWrites;
|
||||
@property(nonatomic) NSInteger lastWriteId;
|
||||
@end
|
||||
|
||||
/**
|
||||
* FWriteTree tracks all pending user-initiated writes and has methods to
|
||||
* calcuate the result of merging them with underlying server data (to create
|
||||
* "event cache" data). Pending writes are added with addOverwriteAtPath: and
|
||||
* addMergeAtPath: and removed with removeWriteId:.
|
||||
*/
|
||||
@implementation FWriteTree
|
||||
|
||||
@synthesize allWrites;
|
||||
@synthesize lastWriteId;
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.visibleWrites = [FCompoundWrite emptyWrite];
|
||||
self.allWrites = [[NSMutableArray alloc] init];
|
||||
self.lastWriteId = -1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new WriteTreeRef for the given path. For use with a new sync point
|
||||
* at the given path.
|
||||
*/
|
||||
- (FWriteTreeRef *)childWritesForPath:(FPath *)path {
|
||||
return [[FWriteTreeRef alloc] initWithPath:path writeTree:self];
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a new overwrite from user code.
|
||||
* @param visible Is set to false by some transactions. It should be excluded
|
||||
* from event caches.
|
||||
*/
|
||||
- (void)addOverwriteAtPath:(FPath *)path
|
||||
newData:(id<FNode>)newData
|
||||
writeId:(NSInteger)writeId
|
||||
isVisible:(BOOL)visible {
|
||||
NSAssert(writeId > self.lastWriteId,
|
||||
@"Stacking an older write on top of a newer one");
|
||||
FWriteRecord *record = [[FWriteRecord alloc] initWithPath:path
|
||||
overwrite:newData
|
||||
writeId:writeId
|
||||
visible:visible];
|
||||
[self.allWrites addObject:record];
|
||||
|
||||
if (visible) {
|
||||
self.visibleWrites = [self.visibleWrites addWrite:newData atPath:path];
|
||||
}
|
||||
|
||||
self.lastWriteId = writeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a new merge from user code.
|
||||
* @param changedChildren maps NSString -> id<FNode>
|
||||
*/
|
||||
- (void)addMergeAtPath:(FPath *)path
|
||||
changedChildren:(FCompoundWrite *)changedChildren
|
||||
writeId:(NSInteger)writeId {
|
||||
NSAssert(writeId > self.lastWriteId,
|
||||
@"Stacking an older merge on top of newer one");
|
||||
FWriteRecord *record = [[FWriteRecord alloc] initWithPath:path
|
||||
merge:changedChildren
|
||||
writeId:writeId];
|
||||
[self.allWrites addObject:record];
|
||||
|
||||
self.visibleWrites = [self.visibleWrites addCompoundWrite:changedChildren
|
||||
atPath:path];
|
||||
self.lastWriteId = writeId;
|
||||
}
|
||||
|
||||
- (FWriteRecord *)writeForId:(NSInteger)writeId {
|
||||
NSUInteger index = [self.allWrites
|
||||
indexOfObjectPassingTest:^BOOL(FWriteRecord *write, NSUInteger idx,
|
||||
BOOL *stop) {
|
||||
return write.writeId == writeId;
|
||||
}];
|
||||
return (index == NSNotFound) ? nil : self.allWrites[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a write (either an overwrite or merge) that has been successfully
|
||||
* acknowledged by the server. Recalculates the tree if necessary. We return the
|
||||
* path of the write and whether it may have been visible, meaning views need to
|
||||
* reevaluate.
|
||||
*
|
||||
* @return YES if the write may have been visible (meaning we'll need to
|
||||
* reevaluate / raise events as a result).
|
||||
*/
|
||||
- (BOOL)removeWriteId:(NSInteger)writeId {
|
||||
NSUInteger index = [self.allWrites
|
||||
indexOfObjectPassingTest:^BOOL(FWriteRecord *record, NSUInteger idx,
|
||||
BOOL *stop) {
|
||||
if (record.writeId == writeId) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}];
|
||||
NSAssert(index != NSNotFound,
|
||||
@"[FWriteTree removeWriteId:] called with nonexistent writeId.");
|
||||
FWriteRecord *writeToRemove = self.allWrites[index];
|
||||
[self.allWrites removeObjectAtIndex:index];
|
||||
|
||||
BOOL removedWriteWasVisible = writeToRemove.visible;
|
||||
BOOL removedWriteOverlapsWithOtherWrites = NO;
|
||||
NSInteger i = [self.allWrites count] - 1;
|
||||
|
||||
while (removedWriteWasVisible && i >= 0) {
|
||||
FWriteRecord *currentWrite = [self.allWrites objectAtIndex:i];
|
||||
if (currentWrite.visible) {
|
||||
if (i >= index && [self record:currentWrite
|
||||
containsPath:writeToRemove.path]) {
|
||||
// The removed write was completely shadowed by a subsequent
|
||||
// write.
|
||||
removedWriteWasVisible = NO;
|
||||
} else if ([writeToRemove.path contains:currentWrite.path]) {
|
||||
// Either we're covering some writes or they're covering part of
|
||||
// us (depending on which came first).
|
||||
removedWriteOverlapsWithOtherWrites = YES;
|
||||
}
|
||||
}
|
||||
i--;
|
||||
}
|
||||
|
||||
if (!removedWriteWasVisible) {
|
||||
return NO;
|
||||
} else if (removedWriteOverlapsWithOtherWrites) {
|
||||
// There's some shadowing going on. Just rebuild the visible writes from
|
||||
// scratch.
|
||||
[self resetTree];
|
||||
return YES;
|
||||
} else {
|
||||
// There's no shadowing. We can safely just remove the write(s) from
|
||||
// visibleWrites.
|
||||
if ([writeToRemove isOverwrite]) {
|
||||
self.visibleWrites =
|
||||
[self.visibleWrites removeWriteAtPath:writeToRemove.path];
|
||||
} else {
|
||||
FCompoundWrite *merge = writeToRemove.merge;
|
||||
[merge enumerateWrites:^(FPath *path, id<FNode> node, BOOL *stop) {
|
||||
self.visibleWrites = [self.visibleWrites
|
||||
removeWriteAtPath:[writeToRemove.path child:path]];
|
||||
}];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)removeAllWrites {
|
||||
NSArray *writes = self.allWrites;
|
||||
self.visibleWrites = [FCompoundWrite emptyWrite];
|
||||
self.allWrites = [NSMutableArray array];
|
||||
return writes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A complete snapshot for the given path if there's visible write data
|
||||
* at that path, else nil. No server data is considered.
|
||||
*/
|
||||
- (id<FNode>)completeWriteDataAtPath:(FPath *)path {
|
||||
return [self.visibleWrites completeNodeAtPath:path];
|
||||
}
|
||||
|
||||
/**
|
||||
* Given optional, underlying server data, and an optional set of constraints
|
||||
* (exclude some sets, include hidden writes), attempt to calculate a complete
|
||||
* snapshot for the given path
|
||||
* @param includeHiddenWrites Defaults to false, whether or not to layer on
|
||||
* writes with visible set to false
|
||||
*/
|
||||
- (id<FNode>)calculateCompleteEventCacheAtPath:(FPath *)treePath
|
||||
completeServerCache:(id<FNode>)completeServerCache
|
||||
excludeWriteIds:(NSArray *)writeIdsToExclude
|
||||
includeHiddenWrites:(BOOL)includeHiddenWrites {
|
||||
if (writeIdsToExclude == nil && !includeHiddenWrites) {
|
||||
id<FNode> shadowingNode =
|
||||
[self.visibleWrites completeNodeAtPath:treePath];
|
||||
if (shadowingNode != nil) {
|
||||
return shadowingNode;
|
||||
} else {
|
||||
// No cache here. Can't claim complete knowledge.
|
||||
FCompoundWrite *subMerge =
|
||||
[self.visibleWrites childCompoundWriteAtPath:treePath];
|
||||
if (subMerge.isEmpty) {
|
||||
return completeServerCache;
|
||||
} else if (completeServerCache == nil &&
|
||||
![subMerge hasCompleteWriteAtPath:[FPath empty]]) {
|
||||
// We wouldn't have a complete snapshot since there's no
|
||||
// underlying data and no complete shadow
|
||||
return nil;
|
||||
} else {
|
||||
id<FNode> layeredCache = completeServerCache != nil
|
||||
? completeServerCache
|
||||
: [FEmptyNode emptyNode];
|
||||
return [subMerge applyToNode:layeredCache];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FCompoundWrite *merge =
|
||||
[self.visibleWrites childCompoundWriteAtPath:treePath];
|
||||
if (!includeHiddenWrites && merge.isEmpty) {
|
||||
return completeServerCache;
|
||||
} else {
|
||||
// If the server cache is null and we don't have a complete cache,
|
||||
// we need to return nil
|
||||
if (!includeHiddenWrites && completeServerCache == nil &&
|
||||
![merge hasCompleteWriteAtPath:[FPath empty]]) {
|
||||
return nil;
|
||||
} else {
|
||||
BOOL (^filter)(FWriteRecord *) = ^(FWriteRecord *record) {
|
||||
return (BOOL)((record.visible || includeHiddenWrites) &&
|
||||
(writeIdsToExclude == nil ||
|
||||
![writeIdsToExclude
|
||||
containsObject:[NSNumber
|
||||
numberWithInteger:
|
||||
record.writeId]]) &&
|
||||
([record.path contains:treePath] ||
|
||||
[treePath contains:record.path]));
|
||||
};
|
||||
FCompoundWrite *mergeAtPath =
|
||||
[FWriteTree layerTreeFromWrites:self.allWrites
|
||||
filter:filter
|
||||
treeRoot:treePath];
|
||||
id<FNode> layeredCache = completeServerCache
|
||||
? completeServerCache
|
||||
: [FEmptyNode emptyNode];
|
||||
return [mergeAtPath applyToNode:layeredCache];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* With optional, underlying server data, attempt to return a children node of
|
||||
* children that we have complete data for. Used when creating new views, to
|
||||
* pre-fill their complete event children snapshot.
|
||||
*/
|
||||
- (FChildrenNode *)calculateCompleteEventChildrenAtPath:(FPath *)treePath
|
||||
completeServerChildren:
|
||||
(id<FNode>)completeServerChildren {
|
||||
__block id<FNode> completeChildren = [FEmptyNode emptyNode];
|
||||
id<FNode> topLevelSet = [self.visibleWrites completeNodeAtPath:treePath];
|
||||
if (topLevelSet != nil) {
|
||||
if (![topLevelSet isLeafNode]) {
|
||||
// We're shadowing everything. Return the children.
|
||||
FChildrenNode *topChildrenNode = topLevelSet;
|
||||
[topChildrenNode enumerateChildrenUsingBlock:^(
|
||||
NSString *key, id<FNode> node, BOOL *stop) {
|
||||
completeChildren = [completeChildren updateImmediateChild:key
|
||||
withNewChild:node];
|
||||
}];
|
||||
}
|
||||
return completeChildren;
|
||||
} else {
|
||||
// Layer any children we have on top of this
|
||||
// We know we don't have a top-level set, so just enumerate existing
|
||||
// children, and apply any updates
|
||||
FCompoundWrite *merge =
|
||||
[self.visibleWrites childCompoundWriteAtPath:treePath];
|
||||
[completeServerChildren enumerateChildrenUsingBlock:^(
|
||||
NSString *key, id<FNode> node, BOOL *stop) {
|
||||
FCompoundWrite *childMerge =
|
||||
[merge childCompoundWriteAtPath:[[FPath alloc] initWith:key]];
|
||||
id<FNode> newChildNode = [childMerge applyToNode:node];
|
||||
completeChildren =
|
||||
[completeChildren updateImmediateChild:key
|
||||
withNewChild:newChildNode];
|
||||
}];
|
||||
// Add any complete children we have from the set.
|
||||
for (FNamedNode *node in merge.completeChildren) {
|
||||
completeChildren =
|
||||
[completeChildren updateImmediateChild:node.name
|
||||
withNewChild:node.node];
|
||||
}
|
||||
return completeChildren;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that the underlying server data has updated, determine what, if
|
||||
* anything, needs to be applied to the event cache.
|
||||
*
|
||||
* Possibilities
|
||||
*
|
||||
* 1. No write are shadowing. Events should be raised, the snap to be applied
|
||||
* comes from the server data.
|
||||
*
|
||||
* 2. Some write is completely shadowing. No events to be raised.
|
||||
*
|
||||
* 3. Is partially shadowed. Events ..
|
||||
*
|
||||
* Either existingEventSnap or existingServerSnap must exist.
|
||||
*/
|
||||
- (id<FNode>)calculateEventCacheAfterServerOverwriteAtPath:(FPath *)treePath
|
||||
childPath:(FPath *)childPath
|
||||
existingEventSnap:
|
||||
(id<FNode>)existingEventSnap
|
||||
existingServerSnap:
|
||||
(id<FNode>)existingServerSnap {
|
||||
NSAssert(existingEventSnap != nil || existingServerSnap != nil,
|
||||
@"Either existingEventSnap or existingServerSanp must exist.");
|
||||
|
||||
FPath *path = [treePath child:childPath];
|
||||
if ([self.visibleWrites hasCompleteWriteAtPath:path]) {
|
||||
// At this point we can probably guarantee that we're in case 2, meaning
|
||||
// no events May need to check visibility while doing the
|
||||
// findRootMostValueAndPath call
|
||||
return nil;
|
||||
} else {
|
||||
// This could be more efficient if the serverNode + updates doesn't
|
||||
// change the eventSnap However this is tricky to find out, since user
|
||||
// updates don't necessary change the server snap, e.g. priority updates
|
||||
// on empty nodes, or deep deletes. Another special case is if the
|
||||
// server adds nodes, but doesn't change any existing writes. It is
|
||||
// therefore not enough to only check if the updates change the
|
||||
// serverNode. Maybe check if the merge tree contains these special
|
||||
// cases and only do a full overwrite in that case?
|
||||
FCompoundWrite *childMerge =
|
||||
[self.visibleWrites childCompoundWriteAtPath:path];
|
||||
if (childMerge.isEmpty) {
|
||||
// We're not shadowing at all. Case 1
|
||||
return [existingServerSnap getChild:childPath];
|
||||
} else {
|
||||
return [childMerge
|
||||
applyToNode:[existingServerSnap getChild:childPath]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a complete child for a given server snap after applying all user
|
||||
* writes or nil if there is no complete child for this child key.
|
||||
*/
|
||||
- (id<FNode>)calculateCompleteChildAtPath:(FPath *)treePath
|
||||
childKey:(NSString *)childKey
|
||||
cache:(FCacheNode *)existingServerCache {
|
||||
FPath *path = [treePath childFromString:childKey];
|
||||
id<FNode> shadowingNode = [self.visibleWrites completeNodeAtPath:path];
|
||||
if (shadowingNode != nil) {
|
||||
return shadowingNode;
|
||||
} else {
|
||||
if ([existingServerCache isCompleteForChild:childKey]) {
|
||||
FCompoundWrite *childMerge =
|
||||
[self.visibleWrites childCompoundWriteAtPath:path];
|
||||
return [childMerge applyToNode:[existingServerCache.node
|
||||
getImmediateChild:childKey]];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a node if there is a complete overwrite for this path. More
|
||||
* specifically, if there is a write at a higher path, this will return the
|
||||
* child of that write relative to the write and this path. Returns null if
|
||||
* there is no write at this path.
|
||||
*/
|
||||
- (id<FNode>)shadowingWriteAtPath:(FPath *)path {
|
||||
return [self.visibleWrites completeNodeAtPath:path];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used when processing child remove events on a query. If we
|
||||
* can, we pull in children that were outside the window, but may now be in the
|
||||
* window.
|
||||
*/
|
||||
- (FNamedNode *)calculateNextNodeAfterPost:(FNamedNode *)post
|
||||
atPath:(FPath *)treePath
|
||||
completeServerData:(id<FNode>)completeServerData
|
||||
reverse:(BOOL)reverse
|
||||
index:(id<FIndex>)index {
|
||||
__block id<FNode> toIterate;
|
||||
FCompoundWrite *merge =
|
||||
[self.visibleWrites childCompoundWriteAtPath:treePath];
|
||||
id<FNode> shadowingNode = [merge completeNodeAtPath:[FPath empty]];
|
||||
if (shadowingNode != nil) {
|
||||
toIterate = shadowingNode;
|
||||
} else if (completeServerData != nil) {
|
||||
toIterate = [merge applyToNode:completeServerData];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
|
||||
__block NSString *currentNextKey = nil;
|
||||
__block id<FNode> currentNextNode = nil;
|
||||
[toIterate enumerateChildrenUsingBlock:^(NSString *key, id<FNode> node,
|
||||
BOOL *stop) {
|
||||
if ([index compareKey:key
|
||||
andNode:node
|
||||
toOtherKey:post.name
|
||||
andNode:post.node
|
||||
reverse:reverse] > NSOrderedSame &&
|
||||
(!currentNextKey || [index compareKey:key
|
||||
andNode:node
|
||||
toOtherKey:currentNextKey
|
||||
andNode:currentNextNode
|
||||
reverse:reverse] < NSOrderedSame)) {
|
||||
currentNextKey = key;
|
||||
currentNextNode = node;
|
||||
}
|
||||
}];
|
||||
|
||||
if (currentNextKey != nil) {
|
||||
return [FNamedNode nodeWithName:currentNextKey node:currentNextNode];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Private Methods
|
||||
|
||||
- (BOOL)record:(FWriteRecord *)record containsPath:(FPath *)path {
|
||||
if ([record isOverwrite]) {
|
||||
return [record.path contains:path];
|
||||
} else {
|
||||
__block BOOL contains = NO;
|
||||
[record.merge
|
||||
enumerateWrites:^(FPath *childPath, id<FNode> node, BOOL *stop) {
|
||||
contains = [[record.path child:childPath] contains:path];
|
||||
*stop = contains;
|
||||
}];
|
||||
return contains;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-layer the writes and merges into a tree so we can efficiently calculate
|
||||
* event snapshots
|
||||
*/
|
||||
- (void)resetTree {
|
||||
self.visibleWrites =
|
||||
[FWriteTree layerTreeFromWrites:self.allWrites
|
||||
filter:[FWriteTree defaultFilter]
|
||||
treeRoot:[FPath empty]];
|
||||
if ([self.allWrites count] > 0) {
|
||||
FWriteRecord *lastRecord = self.allWrites[[self.allWrites count] - 1];
|
||||
self.lastWriteId = lastRecord.writeId;
|
||||
} else {
|
||||
self.lastWriteId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The default filter used when constructing the tree. Keep everything that's
|
||||
* visible.
|
||||
*/
|
||||
+ (BOOL (^)(FWriteRecord *record))defaultFilter {
|
||||
static BOOL (^filter)(FWriteRecord *);
|
||||
static dispatch_once_t filterToken;
|
||||
dispatch_once(&filterToken, ^{
|
||||
filter = ^(FWriteRecord *record) {
|
||||
return YES;
|
||||
};
|
||||
});
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method. Given an array of WriteRecords, a filter for which ones to
|
||||
* include, and a path, construct a merge at that path
|
||||
* @return An FImmutableTree of id<FNode>s.
|
||||
*/
|
||||
+ (FCompoundWrite *)layerTreeFromWrites:(NSArray *)writes
|
||||
filter:(BOOL (^)(FWriteRecord *record))filter
|
||||
treeRoot:(FPath *)treeRoot {
|
||||
__block FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
|
||||
[writes enumerateObjectsUsingBlock:^(FWriteRecord *record, NSUInteger idx,
|
||||
BOOL *stop) {
|
||||
// Theory, a later set will either:
|
||||
// a) abort a relevant transaction, so no need to worry about excluding it
|
||||
// from calculating that transaction b) not be relevant to a transaction
|
||||
// (separate branch), so again will not affect the data for that
|
||||
// transaction
|
||||
if (filter(record)) {
|
||||
FPath *writePath = record.path;
|
||||
if ([record isOverwrite]) {
|
||||
if ([treeRoot contains:writePath]) {
|
||||
FPath *relativePath = [FPath relativePathFrom:treeRoot
|
||||
to:writePath];
|
||||
compoundWrite = [compoundWrite addWrite:record.overwrite
|
||||
atPath:relativePath];
|
||||
} else if ([writePath contains:treeRoot]) {
|
||||
id<FNode> child = [record.overwrite
|
||||
getChild:[FPath relativePathFrom:writePath to:treeRoot]];
|
||||
compoundWrite = [compoundWrite addWrite:child
|
||||
atPath:[FPath empty]];
|
||||
} else {
|
||||
// There is no overlap between root path and write path,
|
||||
// ignore write
|
||||
}
|
||||
} else {
|
||||
if ([treeRoot contains:writePath]) {
|
||||
FPath *relativePath = [FPath relativePathFrom:treeRoot
|
||||
to:writePath];
|
||||
compoundWrite = [compoundWrite addCompoundWrite:record.merge
|
||||
atPath:relativePath];
|
||||
} else if ([writePath contains:treeRoot]) {
|
||||
FPath *relativePath = [FPath relativePathFrom:writePath
|
||||
to:treeRoot];
|
||||
if (relativePath.isEmpty) {
|
||||
compoundWrite =
|
||||
[compoundWrite addCompoundWrite:record.merge
|
||||
atPath:[FPath empty]];
|
||||
} else {
|
||||
id<FNode> child =
|
||||
[record.merge completeNodeAtPath:relativePath];
|
||||
if (child != nil) {
|
||||
// There exists a child in this node that matches the
|
||||
// root path
|
||||
id<FNode> deepNode =
|
||||
[child getChild:[relativePath popFront]];
|
||||
compoundWrite =
|
||||
[compoundWrite addWrite:deepNode
|
||||
atPath:[FPath empty]];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// There is no overlap between root path and write path,
|
||||
// ignore write
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
return compoundWrite;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 FNode;
|
||||
@class FChildrenNode;
|
||||
@class FPath;
|
||||
@class FNamedNode;
|
||||
@class FWriteRecord;
|
||||
@class FWriteTree;
|
||||
@protocol FIndex;
|
||||
@class FCacheNode;
|
||||
|
||||
@interface FWriteTreeRef : NSObject
|
||||
|
||||
- (id)initWithPath:(FPath *)aPath writeTree:(FWriteTree *)tree;
|
||||
|
||||
- (id<FNode>)calculateCompleteEventCacheWithCompleteServerCache:
|
||||
(id<FNode>)completeServerCache;
|
||||
|
||||
- (FChildrenNode *)calculateCompleteEventChildrenWithCompleteServerChildren:
|
||||
(FChildrenNode *)completeServerChildren;
|
||||
|
||||
- (id<FNode>)
|
||||
calculateEventCacheAfterServerOverwriteWithChildPath:(FPath *)childPath
|
||||
existingEventSnap:
|
||||
(id<FNode>)existingEventSnap
|
||||
existingServerSnap:
|
||||
(id<FNode>)existingServerSnap;
|
||||
|
||||
- (id<FNode>)shadowingWriteAtPath:(FPath *)path;
|
||||
|
||||
- (FNamedNode *)calculateNextNodeAfterPost:(FNamedNode *)post
|
||||
completeServerData:(id<FNode>)completeServerData
|
||||
reverse:(BOOL)reverse
|
||||
index:(id<FIndex>)index;
|
||||
|
||||
- (id<FNode>)calculateCompleteChild:(NSString *)childKey
|
||||
cache:(FCacheNode *)existingServerCache;
|
||||
|
||||
- (FWriteTreeRef *)childWriteTreeRef:(NSString *)childKey;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/FWriteTreeRef.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FWriteRecord.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FWriteTree.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FCacheNode.h"
|
||||
#import "FirebaseDatabase/Sources/FIndex.h"
|
||||
#import "FirebaseDatabase/Sources/FNamedNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FChildrenNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
|
||||
@interface FWriteTreeRef ()
|
||||
/**
|
||||
* The path to this particular FWriteTreeRef. Used for calling methods on
|
||||
* writeTree while exposing a simpler interface to callers.
|
||||
*/
|
||||
@property(nonatomic, strong) FPath *path;
|
||||
/**
|
||||
* A reference to the actual tree of the write data. All methods are
|
||||
* pass-through to the tree, but with the appropriate path prefixed.
|
||||
*
|
||||
* This lets us make cheap references to points in the tree for sync points
|
||||
* without having to copy and maintain all of the data.
|
||||
*/
|
||||
@property(nonatomic, strong) FWriteTree *writeTree;
|
||||
@end
|
||||
|
||||
/**
|
||||
* A FWriteTreeRef wraps a FWriteTree and a FPath, for convenient access to a
|
||||
* particular subtree. All the methods just proxy to the underlying FWriteTree.
|
||||
*/
|
||||
@implementation FWriteTreeRef
|
||||
- (id)initWithPath:(FPath *)aPath writeTree:(FWriteTree *)tree {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.path = aPath;
|
||||
self.writeTree = tree;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If possible, returns a complete event cache, using the underlying
|
||||
* server data if possible. In addition, can be used to get a cache that
|
||||
* includes hidden writes, and excludes arbitrary writes. Note that customizing
|
||||
* the returned node can lead to a more expensive calculation.
|
||||
*/
|
||||
- (id<FNode>)calculateCompleteEventCacheWithCompleteServerCache:
|
||||
(id<FNode>)completeServerCache {
|
||||
return [self.writeTree calculateCompleteEventCacheAtPath:self.path
|
||||
completeServerCache:completeServerCache
|
||||
excludeWriteIds:nil
|
||||
includeHiddenWrites:NO];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If possible, returns a children node containing all of the complete
|
||||
* children we have data for. The returned data is a mix of the given server
|
||||
* data and write data.
|
||||
*/
|
||||
- (FChildrenNode *)calculateCompleteEventChildrenWithCompleteServerChildren:
|
||||
(id<FNode>)completeServerChildren {
|
||||
return [self.writeTree
|
||||
calculateCompleteEventChildrenAtPath:self.path
|
||||
completeServerChildren:completeServerChildren];
|
||||
}
|
||||
|
||||
/**
|
||||
* Given that either the underlying server data has updated or the outstanding
|
||||
* writes have been updating, determine what, if anything, needs to be applied
|
||||
* to the event cache.
|
||||
*
|
||||
* Possibilities:
|
||||
*
|
||||
* 1. No writes are shadowing. Events should be raised, the snap to be applied
|
||||
* comes from the server data.
|
||||
*
|
||||
* 2. Some writes are completly shadowing. No events to be raised.
|
||||
*
|
||||
* 3. Is partially shadowed. Events should be raised.
|
||||
*
|
||||
* Either existingEventSnap or existingServerSnap must exist, this is validated
|
||||
* via an assert.
|
||||
*/
|
||||
- (id<FNode>)
|
||||
calculateEventCacheAfterServerOverwriteWithChildPath:(FPath *)childPath
|
||||
existingEventSnap:
|
||||
(id<FNode>)existingEventSnap
|
||||
existingServerSnap:
|
||||
(id<FNode>)existingServerSnap {
|
||||
return [self.writeTree
|
||||
calculateEventCacheAfterServerOverwriteAtPath:self.path
|
||||
childPath:childPath
|
||||
existingEventSnap:existingEventSnap
|
||||
existingServerSnap:existingServerSnap];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a node if there is a complete overwrite for this path. More
|
||||
* specifically, if there is a write at a higher path, this will return the
|
||||
* child of that write relative to the write and this path. Returns nil if there
|
||||
* is no write at this path.
|
||||
*/
|
||||
- (id<FNode>)shadowingWriteAtPath:(FPath *)path {
|
||||
return [self.writeTree shadowingWriteAtPath:[self.path child:path]];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used when processing child remove events on a query. If we
|
||||
* can, we pull in children that are outside the window, but may now be in the
|
||||
* window.
|
||||
*/
|
||||
- (FNamedNode *)calculateNextNodeAfterPost:(FNamedNode *)post
|
||||
completeServerData:(id<FNode>)completeServerData
|
||||
reverse:(BOOL)reverse
|
||||
index:(id<FIndex>)index {
|
||||
return [self.writeTree calculateNextNodeAfterPost:post
|
||||
atPath:self.path
|
||||
completeServerData:completeServerData
|
||||
reverse:reverse
|
||||
index:index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a complete child for a given server snap after applying all user
|
||||
* writes or nil if there is no complete child for this child key.
|
||||
*/
|
||||
- (id<FNode>)calculateCompleteChild:(NSString *)childKey
|
||||
cache:(FCacheNode *)existingServerCache {
|
||||
return [self.writeTree calculateCompleteChildAtPath:self.path
|
||||
childKey:childKey
|
||||
cache:existingServerCache];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a WriteTreeref for a child.
|
||||
*/
|
||||
- (FWriteTreeRef *)childWriteTreeRef:(NSString *)childKey {
|
||||
return
|
||||
[[FWriteTreeRef alloc] initWithPath:[self.path childFromString:childKey]
|
||||
writeTree:self.writeTree];
|
||||
}
|
||||
|
||||
@end
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/Operation/FOperation.h"
|
||||
|
||||
@class FPath;
|
||||
@class FOperationSource;
|
||||
@class FImmutableTree;
|
||||
|
||||
@interface FAckUserWrite : NSObject <FOperation>
|
||||
|
||||
- initWithPath:(FPath *)operationPath
|
||||
affectedTree:(FImmutableTree *)affectedTree
|
||||
revert:(BOOL)shouldRevert;
|
||||
|
||||
@property(nonatomic, strong, readonly) FOperationSource *source;
|
||||
@property(nonatomic, readonly) FOperationType type;
|
||||
@property(nonatomic, strong, readonly) FPath *path;
|
||||
// A FImmutableTree, containing @YES for each affected path. Affected paths
|
||||
// can't overlap.
|
||||
@property(nonatomic, strong, readonly) FImmutableTree *affectedTree;
|
||||
@property(nonatomic, readonly) BOOL revert;
|
||||
|
||||
@end
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/Operation/FAckUserWrite.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Operation/FOperationSource.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FImmutableTree.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
|
||||
@implementation FAckUserWrite
|
||||
|
||||
- (id)initWithPath:(FPath *)operationPath
|
||||
affectedTree:(FImmutableTree *)tree
|
||||
revert:(BOOL)shouldRevert {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self->_source = [FOperationSource userInstance];
|
||||
self->_type = FOperationTypeAckUserWrite;
|
||||
self->_path = operationPath;
|
||||
self->_affectedTree = tree;
|
||||
self->_revert = shouldRevert;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (FAckUserWrite *)operationForChild:(NSString *)childKey {
|
||||
if (![self.path isEmpty]) {
|
||||
NSAssert([self.path.getFront isEqualToString:childKey],
|
||||
@"operationForChild called for unrelated child.");
|
||||
return [[FAckUserWrite alloc] initWithPath:[self.path popFront]
|
||||
affectedTree:self.affectedTree
|
||||
revert:self.revert];
|
||||
} else if (self.affectedTree.value != nil) {
|
||||
NSAssert(self.affectedTree.children.isEmpty,
|
||||
@"affectedTree should not have overlapping affected paths.");
|
||||
// All child locations are affected as well; just return same operation.
|
||||
return self;
|
||||
} else {
|
||||
FImmutableTree *childTree =
|
||||
[self.affectedTree subtreeAtPath:[[FPath alloc] initWith:childKey]];
|
||||
return [[FAckUserWrite alloc] initWithPath:[FPath empty]
|
||||
affectedTree:childTree
|
||||
revert:self.revert];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return
|
||||
[NSString stringWithFormat:
|
||||
@"FAckUserWrite { path=%@, revert=%d, affectedTree=%@ }",
|
||||
self.path, self.revert, self.affectedTree];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/Operation/FOperation.h"
|
||||
|
||||
@class FCompoundWrite;
|
||||
|
||||
@interface FMerge : NSObject <FOperation>
|
||||
|
||||
- (id)initWithSource:(FOperationSource *)aSource
|
||||
path:(FPath *)aPath
|
||||
children:(FCompoundWrite *)children;
|
||||
|
||||
@property(nonatomic, strong, readonly) FOperationSource *source;
|
||||
@property(nonatomic, readonly) FOperationType type;
|
||||
@property(nonatomic, strong, readonly) FPath *path;
|
||||
@property(nonatomic, strong, readonly) FCompoundWrite *children;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/Operation/FMerge.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Operation/FOperationSource.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Operation/FOverwrite.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FCompoundWrite.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
|
||||
@interface FMerge ()
|
||||
@property(nonatomic, strong, readwrite) FOperationSource *source;
|
||||
@property(nonatomic, readwrite) FOperationType type;
|
||||
@property(nonatomic, strong, readwrite) FPath *path;
|
||||
@property(nonatomic, strong) FCompoundWrite *children;
|
||||
@end
|
||||
|
||||
@implementation FMerge
|
||||
|
||||
@synthesize source;
|
||||
@synthesize type;
|
||||
@synthesize path;
|
||||
@synthesize children;
|
||||
|
||||
- (id)initWithSource:(FOperationSource *)aSource
|
||||
path:(FPath *)aPath
|
||||
children:(FCompoundWrite *)someChildren {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.source = aSource;
|
||||
self.type = FOperationTypeMerge;
|
||||
self.path = aPath;
|
||||
self.children = someChildren;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id<FOperation>)operationForChild:(NSString *)childKey {
|
||||
if ([self.path isEmpty]) {
|
||||
FCompoundWrite *childTree = [self.children
|
||||
childCompoundWriteAtPath:[[FPath alloc] initWith:childKey]];
|
||||
if (childTree.isEmpty) {
|
||||
return nil;
|
||||
} else if (childTree.rootWrite != nil) {
|
||||
// We have a snapshot for the child in question. This becomes an
|
||||
// overwrite of the child.
|
||||
return [[FOverwrite alloc] initWithSource:self.source
|
||||
path:[FPath empty]
|
||||
snap:childTree.rootWrite];
|
||||
} else {
|
||||
// This is a merge at a deeper level
|
||||
return [[FMerge alloc] initWithSource:self.source
|
||||
path:[FPath empty]
|
||||
children:childTree];
|
||||
}
|
||||
} else {
|
||||
NSAssert(
|
||||
[self.path.getFront isEqualToString:childKey],
|
||||
@"Can't get a merge for a child not on the path of the operation");
|
||||
return [[FMerge alloc] initWithSource:self.source
|
||||
path:[self.path popFront]
|
||||
children:self.children];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return
|
||||
[NSString stringWithFormat:@"FMerge { path=%@, soruce=%@ children=%@}",
|
||||
self.path, self.source, self.children];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
@class FOperationSource;
|
||||
@class FPath;
|
||||
|
||||
typedef NS_ENUM(NSInteger, FOperationType) {
|
||||
FOperationTypeOverwrite = 0,
|
||||
FOperationTypeMerge = 1,
|
||||
FOperationTypeAckUserWrite = 2,
|
||||
FOperationTypeListenComplete = 3
|
||||
};
|
||||
|
||||
@protocol FOperation <NSObject>
|
||||
@property(nonatomic, strong, readonly) FOperationSource *source;
|
||||
@property(nonatomic, readonly) FOperationType type;
|
||||
@property(nonatomic, strong, readonly) FPath *path;
|
||||
- (id<FOperation>)operationForChild:(NSString *)childKey;
|
||||
@end
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
@class FQueryParams;
|
||||
|
||||
@interface FOperationSource : NSObject
|
||||
|
||||
@property(nonatomic, readonly) BOOL fromUser;
|
||||
@property(nonatomic, readonly) BOOL fromServer;
|
||||
@property(nonatomic, readonly) BOOL isTagged;
|
||||
@property(nonatomic, strong, readonly) FQueryParams *queryParams;
|
||||
|
||||
- initWithFromUser:(BOOL)isFromUser
|
||||
fromServer:(BOOL)isFromServer
|
||||
queryParams:(FQueryParams *)params
|
||||
tagged:(BOOL)isTagged;
|
||||
|
||||
+ (FOperationSource *)userInstance;
|
||||
+ (FOperationSource *)serverInstance;
|
||||
+ (FOperationSource *)forServerTaggedQuery:(FQueryParams *)params;
|
||||
|
||||
@end
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/Operation/FOperationSource.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FQueryParams.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
|
||||
@interface FOperationSource ()
|
||||
@property(nonatomic, readwrite) BOOL fromUser;
|
||||
@property(nonatomic, readwrite) BOOL fromServer;
|
||||
@property(nonatomic, readwrite) BOOL isTagged;
|
||||
@property(nonatomic, strong, readwrite) FQueryParams *queryParams;
|
||||
@end
|
||||
|
||||
@implementation FOperationSource
|
||||
|
||||
@synthesize fromUser;
|
||||
@synthesize fromServer;
|
||||
@synthesize queryParams;
|
||||
|
||||
- (id)initWithFromUser:(BOOL)isFromUser
|
||||
fromServer:(BOOL)isFromServer
|
||||
queryParams:(FQueryParams *)params
|
||||
tagged:(BOOL)tagged {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.fromUser = isFromUser;
|
||||
self.fromServer = isFromServer;
|
||||
self.queryParams = params;
|
||||
self.isTagged = tagged;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (FOperationSource *)userInstance {
|
||||
static FOperationSource *user = nil;
|
||||
static dispatch_once_t userToken;
|
||||
dispatch_once(&userToken, ^{
|
||||
user = [[FOperationSource alloc] initWithFromUser:YES
|
||||
fromServer:NO
|
||||
queryParams:nil
|
||||
tagged:NO];
|
||||
});
|
||||
return user;
|
||||
}
|
||||
|
||||
+ (FOperationSource *)serverInstance {
|
||||
static FOperationSource *server = nil;
|
||||
static dispatch_once_t serverToken;
|
||||
dispatch_once(&serverToken, ^{
|
||||
server = [[FOperationSource alloc] initWithFromUser:NO
|
||||
fromServer:YES
|
||||
queryParams:nil
|
||||
tagged:NO];
|
||||
});
|
||||
return server;
|
||||
}
|
||||
|
||||
+ (FOperationSource *)forServerTaggedQuery:(FQueryParams *)params {
|
||||
return [[FOperationSource alloc] initWithFromUser:NO
|
||||
fromServer:YES
|
||||
queryParams:params
|
||||
tagged:YES];
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"FOperationSource { fromUser=%d, "
|
||||
@"fromServer=%d, queryId=%@, tagged=%d }",
|
||||
self.fromUser, self.fromServer,
|
||||
self.queryParams, self.isTagged];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/Operation/FOperation.h"
|
||||
|
||||
@protocol FNode;
|
||||
|
||||
@interface FOverwrite : NSObject <FOperation>
|
||||
|
||||
- (id)initWithSource:(FOperationSource *)aSource
|
||||
path:(FPath *)aPath
|
||||
snap:(id<FNode>)aSnap;
|
||||
|
||||
@property(nonatomic, strong, readonly) FOperationSource *source;
|
||||
@property(nonatomic, readonly) FOperationType type;
|
||||
@property(nonatomic, strong, readonly) FPath *path;
|
||||
@property(nonatomic, strong, readonly) id<FNode> snap;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/Operation/FOverwrite.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Operation/FOperationSource.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
|
||||
@interface FOverwrite ()
|
||||
@property(nonatomic, strong, readwrite) FOperationSource *source;
|
||||
@property(nonatomic, readwrite) FOperationType type;
|
||||
@property(nonatomic, strong, readwrite) FPath *path;
|
||||
@property(nonatomic, strong) id<FNode> snap;
|
||||
@end
|
||||
|
||||
@implementation FOverwrite
|
||||
|
||||
@synthesize source;
|
||||
@synthesize type;
|
||||
@synthesize path;
|
||||
@synthesize snap;
|
||||
|
||||
- (id)initWithSource:(FOperationSource *)aSource
|
||||
path:(FPath *)aPath
|
||||
snap:(id<FNode>)aSnap {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.source = aSource;
|
||||
self.type = FOperationTypeOverwrite;
|
||||
self.path = aPath;
|
||||
self.snap = aSnap;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (FOverwrite *)operationForChild:(NSString *)childKey {
|
||||
if ([self.path isEmpty]) {
|
||||
return [[FOverwrite alloc]
|
||||
initWithSource:self.source
|
||||
path:[FPath empty]
|
||||
snap:[self.snap getImmediateChild:childKey]];
|
||||
} else {
|
||||
return [[FOverwrite alloc] initWithSource:self.source
|
||||
path:[self.path popFront]
|
||||
snap:self.snap];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString
|
||||
stringWithFormat:@"FOverwrite { path=%@, source=%@, snapshot=%@ }",
|
||||
self.path, self.source, self.snap];
|
||||
}
|
||||
|
||||
@end
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
@interface FIRRetryHelper : NSObject
|
||||
|
||||
- (instancetype)initWithDispatchQueue:(dispatch_queue_t)dispatchQueue
|
||||
minRetryDelayAfterFailure:(NSTimeInterval)minRetryDelayAfterFailure
|
||||
maxRetryDelay:(NSTimeInterval)maxRetryDelay
|
||||
retryExponent:(double)retryExponent
|
||||
jitterFactor:(double)jitterFactor;
|
||||
|
||||
- (void)retry:(void (^)(void))block;
|
||||
|
||||
- (void)cancel;
|
||||
|
||||
- (void)signalSuccess;
|
||||
|
||||
@end
|
||||
+140
@@ -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 "FirebaseDatabase/Sources/Core/Utilities/FIRRetryHelper.h"
|
||||
#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FUtilities.h"
|
||||
|
||||
@interface FIRRetryHelperTask : NSObject
|
||||
|
||||
@property(nonatomic, strong) void (^block)(void);
|
||||
|
||||
@end
|
||||
|
||||
@implementation FIRRetryHelperTask
|
||||
|
||||
- (instancetype)initWithBlock:(void (^)(void))block {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
self->_block = [block copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isCanceled {
|
||||
return self.block == nil;
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
self.block = nil;
|
||||
}
|
||||
|
||||
- (void)execute {
|
||||
if (self.block) {
|
||||
self.block();
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface FIRRetryHelper ()
|
||||
|
||||
@property(nonatomic, strong) dispatch_queue_t dispatchQueue;
|
||||
@property(nonatomic) NSTimeInterval minRetryDelayAfterFailure;
|
||||
@property(nonatomic) NSTimeInterval maxRetryDelay;
|
||||
@property(nonatomic) double retryExponent;
|
||||
@property(nonatomic) double jitterFactor;
|
||||
|
||||
@property(nonatomic) BOOL lastWasSuccess;
|
||||
@property(nonatomic) NSTimeInterval currentRetryDelay;
|
||||
|
||||
@property(nonatomic, strong) FIRRetryHelperTask *scheduledRetry;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FIRRetryHelper
|
||||
|
||||
- (instancetype)initWithDispatchQueue:(dispatch_queue_t)dispatchQueue
|
||||
minRetryDelayAfterFailure:(NSTimeInterval)minRetryDelayAfterFailure
|
||||
maxRetryDelay:(NSTimeInterval)maxRetryDelay
|
||||
retryExponent:(double)retryExponent
|
||||
jitterFactor:(double)jitterFactor {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
self->_dispatchQueue = dispatchQueue;
|
||||
self->_minRetryDelayAfterFailure = minRetryDelayAfterFailure;
|
||||
self->_maxRetryDelay = maxRetryDelay;
|
||||
self->_retryExponent = retryExponent;
|
||||
self->_jitterFactor = jitterFactor;
|
||||
self->_lastWasSuccess = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)retry:(void (^)(void))block {
|
||||
if (self.scheduledRetry != nil) {
|
||||
FFLog(@"I-RDB054001", @"Canceling existing retry attempt");
|
||||
[self.scheduledRetry cancel];
|
||||
self.scheduledRetry = nil;
|
||||
}
|
||||
|
||||
NSTimeInterval delay;
|
||||
if (self.lastWasSuccess) {
|
||||
delay = 0;
|
||||
} else {
|
||||
if (self.currentRetryDelay == 0) {
|
||||
self.currentRetryDelay = self.minRetryDelayAfterFailure;
|
||||
} else {
|
||||
NSTimeInterval newDelay =
|
||||
(self.currentRetryDelay * self.retryExponent);
|
||||
self.currentRetryDelay = MIN(newDelay, self.maxRetryDelay);
|
||||
}
|
||||
|
||||
delay = ((1 - self.jitterFactor) * self.currentRetryDelay) +
|
||||
(self.jitterFactor * self.currentRetryDelay *
|
||||
[FUtilities randomDouble]);
|
||||
FFLog(@"I-RDB054002", @"Scheduling retry in %fs", delay);
|
||||
}
|
||||
self.lastWasSuccess = NO;
|
||||
FIRRetryHelperTask *task = [[FIRRetryHelperTask alloc] initWithBlock:block];
|
||||
self.scheduledRetry = task;
|
||||
dispatch_time_t popTime =
|
||||
dispatch_time(DISPATCH_TIME_NOW, (long long)(delay * NSEC_PER_SEC));
|
||||
dispatch_after(popTime, self.dispatchQueue, ^{
|
||||
if (![task isCanceled]) {
|
||||
self.scheduledRetry = nil;
|
||||
[task execute];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)signalSuccess {
|
||||
self.lastWasSuccess = YES;
|
||||
self.currentRetryDelay = 0;
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
if (self.scheduledRetry != nil) {
|
||||
FFLog(@"I-RDB054003", @"Canceling existing retry attempt");
|
||||
[self.scheduledRetry cancel];
|
||||
self.scheduledRetry = nil;
|
||||
} else {
|
||||
FFLog(@"I-RDB054004", @"No existing retry attempt to cancel");
|
||||
}
|
||||
self.currentRetryDelay = 0;
|
||||
}
|
||||
|
||||
@end
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/Tuples/FTuplePathValue.h"
|
||||
#import "FirebaseDatabase/Sources/third_party/FImmutableSortedDictionary/FImmutableSortedDictionary/FImmutableSortedDictionary.h"
|
||||
|
||||
@interface FImmutableTree : NSObject
|
||||
|
||||
- (id)initWithValue:(id)aValue;
|
||||
- (id)initWithValue:(id)aValue
|
||||
children:(FImmutableSortedDictionary *)childrenMap;
|
||||
|
||||
+ (FImmutableTree *)empty;
|
||||
- (BOOL)isEmpty;
|
||||
|
||||
- (FTuplePathValue *)findRootMostMatchingPath:(FPath *)relativePath
|
||||
predicate:(BOOL (^)(id))predicate;
|
||||
- (FTuplePathValue *)findRootMostValueAndPath:(FPath *)relativePath;
|
||||
- (FImmutableTree *)subtreeAtPath:(FPath *)relativePath;
|
||||
- (FImmutableTree *)setValue:(id)newValue atPath:(FPath *)relativePath;
|
||||
- (FImmutableTree *)removeValueAtPath:(FPath *)relativePath;
|
||||
- (id)valueAtPath:(FPath *)relativePath;
|
||||
- (id)rootMostValueOnPath:(FPath *)path;
|
||||
- (id)rootMostValueOnPath:(FPath *)path matching:(BOOL (^)(id))predicate;
|
||||
- (id)leafMostValueOnPath:(FPath *)path;
|
||||
- (id)leafMostValueOnPath:(FPath *)relativePath
|
||||
matching:(BOOL (^)(id))predicate;
|
||||
- (BOOL)containsValueMatching:(BOOL (^)(id))predicate;
|
||||
- (FImmutableTree *)setTree:(FImmutableTree *)newTree
|
||||
atPath:(FPath *)relativePath;
|
||||
- (id)foldWithBlock:(id (^)(FPath *path, id value,
|
||||
NSDictionary *foldedChildren))block;
|
||||
- (id)findOnPath:(FPath *)path
|
||||
andApplyBlock:(id (^)(FPath *path, id value))block;
|
||||
- (FPath *)forEachOnPath:(FPath *)path
|
||||
whileBlock:(BOOL (^)(FPath *path, id value))block;
|
||||
- (FImmutableTree *)forEachOnPath:(FPath *)path
|
||||
performBlock:(void (^)(FPath *path, id value))block;
|
||||
- (void)forEach:(void (^)(FPath *path, id value))block;
|
||||
- (void)forEachChild:(void (^)(NSString *childKey, id childValue))block;
|
||||
|
||||
@property(nonatomic, strong, readonly) id value;
|
||||
@property(nonatomic, strong, readonly) FImmutableSortedDictionary *children;
|
||||
|
||||
@end
|
||||
+486
@@ -0,0 +1,486 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/Utilities/FImmutableTree.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FUtilities.h"
|
||||
#import "FirebaseDatabase/Sources/third_party/FImmutableSortedDictionary/FImmutableSortedDictionary/FImmutableSortedDictionary.h"
|
||||
|
||||
@interface FImmutableTree ()
|
||||
@property(nonatomic, strong, readwrite) id value;
|
||||
/**
|
||||
* Maps NSString -> FImmutableTree<T>, where <T> is type of value.
|
||||
*/
|
||||
@property(nonatomic, strong, readwrite) FImmutableSortedDictionary *children;
|
||||
@end
|
||||
|
||||
@implementation FImmutableTree
|
||||
@synthesize value;
|
||||
@synthesize children;
|
||||
|
||||
- (id)initWithValue:(id)aValue {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.value = aValue;
|
||||
self.children = [FImmutableTree emptyChildren];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithValue:(id)aValue
|
||||
children:(FImmutableSortedDictionary *)childrenMap {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.value = aValue;
|
||||
self.children = childrenMap;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (FImmutableSortedDictionary *)emptyChildren {
|
||||
static dispatch_once_t emptyChildrenToken;
|
||||
static FImmutableSortedDictionary *emptyChildren;
|
||||
dispatch_once(&emptyChildrenToken, ^{
|
||||
emptyChildren = [FImmutableSortedDictionary
|
||||
dictionaryWithComparator:[FUtilities stringComparator]];
|
||||
});
|
||||
return emptyChildren;
|
||||
}
|
||||
|
||||
+ (FImmutableTree *)empty {
|
||||
static dispatch_once_t emptyImmutableTreeToken;
|
||||
static FImmutableTree *emptyTree = nil;
|
||||
dispatch_once(&emptyImmutableTreeToken, ^{
|
||||
emptyTree = [[FImmutableTree alloc] initWithValue:nil];
|
||||
});
|
||||
return emptyTree;
|
||||
}
|
||||
|
||||
- (BOOL)isEmpty {
|
||||
return self.value == nil && [self.children isEmpty];
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a path and a predicate, return the first node and the path to that node
|
||||
* where the predicate returns true
|
||||
* // TODO Do a perf test. If we're creating a bunch of FTuplePathValue objects
|
||||
* on the way back out, it may be better to pass down a pathSoFar FPath
|
||||
*/
|
||||
- (FTuplePathValue *)findRootMostMatchingPath:(FPath *)relativePath
|
||||
predicate:(BOOL (^)(id value))predicate {
|
||||
if (self.value != nil && predicate(self.value)) {
|
||||
return [[FTuplePathValue alloc] initWithPath:[FPath empty]
|
||||
value:self.value];
|
||||
} else {
|
||||
if ([relativePath isEmpty]) {
|
||||
return nil;
|
||||
} else {
|
||||
NSString *front = [relativePath getFront];
|
||||
FImmutableTree *child = [self.children get:front];
|
||||
if (child != nil) {
|
||||
FTuplePathValue *childExistingPathAndValue =
|
||||
[child findRootMostMatchingPath:[relativePath popFront]
|
||||
predicate:predicate];
|
||||
if (childExistingPathAndValue != nil) {
|
||||
FPath *fullPath = [[[FPath alloc] initWith:front]
|
||||
child:childExistingPathAndValue.path];
|
||||
return [[FTuplePathValue alloc]
|
||||
initWithPath:fullPath
|
||||
value:childExistingPathAndValue.value];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
} else {
|
||||
// No child matching path
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find, if it exists, the shortest subpath of the given path that points a
|
||||
* defined value in the tree
|
||||
*/
|
||||
- (FTuplePathValue *)findRootMostValueAndPath:(FPath *)relativePath {
|
||||
return [self findRootMostMatchingPath:relativePath
|
||||
predicate:^BOOL(__unsafe_unretained id value) {
|
||||
return YES;
|
||||
}];
|
||||
}
|
||||
|
||||
- (id)rootMostValueOnPath:(FPath *)path {
|
||||
return [self rootMostValueOnPath:path
|
||||
matching:^BOOL(id value) {
|
||||
return YES;
|
||||
}];
|
||||
}
|
||||
|
||||
- (id)rootMostValueOnPath:(FPath *)path matching:(BOOL (^)(id))predicate {
|
||||
if (self.value != nil && predicate(self.value)) {
|
||||
return self.value;
|
||||
} else if (path.isEmpty) {
|
||||
return nil;
|
||||
} else {
|
||||
return [[self.children get:path.getFront]
|
||||
rootMostValueOnPath:[path popFront]
|
||||
matching:predicate];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)leafMostValueOnPath:(FPath *)path {
|
||||
return [self leafMostValueOnPath:path
|
||||
matching:^BOOL(id value) {
|
||||
return YES;
|
||||
}];
|
||||
}
|
||||
|
||||
- (id)leafMostValueOnPath:(FPath *)relativePath
|
||||
matching:(BOOL (^)(id))predicate {
|
||||
__block id currentValue = self.value;
|
||||
__block FImmutableTree *currentTree = self;
|
||||
[relativePath enumerateComponentsUsingBlock:^(NSString *key, BOOL *stop) {
|
||||
currentTree = [currentTree.children get:key];
|
||||
if (currentTree == nil) {
|
||||
*stop = YES;
|
||||
} else {
|
||||
id treeValue = currentTree.value;
|
||||
if (treeValue != nil && predicate(treeValue)) {
|
||||
currentValue = treeValue;
|
||||
}
|
||||
}
|
||||
}];
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
- (BOOL)containsValueMatching:(BOOL (^)(id))predicate {
|
||||
if (self.value != nil && predicate(self.value)) {
|
||||
return YES;
|
||||
} else {
|
||||
__block BOOL found = NO;
|
||||
[self.children enumerateKeysAndObjectsUsingBlock:^(
|
||||
NSString *key, FImmutableTree *subtree, BOOL *stop) {
|
||||
found = [subtree containsValueMatching:predicate];
|
||||
if (found)
|
||||
*stop = YES;
|
||||
}];
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
- (FImmutableTree *)subtreeAtPath:(FPath *)relativePath {
|
||||
if ([relativePath isEmpty]) {
|
||||
return self;
|
||||
} else {
|
||||
NSString *front = [relativePath getFront];
|
||||
FImmutableTree *childTree = [self.children get:front];
|
||||
if (childTree != nil) {
|
||||
return [childTree subtreeAtPath:[relativePath popFront]];
|
||||
} else {
|
||||
return [FImmutableTree empty];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value at the specified path
|
||||
*/
|
||||
- (FImmutableTree *)setValue:(id)newValue atPath:(FPath *)relativePath {
|
||||
if ([relativePath isEmpty]) {
|
||||
return [[FImmutableTree alloc] initWithValue:newValue
|
||||
children:self.children];
|
||||
} else {
|
||||
NSString *front = [relativePath getFront];
|
||||
FImmutableTree *child = [self.children get:front];
|
||||
if (child == nil) {
|
||||
child = [FImmutableTree empty];
|
||||
}
|
||||
FImmutableTree *newChild = [child setValue:newValue
|
||||
atPath:[relativePath popFront]];
|
||||
FImmutableSortedDictionary *newChildren =
|
||||
[self.children insertKey:front withValue:newChild];
|
||||
return [[FImmutableTree alloc] initWithValue:self.value
|
||||
children:newChildren];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the value at the specified path
|
||||
*/
|
||||
- (FImmutableTree *)removeValueAtPath:(FPath *)relativePath {
|
||||
if ([relativePath isEmpty]) {
|
||||
if ([self.children isEmpty]) {
|
||||
return [FImmutableTree empty];
|
||||
} else {
|
||||
return [[FImmutableTree alloc] initWithValue:nil
|
||||
children:self.children];
|
||||
}
|
||||
} else {
|
||||
NSString *front = [relativePath getFront];
|
||||
FImmutableTree *child = [self.children get:front];
|
||||
if (child) {
|
||||
FImmutableTree *newChild =
|
||||
[child removeValueAtPath:[relativePath popFront]];
|
||||
FImmutableSortedDictionary *newChildren;
|
||||
if ([newChild isEmpty]) {
|
||||
newChildren = [self.children removeKey:front];
|
||||
} else {
|
||||
newChildren = [self.children insertKey:front
|
||||
withValue:newChild];
|
||||
}
|
||||
if (self.value == nil && [newChildren isEmpty]) {
|
||||
return [FImmutableTree empty];
|
||||
} else {
|
||||
return [[FImmutableTree alloc] initWithValue:self.value
|
||||
children:newChildren];
|
||||
}
|
||||
} else {
|
||||
return self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the tree
|
||||
*/
|
||||
- (id)valueAtPath:(FPath *)relativePath {
|
||||
if ([relativePath isEmpty]) {
|
||||
return self.value;
|
||||
} else {
|
||||
NSString *front = [relativePath getFront];
|
||||
FImmutableTree *child = [self.children get:front];
|
||||
if (child) {
|
||||
return [child valueAtPath:[relativePath popFront]];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the subtree at the specified path with the given new tree
|
||||
*/
|
||||
- (FImmutableTree *)setTree:(FImmutableTree *)newTree
|
||||
atPath:(FPath *)relativePath {
|
||||
if ([relativePath isEmpty]) {
|
||||
return newTree;
|
||||
} else {
|
||||
NSString *front = [relativePath getFront];
|
||||
FImmutableTree *child = [self.children get:front];
|
||||
if (child == nil) {
|
||||
child = [FImmutableTree empty];
|
||||
}
|
||||
FImmutableTree *newChild = [child setTree:newTree
|
||||
atPath:[relativePath popFront]];
|
||||
FImmutableSortedDictionary *newChildren;
|
||||
if ([newChild isEmpty]) {
|
||||
newChildren = [self.children removeKey:front];
|
||||
} else {
|
||||
newChildren = [self.children insertKey:front withValue:newChild];
|
||||
}
|
||||
return [[FImmutableTree alloc] initWithValue:self.value
|
||||
children:newChildren];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a depth first fold on this tree. Transforms a tree into a single
|
||||
* value, given a function that operates on the path to a node, an optional
|
||||
* current value, and a map of the child names to folded subtrees
|
||||
*/
|
||||
- (id)foldWithBlock:(id (^)(FPath *path, id value,
|
||||
NSDictionary *foldedChildren))block {
|
||||
return [self foldWithPathSoFar:[FPath empty] withBlock:block];
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive helper for public facing foldWithBlock: method
|
||||
*/
|
||||
- (id)foldWithPathSoFar:(FPath *)pathSoFar
|
||||
withBlock:(id (^)(FPath *path, id value,
|
||||
NSDictionary *foldedChildren))block {
|
||||
__block NSMutableDictionary *accum = [[NSMutableDictionary alloc] init];
|
||||
[self.children
|
||||
enumerateKeysAndObjectsUsingBlock:^(
|
||||
NSString *childKey, FImmutableTree *childTree, BOOL *stop) {
|
||||
accum[childKey] =
|
||||
[childTree foldWithPathSoFar:[pathSoFar childFromString:childKey]
|
||||
withBlock:block];
|
||||
}];
|
||||
return block(pathSoFar, self.value, accum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first matching value on the given path. Return the result of
|
||||
* applying block to it.
|
||||
*/
|
||||
- (id)findOnPath:(FPath *)path
|
||||
andApplyBlock:(id (^)(FPath *path, id value))block {
|
||||
return [self findOnPath:path pathSoFar:[FPath empty] andApplyBlock:block];
|
||||
}
|
||||
|
||||
- (id)findOnPath:(FPath *)pathToFollow
|
||||
pathSoFar:(FPath *)pathSoFar
|
||||
andApplyBlock:(id (^)(FPath *path, id value))block {
|
||||
id result = self.value ? block(pathSoFar, self.value) : nil;
|
||||
if (result != nil) {
|
||||
return result;
|
||||
} else {
|
||||
if ([pathToFollow isEmpty]) {
|
||||
return nil;
|
||||
} else {
|
||||
NSString *front = [pathToFollow getFront];
|
||||
FImmutableTree *nextChild = [self.children get:front];
|
||||
if (nextChild != nil) {
|
||||
return [nextChild findOnPath:[pathToFollow popFront]
|
||||
pathSoFar:[pathSoFar childFromString:front]
|
||||
andApplyBlock:block];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Call the block on each value along the path for as long as that function
|
||||
* returns true
|
||||
* @return The path to the deepest location inspected
|
||||
*/
|
||||
- (FPath *)forEachOnPath:(FPath *)path whileBlock:(BOOL (^)(FPath *, id))block {
|
||||
return [self forEachOnPath:path pathSoFar:[FPath empty] whileBlock:block];
|
||||
}
|
||||
|
||||
- (FPath *)forEachOnPath:(FPath *)pathToFollow
|
||||
pathSoFar:(FPath *)pathSoFar
|
||||
whileBlock:(BOOL (^)(FPath *, id))block {
|
||||
if ([pathToFollow isEmpty]) {
|
||||
if (self.value) {
|
||||
block(pathSoFar, self.value);
|
||||
}
|
||||
return pathSoFar;
|
||||
} else {
|
||||
BOOL shouldContinue = YES;
|
||||
if (self.value) {
|
||||
shouldContinue = block(pathSoFar, self.value);
|
||||
}
|
||||
if (shouldContinue) {
|
||||
NSString *front = [pathToFollow getFront];
|
||||
FImmutableTree *nextChild = [self.children get:front];
|
||||
if (nextChild) {
|
||||
return
|
||||
[nextChild forEachOnPath:[pathToFollow popFront]
|
||||
pathSoFar:[pathSoFar childFromString:front]
|
||||
whileBlock:block];
|
||||
} else {
|
||||
return pathSoFar;
|
||||
}
|
||||
} else {
|
||||
return pathSoFar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (FImmutableTree *)forEachOnPath:(FPath *)path
|
||||
performBlock:(void (^)(FPath *path, id value))block {
|
||||
return [self forEachOnPath:path pathSoFar:[FPath empty] performBlock:block];
|
||||
}
|
||||
|
||||
- (FImmutableTree *)forEachOnPath:(FPath *)pathToFollow
|
||||
pathSoFar:(FPath *)pathSoFar
|
||||
performBlock:(void (^)(FPath *path, id value))block {
|
||||
if ([pathToFollow isEmpty]) {
|
||||
return self;
|
||||
} else {
|
||||
if (self.value) {
|
||||
block(pathSoFar, self.value);
|
||||
}
|
||||
NSString *front = [pathToFollow getFront];
|
||||
FImmutableTree *nextChild = [self.children get:front];
|
||||
if (nextChild) {
|
||||
return [nextChild forEachOnPath:[pathToFollow popFront]
|
||||
pathSoFar:[pathSoFar childFromString:front]
|
||||
performBlock:block];
|
||||
} else {
|
||||
return [FImmutableTree empty];
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Calls the given block for each node in the tree that has a value. Called in
|
||||
* depth-first order
|
||||
*/
|
||||
- (void)forEach:(void (^)(FPath *path, id value))block {
|
||||
[self forEachPathSoFar:[FPath empty] withBlock:block];
|
||||
}
|
||||
|
||||
- (void)forEachPathSoFar:(FPath *)pathSoFar
|
||||
withBlock:(void (^)(FPath *path, id value))block {
|
||||
[self.children
|
||||
enumerateKeysAndObjectsUsingBlock:^(
|
||||
NSString *childKey, FImmutableTree *childTree, BOOL *stop) {
|
||||
[childTree forEachPathSoFar:[pathSoFar childFromString:childKey]
|
||||
withBlock:block];
|
||||
}];
|
||||
if (self.value) {
|
||||
block(pathSoFar, self.value);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)forEachChild:(void (^)(NSString *childKey, id childValue))block {
|
||||
[self.children
|
||||
enumerateKeysAndObjectsUsingBlock:^(
|
||||
NSString *childKey, FImmutableTree *childTree, BOOL *stop) {
|
||||
if (childTree.value) {
|
||||
block(childKey, childTree.value);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if (![object isKindOfClass:[FImmutableTree class]]) {
|
||||
return NO;
|
||||
}
|
||||
FImmutableTree *other = (FImmutableTree *)object;
|
||||
return (self.value == other.value || [self.value isEqual:other.value]) &&
|
||||
[self.children isEqual:other.children];
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
return self.children.hash * 31 + [self.value hash];
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
NSMutableString *string = [[NSMutableString alloc] init];
|
||||
[string appendString:@"FImmutableTree { value="];
|
||||
[string appendString:(self.value ? [self.value description] : @"<nil>")];
|
||||
[string appendString:@", children={"];
|
||||
[self.children
|
||||
enumerateKeysAndObjectsUsingBlock:^(
|
||||
NSString *childKey, FImmutableTree *childTree, BOOL *stop) {
|
||||
[string appendString:@" "];
|
||||
[string appendString:childKey];
|
||||
[string appendString:@"="];
|
||||
[string appendString:[childTree.value description]];
|
||||
}];
|
||||
[string appendString:@" } }"];
|
||||
return [NSString stringWithString:string];
|
||||
}
|
||||
|
||||
- (NSString *)debugDescription {
|
||||
return [self description];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
@interface FPath : NSObject <NSCopying>
|
||||
|
||||
+ (FPath *)relativePathFrom:(FPath *)outer to:(FPath *)inner;
|
||||
+ (FPath *)empty;
|
||||
+ (FPath *)pathWithString:(NSString *)string;
|
||||
|
||||
- (id)initWith:(NSString *)path;
|
||||
- (id)initWithPieces:(NSArray *)somePieces andPieceNum:(NSInteger)aPieceNum;
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone;
|
||||
|
||||
- (void)enumerateComponentsUsingBlock:(void (^)(NSString *key,
|
||||
BOOL *stop))block;
|
||||
- (NSString *)getFront;
|
||||
- (NSUInteger)length;
|
||||
- (FPath *)popFront;
|
||||
- (NSString *)getBack;
|
||||
- (NSString *)toString;
|
||||
- (NSString *)toStringWithTrailingSlash;
|
||||
- (NSString *)wireFormat;
|
||||
- (FPath *)parent;
|
||||
- (FPath *)child:(FPath *)childPathObj;
|
||||
- (FPath *)childFromString:(NSString *)childPath;
|
||||
- (BOOL)isEmpty;
|
||||
- (BOOL)contains:(FPath *)other;
|
||||
- (NSComparisonResult)compare:(FPath *)other;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
|
||||
#import "FirebaseDatabase/Sources/Utilities/FUtilities.h"
|
||||
|
||||
@interface FPath ()
|
||||
|
||||
@property(nonatomic, readwrite, assign) NSInteger pieceNum;
|
||||
@property(nonatomic, strong) NSArray *pieces;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FPath
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initializers
|
||||
|
||||
+ (FPath *)relativePathFrom:(FPath *)outer to:(FPath *)inner {
|
||||
NSString *outerFront = [outer getFront];
|
||||
NSString *innerFront = [inner getFront];
|
||||
if (outerFront == nil) {
|
||||
return inner;
|
||||
} else if ([outerFront isEqualToString:innerFront]) {
|
||||
return [self relativePathFrom:[outer popFront] to:[inner popFront]];
|
||||
} else {
|
||||
@throw [[NSException alloc]
|
||||
initWithName:@"FirebaseDatabaseInternalError"
|
||||
reason:[NSString
|
||||
stringWithFormat:
|
||||
@"innerPath (%@) is not within outerPath (%@)",
|
||||
inner, outer]
|
||||
userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
+ (FPath *)pathWithString:(NSString *)string {
|
||||
return [[FPath alloc] initWith:string];
|
||||
}
|
||||
|
||||
- (id)initWith:(NSString *)path {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
NSArray *pathPieces = [path componentsSeparatedByString:@"/"];
|
||||
NSMutableArray *newPieces = [[NSMutableArray alloc] init];
|
||||
for (NSInteger i = 0; i < pathPieces.count; i++) {
|
||||
NSString *piece = [pathPieces objectAtIndex:i];
|
||||
if (piece.length > 0) {
|
||||
[newPieces addObject:piece];
|
||||
}
|
||||
}
|
||||
|
||||
self.pieces = newPieces;
|
||||
self.pieceNum = 0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithPieces:(NSArray *)somePieces andPieceNum:(NSInteger)aPieceNum {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.pieceNum = aPieceNum;
|
||||
self.pieces = somePieces;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone {
|
||||
// Immutable, so it's safe to return self
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [self toString];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Public methods
|
||||
|
||||
- (NSString *)getFront {
|
||||
if (self.pieceNum >= self.pieces.count) {
|
||||
return nil;
|
||||
}
|
||||
return [self.pieces objectAtIndex:self.pieceNum];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The number of segments in this path
|
||||
*/
|
||||
- (NSUInteger)length {
|
||||
return self.pieces.count - self.pieceNum;
|
||||
}
|
||||
|
||||
- (FPath *)popFront {
|
||||
NSInteger newPieceNum = self.pieceNum;
|
||||
if (newPieceNum < self.pieces.count) {
|
||||
newPieceNum++;
|
||||
}
|
||||
return [[FPath alloc] initWithPieces:self.pieces andPieceNum:newPieceNum];
|
||||
}
|
||||
|
||||
- (NSString *)getBack {
|
||||
if (self.pieceNum < self.pieces.count) {
|
||||
return [self.pieces lastObject];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)toString {
|
||||
return [self toStringWithTrailingSlash:NO];
|
||||
}
|
||||
|
||||
- (NSString *)toStringWithTrailingSlash {
|
||||
return [self toStringWithTrailingSlash:YES];
|
||||
}
|
||||
|
||||
- (NSString *)toStringWithTrailingSlash:(BOOL)trailingSlash {
|
||||
NSMutableString *pathString = [[NSMutableString alloc] init];
|
||||
for (NSInteger i = self.pieceNum; i < self.pieces.count; i++) {
|
||||
[pathString appendString:@"/"];
|
||||
[pathString appendString:[self.pieces objectAtIndex:i]];
|
||||
}
|
||||
if ([pathString length] == 0) {
|
||||
return @"/";
|
||||
} else {
|
||||
if (trailingSlash) {
|
||||
[pathString appendString:@"/"];
|
||||
}
|
||||
return pathString;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)wireFormat {
|
||||
if ([self isEmpty]) {
|
||||
return @"/";
|
||||
} else {
|
||||
NSMutableString *pathString = [[NSMutableString alloc] init];
|
||||
for (NSInteger i = self.pieceNum; i < self.pieces.count; i++) {
|
||||
if (i > self.pieceNum) {
|
||||
[pathString appendString:@"/"];
|
||||
}
|
||||
[pathString appendString:[self.pieces objectAtIndex:i]];
|
||||
}
|
||||
return pathString;
|
||||
}
|
||||
}
|
||||
|
||||
- (FPath *)parent {
|
||||
if (self.pieceNum >= self.pieces.count) {
|
||||
return nil;
|
||||
} else {
|
||||
NSMutableArray *newPieces = [[NSMutableArray alloc] init];
|
||||
for (NSInteger i = self.pieceNum; i < self.pieces.count - 1; i++) {
|
||||
[newPieces addObject:[self.pieces objectAtIndex:i]];
|
||||
}
|
||||
return [[FPath alloc] initWithPieces:newPieces andPieceNum:0];
|
||||
}
|
||||
}
|
||||
|
||||
- (FPath *)child:(FPath *)childPathObj {
|
||||
NSMutableArray *newPieces = [[NSMutableArray alloc] init];
|
||||
for (NSInteger i = self.pieceNum; i < self.pieces.count; i++) {
|
||||
[newPieces addObject:[self.pieces objectAtIndex:i]];
|
||||
}
|
||||
|
||||
for (NSInteger i = childPathObj.pieceNum; i < childPathObj.pieces.count;
|
||||
i++) {
|
||||
[newPieces addObject:[childPathObj.pieces objectAtIndex:i]];
|
||||
}
|
||||
|
||||
return [[FPath alloc] initWithPieces:newPieces andPieceNum:0];
|
||||
}
|
||||
|
||||
- (FPath *)childFromString:(NSString *)childPath {
|
||||
NSMutableArray *newPieces = [[NSMutableArray alloc] init];
|
||||
for (NSInteger i = self.pieceNum; i < self.pieces.count; i++) {
|
||||
[newPieces addObject:[self.pieces objectAtIndex:i]];
|
||||
}
|
||||
|
||||
NSArray *pathPieces = [childPath componentsSeparatedByString:@"/"];
|
||||
for (unsigned int i = 0; i < pathPieces.count; i++) {
|
||||
NSString *piece = [pathPieces objectAtIndex:i];
|
||||
if (piece.length > 0) {
|
||||
[newPieces addObject:piece];
|
||||
}
|
||||
}
|
||||
|
||||
return [[FPath alloc] initWithPieces:newPieces andPieceNum:0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if there are no segments in this path
|
||||
*/
|
||||
- (BOOL)isEmpty {
|
||||
return self.pieceNum >= self.pieces.count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Singleton to represent an empty path
|
||||
*/
|
||||
+ (FPath *)empty {
|
||||
static dispatch_once_t oneEmptyPath;
|
||||
static FPath *emptyPath;
|
||||
dispatch_once(&oneEmptyPath, ^{
|
||||
emptyPath = [[FPath alloc] initWith:@""];
|
||||
});
|
||||
return emptyPath;
|
||||
}
|
||||
|
||||
- (BOOL)contains:(FPath *)other {
|
||||
if (self.length > other.length) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSInteger i = self.pieceNum;
|
||||
NSInteger j = other.pieceNum;
|
||||
while (i < self.pieces.count) {
|
||||
NSString *thisSeg = [self.pieces objectAtIndex:i];
|
||||
NSString *otherSeg = [other.pieces objectAtIndex:j];
|
||||
if (![thisSeg isEqualToString:otherSeg]) {
|
||||
return NO;
|
||||
}
|
||||
++i;
|
||||
++j;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)enumerateComponentsUsingBlock:(void (^)(NSString *, BOOL *))block {
|
||||
BOOL stop = NO;
|
||||
for (NSInteger i = self.pieceNum; !stop && i < self.pieces.count; i++) {
|
||||
block(self.pieces[i], &stop);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSComparisonResult)compare:(FPath *)other {
|
||||
NSInteger myCount = self.pieces.count;
|
||||
NSInteger otherCount = other.pieces.count;
|
||||
for (NSInteger i = self.pieceNum, j = other.pieceNum;
|
||||
i < myCount && j < otherCount; i++, j++) {
|
||||
NSComparisonResult comparison = [FUtilities compareKey:self.pieces[i]
|
||||
toKey:other.pieces[j]];
|
||||
if (comparison != NSOrderedSame) {
|
||||
return comparison;
|
||||
}
|
||||
}
|
||||
if (self.length < other.length) {
|
||||
return NSOrderedAscending;
|
||||
} else if (other.length < self.length) {
|
||||
return NSOrderedDescending;
|
||||
} else {
|
||||
NSAssert(self.length == other.length,
|
||||
@"Paths must be the same lengths");
|
||||
return NSOrderedSame;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return YES if paths are the same
|
||||
*/
|
||||
- (BOOL)isEqual:(id)other {
|
||||
if (other == self) {
|
||||
return YES;
|
||||
}
|
||||
if (!other || ![other isKindOfClass:[self class]]) {
|
||||
return NO;
|
||||
}
|
||||
FPath *otherPath = (FPath *)other;
|
||||
if (self.length != otherPath.length) {
|
||||
return NO;
|
||||
}
|
||||
for (NSUInteger i = self.pieceNum, j = otherPath.pieceNum;
|
||||
i < self.pieces.count; i++, j++) {
|
||||
if (![self.pieces[i] isEqualToString:otherPath.pieces[j]]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
NSUInteger hashCode = 0;
|
||||
for (NSInteger i = self.pieceNum; i < self.pieces.count; i++) {
|
||||
hashCode = hashCode * 37 + [self.pieces[i] hash];
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FTreeNode.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FTree : NSObject
|
||||
|
||||
- (id)init;
|
||||
- (id)initWithName:(NSString *)aName
|
||||
withParent:(FTree *)aParent
|
||||
withNode:(FTreeNode *)aNode;
|
||||
|
||||
- (FTree *)subTree:(FPath *)path;
|
||||
- (id)getValue;
|
||||
- (void)setValue:(id)value;
|
||||
- (void)clear;
|
||||
- (BOOL)hasChildren;
|
||||
- (BOOL)isEmpty;
|
||||
- (void)forEachChildMutationSafe:(void (^)(FTree *))action;
|
||||
- (void)forEachChild:(void (^)(FTree *))action;
|
||||
- (void)forEachDescendant:(void (^)(FTree *))action;
|
||||
- (void)forEachDescendant:(void (^)(FTree *))action
|
||||
includeSelf:(BOOL)incSelf
|
||||
childrenFirst:(BOOL)childFirst;
|
||||
- (BOOL)forEachAncestor:(BOOL (^)(FTree *))action;
|
||||
- (BOOL)forEachAncestor:(BOOL (^)(FTree *))action includeSelf:(BOOL)incSelf;
|
||||
- (void)forEachImmediateDescendantWithValue:(void (^)(FTree *))action;
|
||||
- (BOOL)valueExistsAtOrAbove:(FPath *)path;
|
||||
- (FPath *)path;
|
||||
- (void)updateParents;
|
||||
- (void)updateChild:(NSString *)childName withNode:(FTree *)child;
|
||||
|
||||
@property(nonatomic, strong) NSString *name;
|
||||
@property(nonatomic, strong) FTree *parent;
|
||||
@property(nonatomic, strong) FTreeNode *node;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/Utilities/FTree.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FTreeNode.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FUtilities.h"
|
||||
|
||||
@implementation FTree
|
||||
|
||||
@synthesize name;
|
||||
@synthesize parent;
|
||||
@synthesize node;
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.name = @"";
|
||||
self.parent = nil;
|
||||
self.node = [[FTreeNode alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithName:(NSString *)aName
|
||||
withParent:(FTree *)aParent
|
||||
withNode:(FTreeNode *)aNode {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.name = aName != nil ? aName : @"";
|
||||
self.parent = aParent != nil ? aParent : nil;
|
||||
self.node = aNode != nil ? aNode : [[FTreeNode alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (FTree *)subTree:(FPath *)path {
|
||||
FTree *child = self;
|
||||
NSString *next = [path getFront];
|
||||
while (next != nil) {
|
||||
FTreeNode *childNode = child.node.children[next];
|
||||
if (childNode == nil) {
|
||||
childNode = [[FTreeNode alloc] init];
|
||||
}
|
||||
child = [[FTree alloc] initWithName:next
|
||||
withParent:child
|
||||
withNode:childNode];
|
||||
path = [path popFront];
|
||||
next = [path getFront];
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
- (id)getValue {
|
||||
return self.node.value;
|
||||
}
|
||||
|
||||
- (void)setValue:(id)value {
|
||||
self.node.value = value;
|
||||
[self updateParents];
|
||||
}
|
||||
|
||||
- (void)clear {
|
||||
self.node.value = nil;
|
||||
[self.node.children removeAllObjects];
|
||||
self.node.childCount = 0;
|
||||
[self updateParents];
|
||||
}
|
||||
|
||||
- (BOOL)hasChildren {
|
||||
return self.node.childCount > 0;
|
||||
}
|
||||
|
||||
- (BOOL)isEmpty {
|
||||
return [self getValue] == nil && ![self hasChildren];
|
||||
}
|
||||
|
||||
- (void)forEachChild:(void (^)(FTree *))action {
|
||||
for (NSString *key in self.node.children) {
|
||||
action([[FTree alloc]
|
||||
initWithName:key
|
||||
withParent:self
|
||||
withNode:[self.node.children objectForKey:key]]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)forEachChildMutationSafe:(void (^)(FTree *))action {
|
||||
for (NSString *key in [self.node.children copy]) {
|
||||
action([[FTree alloc]
|
||||
initWithName:key
|
||||
withParent:self
|
||||
withNode:[self.node.children objectForKey:key]]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)forEachDescendant:(void (^)(FTree *))action {
|
||||
[self forEachDescendant:action includeSelf:NO childrenFirst:NO];
|
||||
}
|
||||
|
||||
- (void)forEachDescendant:(void (^)(FTree *))action
|
||||
includeSelf:(BOOL)incSelf
|
||||
childrenFirst:(BOOL)childFirst {
|
||||
if (incSelf && !childFirst) {
|
||||
action(self);
|
||||
}
|
||||
|
||||
[self forEachChild:^(FTree *child) {
|
||||
[child forEachDescendant:action includeSelf:YES childrenFirst:childFirst];
|
||||
}];
|
||||
|
||||
if (incSelf && childFirst) {
|
||||
action(self);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)forEachAncestor:(BOOL (^)(FTree *))action {
|
||||
return [self forEachAncestor:action includeSelf:NO];
|
||||
}
|
||||
|
||||
- (BOOL)forEachAncestor:(BOOL (^)(FTree *))action includeSelf:(BOOL)incSelf {
|
||||
FTree *aNode = (incSelf) ? self : self.parent;
|
||||
while (aNode != nil) {
|
||||
if (action(aNode)) {
|
||||
return YES;
|
||||
}
|
||||
aNode = aNode.parent;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)forEachImmediateDescendantWithValue:(void (^)(FTree *))action {
|
||||
[self forEachChild:^(FTree *child) {
|
||||
if ([child getValue] != nil) {
|
||||
action(child);
|
||||
} else {
|
||||
[child forEachImmediateDescendantWithValue:action];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)valueExistsAtOrAbove:(FPath *)path {
|
||||
FTreeNode *aNode = self.node;
|
||||
while (aNode != nil) {
|
||||
if (aNode.value != nil) {
|
||||
return YES;
|
||||
}
|
||||
aNode = [aNode.children objectForKey:path.getFront];
|
||||
path = [path popFront];
|
||||
}
|
||||
// XXX Check with Michael if this is correct; deviates from JS.
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (FPath *)path {
|
||||
return [[FPath alloc]
|
||||
initWith:(self.parent == nil)
|
||||
? self.name
|
||||
: [NSString stringWithFormat:@"%@/%@", [self.parent path],
|
||||
self.name]];
|
||||
}
|
||||
|
||||
- (void)updateParents {
|
||||
[self.parent updateChild:self.name withNode:self];
|
||||
}
|
||||
|
||||
- (void)updateChild:(NSString *)childName withNode:(FTree *)child {
|
||||
BOOL childEmpty = [child isEmpty];
|
||||
BOOL childExists = self.node.children[childName] != nil;
|
||||
if (childEmpty && childExists) {
|
||||
[self.node.children removeObjectForKey:childName];
|
||||
self.node.childCount = self.node.childCount - 1;
|
||||
[self updateParents];
|
||||
} else if (!childEmpty && !childExists) {
|
||||
[self.node.children setObject:child.node forKey:childName];
|
||||
self.node.childCount = self.node.childCount + 1;
|
||||
[self updateParents];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -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 <Foundation/Foundation.h>
|
||||
|
||||
@interface FTreeNode : NSObject
|
||||
|
||||
@property(nonatomic, strong) NSMutableDictionary *children;
|
||||
@property(nonatomic, readwrite, assign) int childCount;
|
||||
@property(nonatomic, strong) id value;
|
||||
|
||||
@end
|
||||
@@ -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 "FirebaseDatabase/Sources/Core/Utilities/FTreeNode.h"
|
||||
|
||||
@implementation FTreeNode
|
||||
|
||||
@synthesize children;
|
||||
@synthesize childCount;
|
||||
@synthesize value;
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.children = [[NSMutableDictionary alloc] init];
|
||||
self.childCount = 0;
|
||||
self.value = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 FNode;
|
||||
@class FIndexedNode;
|
||||
@class FPath;
|
||||
|
||||
/**
|
||||
* A cache node only stores complete children. Additionally it holds a flag
|
||||
* whether the node can be considered fully initialized in the sense that we
|
||||
* know at one point in time, this represented a valid state of the world, e.g.
|
||||
* initialized with data from the server, or a complete overwrite by the client.
|
||||
* It is not necessarily complete because it may have been from a tagged query.
|
||||
* The filtered flag also tracks whether a node potentially had children removed
|
||||
* due to a filter.
|
||||
*/
|
||||
@interface FCacheNode : NSObject
|
||||
|
||||
- (id)initWithIndexedNode:(FIndexedNode *)indexedNode
|
||||
isFullyInitialized:(BOOL)fullyInitialized
|
||||
isFiltered:(BOOL)filtered;
|
||||
|
||||
- (BOOL)isCompleteForPath:(FPath *)path;
|
||||
- (BOOL)isCompleteForChild:(NSString *)childKey;
|
||||
|
||||
@property(nonatomic, readonly) BOOL isFullyInitialized;
|
||||
@property(nonatomic, readonly) BOOL isFiltered;
|
||||
@property(nonatomic, strong, readonly) FIndexedNode *indexedNode;
|
||||
@property(nonatomic, strong, readonly) id<FNode> node;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/View/FCacheNode.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FEmptyNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FIndexedNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
|
||||
@interface FCacheNode ()
|
||||
@property(nonatomic, readwrite) BOOL isFullyInitialized;
|
||||
@property(nonatomic, readwrite) BOOL isFiltered;
|
||||
@property(nonatomic, strong, readwrite) FIndexedNode *indexedNode;
|
||||
@end
|
||||
|
||||
@implementation FCacheNode
|
||||
- (id)initWithIndexedNode:(FIndexedNode *)indexedNode
|
||||
isFullyInitialized:(BOOL)fullyInitialized
|
||||
isFiltered:(BOOL)filtered {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.indexedNode = indexedNode;
|
||||
self.isFullyInitialized = fullyInitialized;
|
||||
self.isFiltered = filtered;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isCompleteForPath:(FPath *)path {
|
||||
if (path.isEmpty) {
|
||||
return self.isFullyInitialized && !self.isFiltered;
|
||||
} else {
|
||||
NSString *childKey = [path getFront];
|
||||
return [self isCompleteForChild:childKey];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isCompleteForChild:(NSString *)childKey {
|
||||
return (self.isFullyInitialized && !self.isFiltered) ||
|
||||
[self.node hasChild:childKey];
|
||||
}
|
||||
|
||||
- (id<FNode>)node {
|
||||
return self.indexedNode.node;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/View/FEvent.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol FEventRegistration;
|
||||
|
||||
@interface FCancelEvent : NSObject <FEvent>
|
||||
|
||||
- initWithEventRegistration:(id<FEventRegistration>)eventRegistration
|
||||
error:(NSError *)error
|
||||
path:(FPath *)path;
|
||||
|
||||
@property(nonatomic, strong, readonly) NSError *error;
|
||||
@property(nonatomic, strong, readonly) FPath *path;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/View/FCancelEvent.h"
|
||||
#import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FEventRegistration.h"
|
||||
|
||||
@interface FCancelEvent ()
|
||||
@property(nonatomic, strong) id<FEventRegistration> eventRegistration;
|
||||
@property(nonatomic, strong, readwrite) NSError *error;
|
||||
@property(nonatomic, strong, readwrite) FPath *path;
|
||||
@end
|
||||
|
||||
@implementation FCancelEvent
|
||||
|
||||
@synthesize eventRegistration;
|
||||
@synthesize error;
|
||||
@synthesize path;
|
||||
|
||||
- (id)initWithEventRegistration:(id<FEventRegistration>)registration
|
||||
error:(NSError *)anError
|
||||
path:(FPath *)aPath {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.eventRegistration = registration;
|
||||
self.error = anError;
|
||||
self.path = aPath;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)fireEventOnQueue:(dispatch_queue_t)queue {
|
||||
[self.eventRegistration fireEvent:self queue:queue];
|
||||
}
|
||||
|
||||
- (BOOL)isCancelEvent {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"%@: cancel", self.path];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabaseReference.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FIndexedNode.h"
|
||||
#import "FirebaseDatabase/Sources/Snapshot/FNode.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FChange : NSObject
|
||||
|
||||
@property(nonatomic, readonly) FIRDataEventType type;
|
||||
@property(nonatomic, strong, readonly) FIndexedNode *indexedNode;
|
||||
@property(nonatomic, strong, readonly) NSString *childKey;
|
||||
@property(nonatomic, strong, readonly) NSString *prevKey;
|
||||
@property(nonatomic, strong, readonly) FIndexedNode *oldIndexedNode;
|
||||
|
||||
- (id)initWithType:(FIRDataEventType)type
|
||||
indexedNode:(FIndexedNode *)indexedNode;
|
||||
- (id)initWithType:(FIRDataEventType)type
|
||||
indexedNode:(FIndexedNode *)indexedNode
|
||||
childKey:(NSString *)childKey;
|
||||
- (id)initWithType:(FIRDataEventType)type
|
||||
indexedNode:(FIndexedNode *)indexedNode
|
||||
childKey:(NSString *)childKey
|
||||
oldIndexedNode:(FIndexedNode *)oldIndexedNode;
|
||||
|
||||
- (FChange *)changeWithPrevKey:(NSString *)prevKey;
|
||||
@end
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/View/FChange.h"
|
||||
|
||||
@interface FChange ()
|
||||
|
||||
@property(nonatomic, strong, readwrite) NSString *prevKey;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FChange
|
||||
|
||||
- (id)initWithType:(FIRDataEventType)type
|
||||
indexedNode:(FIndexedNode *)indexedNode {
|
||||
return [self initWithType:type
|
||||
indexedNode:indexedNode
|
||||
childKey:nil
|
||||
oldIndexedNode:nil];
|
||||
}
|
||||
|
||||
- (id)initWithType:(FIRDataEventType)type
|
||||
indexedNode:(FIndexedNode *)indexedNode
|
||||
childKey:(NSString *)childKey {
|
||||
return [self initWithType:type
|
||||
indexedNode:indexedNode
|
||||
childKey:childKey
|
||||
oldIndexedNode:nil];
|
||||
}
|
||||
|
||||
- (id)initWithType:(FIRDataEventType)type
|
||||
indexedNode:(FIndexedNode *)indexedNode
|
||||
childKey:(NSString *)childKey
|
||||
oldIndexedNode:(FIndexedNode *)oldIndexedNode {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
self->_type = type;
|
||||
self->_indexedNode = indexedNode;
|
||||
self->_childKey = childKey;
|
||||
self->_oldIndexedNode = oldIndexedNode;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (FChange *)changeWithPrevKey:(NSString *)prevKey {
|
||||
FChange *newChange = [[FChange alloc] initWithType:self.type
|
||||
indexedNode:self.indexedNode
|
||||
childKey:self.childKey
|
||||
oldIndexedNode:self.oldIndexedNode];
|
||||
newChange.prevKey = prevKey;
|
||||
return newChange;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"event: %d, data: %@", (int)self.type,
|
||||
[self.indexedNode.node val]];
|
||||
}
|
||||
|
||||
@end
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/View/FEventRegistration.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FTypedefs.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class FRepo;
|
||||
|
||||
@interface FChildEventRegistration : NSObject <FEventRegistration>
|
||||
|
||||
- (id)initWithRepo:(FRepo *)repo
|
||||
handle:(FIRDatabaseHandle)fHandle
|
||||
callbacks:(NSDictionary *)callbackBlocks
|
||||
cancelCallback:(fbt_void_nserror)cancelCallbackBlock;
|
||||
|
||||
/**
|
||||
* Maps FIRDataEventType (as NSNumber) to fbt_void_datasnapshot_nsstring
|
||||
*/
|
||||
@property(nonatomic, copy, readonly) NSDictionary *callbacks;
|
||||
@property(nonatomic, copy, readonly) fbt_void_nserror cancelCallback;
|
||||
@property(nonatomic, readonly) FIRDatabaseHandle handle;
|
||||
|
||||
@end
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/View/FChildEventRegistration.h"
|
||||
#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
|
||||
#import "FirebaseDatabase/Sources/Api/Private/FIRDataSnapshot_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Api/Private/FIRDatabaseQuery_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FQueryParams.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FQuerySpec.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FCancelEvent.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FDataEvent.h"
|
||||
|
||||
@interface FChildEventRegistration ()
|
||||
@property(nonatomic, strong) FRepo *repo;
|
||||
@property(nonatomic, copy, readwrite) NSDictionary *callbacks;
|
||||
@property(nonatomic, copy, readwrite) fbt_void_nserror cancelCallback;
|
||||
@property(nonatomic, readwrite) FIRDatabaseHandle handle;
|
||||
@end
|
||||
|
||||
@implementation FChildEventRegistration
|
||||
|
||||
- (id)initWithRepo:(id)repo
|
||||
handle:(FIRDatabaseHandle)fHandle
|
||||
callbacks:(NSDictionary *)callbackBlocks
|
||||
cancelCallback:(fbt_void_nserror)cancelCallbackBlock {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.repo = repo;
|
||||
self.handle = fHandle;
|
||||
self.callbacks = callbackBlocks;
|
||||
self.cancelCallback = cancelCallbackBlock;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)responseTo:(FIRDataEventType)eventType {
|
||||
return self.callbacks != nil &&
|
||||
[self.callbacks
|
||||
objectForKey:[NSNumber numberWithInteger:eventType]] != nil;
|
||||
}
|
||||
|
||||
- (FDataEvent *)createEventFrom:(FChange *)change query:(FQuerySpec *)query {
|
||||
FIRDatabaseReference *ref = [[FIRDatabaseReference alloc]
|
||||
initWithRepo:self.repo
|
||||
path:[query.path childFromString:change.childKey]];
|
||||
FIRDataSnapshot *snapshot =
|
||||
[[FIRDataSnapshot alloc] initWithRef:ref
|
||||
indexedNode:change.indexedNode];
|
||||
|
||||
FDataEvent *eventData =
|
||||
[[FDataEvent alloc] initWithEventType:change.type
|
||||
eventRegistration:self
|
||||
dataSnapshot:snapshot
|
||||
prevName:change.prevKey];
|
||||
return eventData;
|
||||
}
|
||||
|
||||
- (void)fireEvent:(id<FEvent>)event queue:(dispatch_queue_t)queue {
|
||||
if ([event isCancelEvent]) {
|
||||
FCancelEvent *cancelEvent = event;
|
||||
FFLog(@"I-RDB061001", @"Raising cancel value event on %@", event.path);
|
||||
NSAssert(
|
||||
self.cancelCallback != nil,
|
||||
@"Raising a cancel event on a listener with no cancel callback");
|
||||
dispatch_async(queue, ^{
|
||||
self.cancelCallback(cancelEvent.error);
|
||||
});
|
||||
} else if (self.callbacks != nil) {
|
||||
FDataEvent *dataEvent = event;
|
||||
FFLog(@"I-RDB061002", @"Raising event callback (%ld) on %@",
|
||||
(long)dataEvent.eventType, dataEvent.path);
|
||||
fbt_void_datasnapshot_nsstring callback = [self.callbacks
|
||||
objectForKey:[NSNumber numberWithInteger:dataEvent.eventType]];
|
||||
|
||||
if (callback != nil) {
|
||||
dispatch_async(queue, ^{
|
||||
callback(dataEvent.snapshot, dataEvent.prevName);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (FCancelEvent *)createCancelEventFromError:(NSError *)error
|
||||
path:(FPath *)path {
|
||||
if (self.cancelCallback != nil) {
|
||||
return [[FCancelEvent alloc] initWithEventRegistration:self
|
||||
error:error
|
||||
path:path];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)matches:(id<FEventRegistration>)other {
|
||||
return self.handle == NSNotFound || other.handle == NSNotFound ||
|
||||
self.handle == other.handle;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/View/FEvent.h"
|
||||
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDataSnapshot.h"
|
||||
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDatabaseReference.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/Tuples/FTupleUserCallback.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol FEventRegistration;
|
||||
@protocol FIndex;
|
||||
|
||||
@interface FDataEvent : NSObject <FEvent>
|
||||
|
||||
- initWithEventType:(FIRDataEventType)type
|
||||
eventRegistration:(id<FEventRegistration>)eventRegistration
|
||||
dataSnapshot:(FIRDataSnapshot *)dataSnapshot;
|
||||
- initWithEventType:(FIRDataEventType)type
|
||||
eventRegistration:(id<FEventRegistration>)eventRegistration
|
||||
dataSnapshot:(FIRDataSnapshot *)snapshot
|
||||
prevName:(NSString *)prevName;
|
||||
|
||||
@property(nonatomic, strong, readonly) id<FEventRegistration> eventRegistration;
|
||||
@property(nonatomic, strong, readonly) FIRDataSnapshot *snapshot;
|
||||
@property(nonatomic, strong, readonly) NSString *prevName;
|
||||
@property(nonatomic, readonly) FIRDataEventType eventType;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/View/FDataEvent.h"
|
||||
#import "FirebaseDatabase/Sources/Api/Private/FIRDatabaseQuery_Private.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FEventRegistration.h"
|
||||
#import "FirebaseDatabase/Sources/FIndex.h"
|
||||
|
||||
@interface FDataEvent ()
|
||||
@property(nonatomic, strong, readwrite) id<FEventRegistration>
|
||||
eventRegistration;
|
||||
@property(nonatomic, strong, readwrite) FIRDataSnapshot *snapshot;
|
||||
@property(nonatomic, strong, readwrite) NSString *prevName;
|
||||
@property(nonatomic, readwrite) FIRDataEventType eventType;
|
||||
@end
|
||||
|
||||
@implementation FDataEvent
|
||||
|
||||
@synthesize eventRegistration;
|
||||
@synthesize snapshot;
|
||||
@synthesize prevName;
|
||||
@synthesize eventType;
|
||||
|
||||
- (id)initWithEventType:(FIRDataEventType)type
|
||||
eventRegistration:(id<FEventRegistration>)registration
|
||||
dataSnapshot:(FIRDataSnapshot *)dataSnapshot {
|
||||
return [self initWithEventType:type
|
||||
eventRegistration:registration
|
||||
dataSnapshot:dataSnapshot
|
||||
prevName:nil];
|
||||
}
|
||||
|
||||
- (id)initWithEventType:(FIRDataEventType)type
|
||||
eventRegistration:(id<FEventRegistration>)registration
|
||||
dataSnapshot:(FIRDataSnapshot *)dataSnapshot
|
||||
prevName:(NSString *)previousName {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.eventRegistration = registration;
|
||||
self.snapshot = dataSnapshot;
|
||||
self.prevName = previousName;
|
||||
self.eventType = type;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (FPath *)path {
|
||||
// Used for logging, so delay calculation
|
||||
FIRDatabaseReference *ref = self.snapshot.ref;
|
||||
if (self.eventType == FIRDataEventTypeValue) {
|
||||
return ref.path;
|
||||
} else {
|
||||
return ref.parent.path;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)fireEventOnQueue:(dispatch_queue_t)queue {
|
||||
[self.eventRegistration fireEvent:self queue:queue];
|
||||
}
|
||||
|
||||
- (BOOL)isCancelEvent {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"event %d, data: %@", (int)eventType,
|
||||
[snapshot value]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -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 "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDataEventType.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class FPath;
|
||||
|
||||
@protocol FEvent <NSObject>
|
||||
- (FPath *)path;
|
||||
- (void)fireEventOnQueue:(dispatch_queue_t)queue;
|
||||
- (BOOL)isCancelEvent;
|
||||
- (NSString *)description;
|
||||
@end
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Utilities/FTypedefs.h"
|
||||
|
||||
@class FPath;
|
||||
@class FRepo;
|
||||
@class FIRDatabaseConfig;
|
||||
|
||||
/**
|
||||
* Left as instance methods rather than class methods so that we could
|
||||
* potentially callback on different queues for different repos. This is
|
||||
* semi-parallel to JS's FEventQueue
|
||||
*/
|
||||
@interface FEventRaiser : NSObject
|
||||
|
||||
- (id)initWithQueue:(dispatch_queue_t)queue;
|
||||
|
||||
- (void)raiseEvents:(NSArray *)eventDataList;
|
||||
- (void)raiseCallback:(fbt_void_void)callback;
|
||||
- (void)raiseCallbacks:(NSArray *)callbackList;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/View/FEventRaiser.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FRepo.h"
|
||||
#import "FirebaseDatabase/Sources/Core/FRepoManager.h"
|
||||
#import "FirebaseDatabase/Sources/Core/View/FDataEvent.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FTypedefs.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/FUtilities.h"
|
||||
#import "FirebaseDatabase/Sources/Utilities/Tuples/FTupleUserCallback.h"
|
||||
|
||||
@interface FEventRaiser ()
|
||||
|
||||
@property(nonatomic, strong) dispatch_queue_t queue;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This class exists for symmetry with other clients, but since events are
|
||||
* async, we don't need to do the complicated stuff the JS client does to
|
||||
* preserve event order.
|
||||
*/
|
||||
@implementation FEventRaiser
|
||||
|
||||
- (id)init {
|
||||
[NSException raise:NSInternalInconsistencyException
|
||||
format:@"Can't use default constructor"];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)initWithQueue:(dispatch_queue_t)queue {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
self->_queue = queue;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)raiseEvents:(NSArray *)eventDataList {
|
||||
for (id<FEvent> event in eventDataList) {
|
||||
[event fireEventOnQueue:self.queue];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)raiseCallback:(fbt_void_void)callback {
|
||||
dispatch_async(self.queue, callback);
|
||||
}
|
||||
|
||||
- (void)raiseCallbacks:(NSArray *)callbackList {
|
||||
for (fbt_void_void callback in callbackList) {
|
||||
dispatch_async(self.queue, callback);
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)raiseCallbacks:(NSArray *)callbackList queue:(dispatch_queue_t)queue {
|
||||
for (fbt_void_void callback in callbackList) {
|
||||
dispatch_async(queue, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 "FirebaseDatabase/Sources/Core/View/FChange.h"
|
||||
#import "FirebaseDatabase/Sources/Public/FirebaseDatabase/FIRDataEventType.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol FEvent;
|
||||
@class FDataEvent;
|
||||
@class FCancelEvent;
|
||||
@class FQuerySpec;
|
||||
|
||||
@protocol FEventRegistration <NSObject>
|
||||
- (BOOL)responseTo:(FIRDataEventType)eventType;
|
||||
- (FDataEvent *)createEventFrom:(FChange *)change query:(FQuerySpec *)query;
|
||||
- (void)fireEvent:(id<FEvent>)event queue:(dispatch_queue_t)queue;
|
||||
- (FCancelEvent *)createCancelEventFromError:(NSError *)error
|
||||
path:(FPath *)path;
|
||||
/**
|
||||
* Used to figure out what event registration match the event registration that
|
||||
* needs to be removed.
|
||||
*/
|
||||
- (BOOL)matches:(id<FEventRegistration>)other;
|
||||
@property(nonatomic, readonly) FIRDatabaseHandle handle;
|
||||
@end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user