This commit is contained in:
Yao
2024-12-20 17:49:45 +08:00
parent 86b0363ce1
commit 654d456c7d
7011 changed files with 1705926 additions and 7 deletions

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2023 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 "FIRAggregateField.h"
#import "FIRFieldPath.h"
#include <string>
#include "Firestore/core/src/model/aggregate_alias.h"
#include "Firestore/core/src/model/aggregate_field.h"
namespace model = firebase::firestore::model;
NS_ASSUME_NONNULL_BEGIN
@interface FIRAggregateField (Internal)
- (model::AggregateField)createInternalValue;
- (model::AggregateAlias)createAlias;
- (const std::string)name;
- (const FIRFieldPath *)fieldPath;
@end
/**
* FIRAggregateField class for sum aggregations. Exposed internally so code can do isKindOfClass
* checks on it.
*/
@interface FSTSumAggregateField : FIRAggregateField
- (instancetype)init NS_UNAVAILABLE;
- (id)initWithFieldPath:(FIRFieldPath *)fieldPath;
@end
/**
* FIRAggregateField class for average aggregations. Exposed internally so code can do isKindOfClass
* checks on it.
*/
@interface FSTAverageAggregateField : FIRAggregateField
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithFieldPath:(FIRFieldPath *)fieldPath;
@end
/**
* FIRAggregateField class for count aggregations. Exposed internally so code can do isKindOfClass
* checks on it.
*/
@interface FSTCountAggregateField : FIRAggregateField
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initPrivate;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2023 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 "FIRAggregateField.h"
#import "Firestore/Source/API/FIRAggregateField+Internal.h"
#import "Firestore/Source/API/FIRFieldPath+Internal.h"
#import "Firestore/core/src/model/aggregate_field.h"
using firebase::firestore::model::AggregateField;
NS_ASSUME_NONNULL_BEGIN
#pragma mark - FIRAggregateField
@interface FIRAggregateField ()
@property(nonatomic, strong) FIRFieldPath *_fieldPath;
@property(nonatomic, readwrite) model::AggregateField::OpKind _op;
- (instancetype)initWithFieldPathAndKind:(nullable FIRFieldPath *)fieldPath
opKind:(model::AggregateField::OpKind)op;
@end
@implementation FIRAggregateField
- (instancetype)initWithFieldPathAndKind:(nullable FIRFieldPath *)fieldPath
opKind:(model::AggregateField::OpKind)op {
if (self = [super init]) {
self._fieldPath = fieldPath;
self._op = op;
}
return self;
}
- (FIRFieldPath *)fieldPath {
return [self _fieldPath];
}
- (const std::string)name {
switch ([self _op]) {
case AggregateField::OpKind::Sum:
return std::string("sum");
case AggregateField::OpKind::Avg:
return std::string("avg");
case AggregateField::OpKind::Count:
return std::string("count");
}
UNREACHABLE();
}
- (model::AggregateField)createInternalValue {
if (self.fieldPath != Nil) {
return model::AggregateField([self _op], [self createAlias], self.fieldPath.internalValue);
} else {
return model::AggregateField([self _op], [self createAlias]);
}
}
- (model::AggregateAlias)createAlias {
if (self.fieldPath != Nil) {
return model::AggregateAlias([self name] + std::string{"_"} +
self.fieldPath.internalValue.CanonicalString());
} else {
return model::AggregateAlias([self name]);
}
}
+ (instancetype)aggregateFieldForCount {
return [[FSTCountAggregateField alloc] initPrivate];
}
+ (instancetype)aggregateFieldForSumOfField:(NSString *)field {
return [self aggregateFieldForSumOfFieldPath:[FIRFieldPath pathWithDotSeparatedString:field]];
}
+ (instancetype)aggregateFieldForSumOfFieldPath:(FIRFieldPath *)fieldPath {
return [[FSTSumAggregateField alloc] initWithFieldPath:fieldPath];
}
+ (instancetype)aggregateFieldForAverageOfField:(NSString *)field {
return [self aggregateFieldForAverageOfFieldPath:[FIRFieldPath pathWithDotSeparatedString:field]];
}
+ (instancetype)aggregateFieldForAverageOfFieldPath:(FIRFieldPath *)fieldPath {
return [[FSTAverageAggregateField alloc] initWithFieldPath:fieldPath];
}
@end
#pragma mark - FSTSumAggregateField
@implementation FSTSumAggregateField
- (instancetype)initWithFieldPath:(FIRFieldPath *)fieldPath {
self = [super initWithFieldPathAndKind:fieldPath opKind:model::AggregateField::OpKind::Sum];
return self;
}
@end
#pragma mark - FSTAverageAggregateField
@implementation FSTAverageAggregateField
- (instancetype)initWithFieldPath:(FIRFieldPath *)fieldPath {
self = [super initWithFieldPathAndKind:fieldPath opKind:model::AggregateField::OpKind::Avg];
return self;
}
@end
#pragma mark - FSTCountAggregateField
@implementation FSTCountAggregateField
- (instancetype)initPrivate {
self = [super initWithFieldPathAndKind:Nil opKind:model::AggregateField::OpKind::Count];
return self;
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2022 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 "FIRAggregateQuery.h"
#import "FIRAggregateField.h"
#import "FIRQuery.h"
NS_ASSUME_NONNULL_BEGIN
@interface FIRAggregateQuery (/* init */)
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithQuery:(FIRQuery *)query
aggregateFields:(NSArray<FIRAggregateField *> *)aggregations
NS_DESIGNATED_INITIALIZER;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,91 @@
/*
* Copyright 2022 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 "FIRAggregateQuery+Internal.h"
#import "Firestore/Source/API/FIRAggregateField+Internal.h"
#import "Firestore/Source/API/FIRAggregateQuerySnapshot+Internal.h"
#import "Firestore/Source/API/FIRQuery+Internal.h"
#include "Firestore/core/src/api/aggregate_query.h"
#include "Firestore/core/src/util/error_apple.h"
using firebase::firestore::api::AggregateQuery;
using firebase::firestore::model::AggregateField;
using firebase::firestore::model::ObjectValue;
using firebase::firestore::util::StatusOr;
NS_ASSUME_NONNULL_BEGIN
#pragma mark - FIRAggregateQuery
@implementation FIRAggregateQuery {
FIRQuery *_query;
std::unique_ptr<AggregateQuery> _aggregateQuery;
}
- (instancetype)initWithQuery:(FIRQuery *)query
aggregateFields:(NSArray<FIRAggregateField *> *)aggregateFields {
if (self = [super init]) {
_query = query;
std::vector<AggregateField> _aggregateFields;
for (FIRAggregateField *field in aggregateFields) {
_aggregateFields.push_back([field createInternalValue]);
}
_aggregateQuery =
absl::make_unique<AggregateQuery>(query.apiQuery.Aggregate(std::move(_aggregateFields)));
}
return self;
}
#pragma mark - NSObject Methods
- (BOOL)isEqual:(nullable id)other {
if (other == self) return YES;
if (![[other class] isEqual:[self class]]) return NO;
auto otherQuery = static_cast<FIRAggregateQuery *>(other);
return [_query isEqual:otherQuery->_query] && *_aggregateQuery == *(otherQuery->_aggregateQuery);
}
- (NSUInteger)hash {
return _aggregateQuery->Hash();
}
#pragma mark - Public Methods
- (FIRQuery *)query {
return _query;
}
- (void)aggregationWithSource:(FIRAggregateSource)source
completion:(void (^)(FIRAggregateQuerySnapshot *_Nullable snapshot,
NSError *_Nullable error))completion {
_aggregateQuery->GetAggregate([self, completion](const StatusOr<ObjectValue> &result) {
if (result.ok()) {
completion([[FIRAggregateQuerySnapshot alloc] initWithObject:result.ValueOrDie() query:self],
nil);
} else {
completion(nil, MakeNSError(result.status()));
}
});
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2022 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 "FIRAggregateQuerySnapshot.h"
#import "FIRAggregateField.h"
#import "FIRDocumentSnapshot.h"
#include "Firestore/core/src/api/api_fwd.h"
@class FIRAggregateQuery;
namespace model = firebase::firestore::model;
NS_ASSUME_NONNULL_BEGIN
@interface FIRAggregateQuerySnapshot (/* init */)
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithObject:(model::ObjectValue)result
query:(FIRAggregateQuery *)query NS_DESIGNATED_INITIALIZER;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2022 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 "FIRAggregateQuerySnapshot+Internal.h"
#import "FIRAggregateQuery.h"
#import "FIRQuery.h"
#import "Firestore/Source/API/FIRAggregateField+Internal.h"
#import "Firestore/Source/API/FIRFieldPath+Internal.h"
#import "Firestore/Source/API/FIRFirestore+Internal.h"
#import "Firestore/Source/API/FSTUserDataWriter.h"
#include "absl/types/optional.h"
#include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
#include "Firestore/core/src/model/aggregate_alias.h"
#include "Firestore/core/src/model/field_path.h"
#include "Firestore/core/src/util/exception.h"
using firebase::firestore::google_firestore_v1_Value;
using firebase::firestore::model::AggregateAlias;
using firebase::firestore::model::FieldPath;
using firebase::firestore::model::ObjectValue;
using firebase::firestore::util::ThrowInvalidArgument;
NS_ASSUME_NONNULL_BEGIN
@implementation FIRAggregateQuerySnapshot {
ObjectValue _result;
FIRAggregateQuery *_query;
}
- (instancetype)initWithObject:(ObjectValue)result query:(FIRAggregateQuery *)query {
if (self = [super init]) {
_result = std::move(result);
_query = query;
}
return self;
}
#pragma mark - NSObject Methods
- (BOOL)isEqual:(nullable id)other {
if (other == self) return YES;
if (![[other class] isEqual:[self class]]) return NO;
auto otherSnap = static_cast<FIRAggregateQuerySnapshot *>(other);
return _result == otherSnap->_result && [_query isEqual:otherSnap->_query];
}
- (NSUInteger)hash {
NSUInteger result = [_query hash];
result = 31 * result + _result.Hash();
return result;
}
#pragma mark - Public Methods
- (NSNumber *)count {
return (NSNumber *)[self valueForAggregateField:[FIRAggregateField aggregateFieldForCount]];
}
- (FIRAggregateQuery *)query {
return _query;
}
- (id)valueForAggregateField:(FIRAggregateField *)aggregateField {
FIRServerTimestampBehavior serverTimestampBehavior = FIRServerTimestampBehaviorNone;
AggregateAlias alias = [aggregateField createAlias];
absl::optional<google_firestore_v1_Value> fieldValue = _result.Get(alias.StringValue());
if (!fieldValue) {
std::string path{""};
if (aggregateField.fieldPath) {
path = [aggregateField.fieldPath internalValue].CanonicalString();
}
ThrowInvalidArgument("'%s(%s)' was not requested in the aggregation query.",
[aggregateField name], path);
}
FSTUserDataWriter *dataWriter =
[[FSTUserDataWriter alloc] initWithFirestore:_query.query.firestore.wrapped
serverTimestampBehavior:serverTimestampBehavior];
return [dataWriter convertedValue:*fieldValue];
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,48 @@
/*
* 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 "FIRCollectionReference.h"
#include <memory>
#include "Firestore/core/src/api/api_fwd.h"
namespace firebase {
namespace firestore {
namespace model {
class ResourcePath;
} // namespace model
} // namespace firestore
} // namespace firebase
namespace api = firebase::firestore::api;
namespace model = firebase::firestore::model;
NS_ASSUME_NONNULL_BEGIN
/** Internal FIRCollectionReference API we don't want exposed in our public header files. */
@interface FIRCollectionReference (/* Init */)
- (instancetype)initWithReference:(api::CollectionReference &&)reference NS_DESIGNATED_INITIALIZER;
// Mark the super class designated initializer unavailable.
- (instancetype)initWithQuery:(api::Query &&)query NS_UNAVAILABLE;
- (instancetype)initWithPath:(model::ResourcePath)path
firestore:(std::shared_ptr<api::Firestore>)firestore;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,135 @@
/*
* 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 "FIRCollectionReference.h"
#include <utility>
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
#import "Firestore/Source/API/FIRFirestore+Internal.h"
#import "Firestore/Source/API/FIRQuery+Internal.h"
#import "Firestore/Source/API/FSTUserDataReader.h"
#include "Firestore/core/src/api/collection_reference.h"
#include "Firestore/core/src/api/document_reference.h"
#include "Firestore/core/src/core/user_data.h"
#include "Firestore/core/src/model/resource_path.h"
#include "Firestore/core/src/util/error_apple.h"
#include "Firestore/core/src/util/exception.h"
#include "Firestore/core/src/util/string_apple.h"
using firebase::firestore::api::CollectionReference;
using firebase::firestore::api::DocumentReference;
using firebase::firestore::core::ParsedSetData;
using firebase::firestore::model::ResourcePath;
using firebase::firestore::util::MakeCallback;
using firebase::firestore::util::MakeNSString;
using firebase::firestore::util::MakeString;
using firebase::firestore::util::ThrowInvalidArgument;
NS_ASSUME_NONNULL_BEGIN
@implementation FIRCollectionReference
- (instancetype)initWithReference:(CollectionReference &&)reference {
return [super initWithQuery:std::move(reference)];
}
- (instancetype)initWithPath:(ResourcePath)path
firestore:(std::shared_ptr<api::Firestore>)firestore {
CollectionReference ref(std::move(path), std::move(firestore));
return [self initWithReference:std::move(ref)];
}
// Override the designated initializer from the super class.
- (instancetype)initWithQuery:(__unused api::Query &&)query {
HARD_FAIL("Use FIRCollectionReference initWithPath: initializer.");
}
// NSObject Methods
- (BOOL)isEqual:(nullable id)other {
if (other == self) return YES;
if (![[other class] isEqual:[self class]]) return NO;
return [self isEqualToReference:other];
}
- (BOOL)isEqualToReference:(nullable FIRCollectionReference *)otherReference {
if (self == otherReference) return YES;
if (otherReference == nil) return NO;
return self.reference == otherReference.reference;
}
- (NSUInteger)hash {
return self.reference.Hash();
}
- (const CollectionReference &)reference {
// TODO(wilhuff): Use some alternate method for doing this.
//
// Casting from Query& to CollectionReference& when the value is actually a
// Query violates aliasing rules and is technically undefined behavior.
// Nevertheless this works on Clang so this is good enough for now.
return static_cast<const CollectionReference &>(self.apiQuery);
}
- (NSString *)collectionID {
return MakeNSString(self.reference.collection_id());
}
- (FIRDocumentReference *_Nullable)parent {
absl::optional<DocumentReference> parent = self.reference.parent();
if (!parent) {
return nil;
}
return [[FIRDocumentReference alloc] initWithReference:std::move(*parent)];
}
- (NSString *)path {
return MakeNSString(self.reference.path());
}
- (FIRDocumentReference *)documentWithPath:(NSString *)documentPath {
if (!documentPath) {
ThrowInvalidArgument("Document path cannot be nil.");
}
if (!documentPath.length) {
ThrowInvalidArgument("Document path cannot be empty.");
}
DocumentReference child = self.reference.Document(MakeString(documentPath));
return [[FIRDocumentReference alloc] initWithReference:std::move(child)];
}
- (FIRDocumentReference *)addDocumentWithData:(NSDictionary<NSString *, id> *)data {
return [self addDocumentWithData:data completion:nil];
}
- (FIRDocumentReference *)addDocumentWithData:(NSDictionary<NSString *, id> *)data
completion:
(nullable void (^)(NSError *_Nullable error))completion {
ParsedSetData parsed = [self.firestore.dataReader parsedSetData:data];
DocumentReference docRef =
self.reference.AddDocument(std::move(parsed), MakeCallback(completion));
return [[FIRDocumentReference alloc] initWithReference:std::move(docRef)];
}
- (FIRDocumentReference *)documentWithAutoID {
return [[FIRDocumentReference alloc] initWithReference:self.reference.Document()];
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -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 "FIRDocumentChange.h"
#import <Foundation/Foundation.h>
#include "Firestore/core/src/api/document_change.h"
namespace api = firebase::firestore::api;
NS_ASSUME_NONNULL_BEGIN
@interface FIRDocumentChange (/* Init */)
- (instancetype)initWithDocumentChange:(api::DocumentChange &&)documentChange
NS_DESIGNATED_INITIALIZER;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,90 @@
/*
* 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 "Firestore/Source/API/FIRDocumentChange+Internal.h"
#import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h"
#include "Firestore/core/src/api/document_change.h"
#include "Firestore/core/src/util/hard_assert.h"
using firebase::firestore::api::DocumentChange;
NS_ASSUME_NONNULL_BEGIN
namespace {
/**
* Converts from C++ document change indexes to Objective-C document change
* indexes. Objective-C's NSNotFound is signed NSIntegerMax, not unsigned -1.
*/
constexpr NSUInteger MakeIndex(size_t index) {
return index == DocumentChange::npos ? NSNotFound : index;
}
} // namespace
@implementation FIRDocumentChange {
DocumentChange _documentChange;
}
- (instancetype)initWithDocumentChange:(DocumentChange &&)documentChange {
if (self = [super init]) {
_documentChange = std::move(documentChange);
}
return self;
}
- (BOOL)isEqual:(nullable id)other {
if (other == self) return YES;
if (![other isKindOfClass:[FIRDocumentChange class]]) return NO;
FIRDocumentChange *change = (FIRDocumentChange *)other;
return _documentChange == change->_documentChange;
}
- (NSUInteger)hash {
return _documentChange.Hash();
}
- (FIRDocumentChangeType)type {
switch (_documentChange.type()) {
case DocumentChange::Type::Added:
return FIRDocumentChangeTypeAdded;
case DocumentChange::Type::Modified:
return FIRDocumentChangeTypeModified;
case DocumentChange::Type::Removed:
return FIRDocumentChangeTypeRemoved;
}
HARD_FAIL("Unknown DocumentChange::Type: %s", _documentChange.type());
}
- (FIRQueryDocumentSnapshot *)document {
return [[FIRQueryDocumentSnapshot alloc] initWithSnapshot:_documentChange.document()];
}
- (NSUInteger)oldIndex {
return MakeIndex(_documentChange.old_index());
}
- (NSUInteger)newIndex {
return MakeIndex(_documentChange.new_index());
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRDocumentReference.h"
#include <memory>
#include "Firestore/core/src/api/api_fwd.h"
namespace firebase {
namespace firestore {
namespace model {
class DocumentKey;
class ResourcePath;
} // namespace model
} // namespace firestore
} // namespace firebase
namespace api = firebase::firestore::api;
namespace model = firebase::firestore::model;
NS_ASSUME_NONNULL_BEGIN
@interface FIRDocumentReference (/* Init */)
- (instancetype)initWithReference:(api::DocumentReference &&)reference NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithPath:(model::ResourcePath)path
firestore:(std::shared_ptr<api::Firestore>)firestore;
- (instancetype)initWithKey:(model::DocumentKey)key
firestore:(std::shared_ptr<api::Firestore>)firestore;
@end
/** Internal FIRDocumentReference API we don't want exposed in our public header files. */
@interface FIRDocumentReference (Internal)
- (const api::DocumentReference &)internalReference;
- (const model::DocumentKey &)key;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,270 @@
/*
* Copyright 2017 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 "FIRDocumentReference+Internal.h"
#include <memory>
#include <utility>
#import "FIRFirestoreErrors.h"
#import "Firestore/Source/API/FIRCollectionReference+Internal.h"
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
#import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h"
#import "Firestore/Source/API/FIRFirestore+Internal.h"
#import "Firestore/Source/API/FIRFirestoreSource+Internal.h"
#import "Firestore/Source/API/FIRListenerRegistration+Internal.h"
#import "Firestore/Source/API/FSTUserDataReader.h"
#import "Firestore/Source/API/converters.h"
#include "Firestore/core/src/api/collection_reference.h"
#include "Firestore/core/src/api/document_reference.h"
#include "Firestore/core/src/api/document_snapshot.h"
#include "Firestore/core/src/api/source.h"
#include "Firestore/core/src/core/event_listener.h"
#include "Firestore/core/src/core/listen_options.h"
#include "Firestore/core/src/core/user_data.h"
#include "Firestore/core/src/model/document_key.h"
#include "Firestore/core/src/model/document_set.h"
#include "Firestore/core/src/model/resource_path.h"
#include "Firestore/core/src/util/error_apple.h"
#include "Firestore/core/src/util/exception.h"
#include "Firestore/core/src/util/status.h"
#include "Firestore/core/src/util/statusor.h"
#include "Firestore/core/src/util/string_apple.h"
using firebase::firestore::api::CollectionReference;
using firebase::firestore::api::DocumentReference;
using firebase::firestore::api::DocumentSnapshot;
using firebase::firestore::api::DocumentSnapshotListener;
using firebase::firestore::api::Firestore;
using firebase::firestore::api::ListenerRegistration;
using firebase::firestore::api::MakeListenSource;
using firebase::firestore::api::MakeSource;
using firebase::firestore::api::Source;
using firebase::firestore::core::EventListener;
using firebase::firestore::core::ListenOptions;
using firebase::firestore::core::ParsedSetData;
using firebase::firestore::core::ParsedUpdateData;
using firebase::firestore::model::DocumentKey;
using firebase::firestore::model::ResourcePath;
using firebase::firestore::util::MakeCallback;
using firebase::firestore::util::MakeNSString;
using firebase::firestore::util::MakeString;
using firebase::firestore::util::StatusOr;
using firebase::firestore::util::StatusOrCallback;
using firebase::firestore::util::ThrowInvalidArgument;
NS_ASSUME_NONNULL_BEGIN
#pragma mark - FIRDocumentReference
@implementation FIRDocumentReference {
DocumentReference _documentReference;
}
- (instancetype)initWithReference:(DocumentReference &&)reference {
if (self = [super init]) {
_documentReference = std::move(reference);
}
return self;
}
- (instancetype)initWithPath:(ResourcePath)path firestore:(std::shared_ptr<Firestore>)firestore {
if (path.size() % 2 != 0) {
ThrowInvalidArgument("Invalid document reference. Document references must have an even "
"number of segments, but %s has %s",
path.CanonicalString(), path.size());
}
return [self initWithKey:DocumentKey{std::move(path)} firestore:firestore];
}
- (instancetype)initWithKey:(DocumentKey)key firestore:(std::shared_ptr<Firestore>)firestore {
DocumentReference delegate{std::move(key), firestore};
return [self initWithReference:std::move(delegate)];
}
#pragma mark - NSObject Methods
- (BOOL)isEqual:(nullable id)other {
if (other == self) return YES;
if (![[other class] isEqual:[self class]]) return NO;
return _documentReference == static_cast<FIRDocumentReference *>(other)->_documentReference;
}
- (NSUInteger)hash {
return _documentReference.Hash();
}
#pragma mark - Public Methods
@dynamic firestore;
- (FIRFirestore *)firestore {
return [FIRFirestore recoverFromFirestore:_documentReference.firestore()];
}
- (NSString *)documentID {
return MakeNSString(_documentReference.document_id());
}
- (FIRCollectionReference *)parent {
return [[FIRCollectionReference alloc] initWithReference:_documentReference.Parent()];
}
- (NSString *)path {
return MakeNSString(_documentReference.Path());
}
- (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath {
if (!collectionPath) {
ThrowInvalidArgument("Collection path cannot be nil.");
}
if (!collectionPath.length) {
ThrowInvalidArgument("Collection path cannot be empty.");
}
CollectionReference child = _documentReference.GetCollectionReference(MakeString(collectionPath));
return [[FIRCollectionReference alloc] initWithReference:std::move(child)];
}
- (void)setData:(NSDictionary<NSString *, id> *)documentData {
[self setData:documentData merge:NO completion:nil];
}
- (void)setData:(NSDictionary<NSString *, id> *)documentData merge:(BOOL)merge {
[self setData:documentData merge:merge completion:nil];
}
- (void)setData:(NSDictionary<NSString *, id> *)documentData
mergeFields:(NSArray<id> *)mergeFields {
[self setData:documentData mergeFields:mergeFields completion:nil];
}
- (void)setData:(NSDictionary<NSString *, id> *)documentData
completion:(nullable void (^)(NSError *_Nullable error))completion {
[self setData:documentData merge:NO completion:completion];
}
- (void)setData:(NSDictionary<NSString *, id> *)documentData
merge:(BOOL)merge
completion:(nullable void (^)(NSError *_Nullable error))completion {
auto dataReader = self.firestore.dataReader;
ParsedSetData parsed = merge ? [dataReader parsedMergeData:documentData fieldMask:nil]
: [dataReader parsedSetData:documentData];
_documentReference.SetData(std::move(parsed), MakeCallback(completion));
}
- (void)setData:(NSDictionary<NSString *, id> *)documentData
mergeFields:(NSArray<id> *)mergeFields
completion:(nullable void (^)(NSError *_Nullable error))completion {
ParsedSetData parsed = [self.firestore.dataReader parsedMergeData:documentData
fieldMask:mergeFields];
_documentReference.SetData(std::move(parsed), MakeCallback(completion));
}
- (void)updateData:(NSDictionary<id, id> *)fields {
[self updateData:fields completion:nil];
}
- (void)updateData:(NSDictionary<id, id> *)fields
completion:(nullable void (^)(NSError *_Nullable error))completion {
ParsedUpdateData parsed = [self.firestore.dataReader parsedUpdateData:fields];
_documentReference.UpdateData(std::move(parsed), MakeCallback(completion));
}
- (void)deleteDocument {
[self deleteDocumentWithCompletion:nil];
}
- (void)deleteDocumentWithCompletion:(nullable void (^)(NSError *_Nullable error))completion {
_documentReference.DeleteDocument(MakeCallback(completion));
}
- (void)getDocumentWithCompletion:(FIRDocumentSnapshotBlock)completion {
_documentReference.GetDocument(Source::Default, [self wrapDocumentSnapshotBlock:completion]);
}
- (void)getDocumentWithSource:(FIRFirestoreSource)source
completion:(FIRDocumentSnapshotBlock)completion {
_documentReference.GetDocument(MakeSource(source), [self wrapDocumentSnapshotBlock:completion]);
}
- (id<FIRListenerRegistration>)addSnapshotListener:(FIRDocumentSnapshotBlock)listener {
return [self addSnapshotListenerWithIncludeMetadataChanges:NO listener:listener];
}
- (id<FIRListenerRegistration>)
addSnapshotListenerWithIncludeMetadataChanges:(BOOL)includeMetadataChanges
listener:(FIRDocumentSnapshotBlock)listener {
ListenOptions options = ListenOptions::FromIncludeMetadataChanges(includeMetadataChanges);
return [self addSnapshotListenerInternalWithOptions:options listener:listener];
}
- (id<FIRListenerRegistration>)addSnapshotListenerWithOptions:(FIRSnapshotListenOptions *)options
listener:(FIRDocumentSnapshotBlock)listener {
ListenOptions listenOptions =
ListenOptions::FromOptions(options.includeMetadataChanges, MakeListenSource(options.source));
return [self addSnapshotListenerInternalWithOptions:listenOptions listener:listener];
}
- (id<FIRListenerRegistration>)addSnapshotListenerInternalWithOptions:(ListenOptions)internalOptions
listener:(FIRDocumentSnapshotBlock)
listener {
std::unique_ptr<ListenerRegistration> result = _documentReference.AddSnapshotListener(
std::move(internalOptions), [self wrapDocumentSnapshotBlock:listener]);
return [[FSTListenerRegistration alloc] initWithRegistration:std::move(result)];
}
- (DocumentSnapshotListener)wrapDocumentSnapshotBlock:(FIRDocumentSnapshotBlock)block {
class Converter : public EventListener<DocumentSnapshot> {
public:
explicit Converter(FIRDocumentSnapshotBlock block) : block_(block) {
}
void OnEvent(StatusOr<DocumentSnapshot> maybe_snapshot) override {
if (maybe_snapshot.ok()) {
FIRDocumentSnapshot *result =
[[FIRDocumentSnapshot alloc] initWithSnapshot:std::move(maybe_snapshot).ValueOrDie()];
block_(result, nil);
} else {
block_(nil, MakeNSError(maybe_snapshot.status()));
}
}
private:
FIRDocumentSnapshotBlock block_;
};
return absl::make_unique<Converter>(block);
}
@end
#pragma mark - FIRDocumentReference (Internal)
@implementation FIRDocumentReference (Internal)
- (const api::DocumentReference &)internalReference {
return _documentReference;
}
- (const DocumentKey &)key {
return _documentReference.key();
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,55 @@
/*
* 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 "FIRDocumentSnapshot.h"
#include <memory>
#include "Firestore/core/src/api/api_fwd.h"
#include "Firestore/core/src/model/model_fwd.h"
@class FIRFirestore;
namespace api = firebase::firestore::api;
namespace model = firebase::firestore::model;
NS_ASSUME_NONNULL_BEGIN
@interface FIRDocumentSnapshot (/* Init */)
- (instancetype)initWithSnapshot:(api::DocumentSnapshot &&)snapshot NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithFirestore:(FIRFirestore *)firestore
documentKey:(model::DocumentKey)documentKey
document:(const absl::optional<model::Document> &)document
metadata:(api::SnapshotMetadata)metadata;
- (instancetype)initWithFirestore:(FIRFirestore *)firestore
documentKey:(model::DocumentKey)documentKey
document:(const absl::optional<model::Document> &)document
fromCache:(bool)fromCache
hasPendingWrites:(bool)hasPendingWrites;
@end
/** Internal FIRDocumentSnapshot API we don't want exposed in our public header files. */
@interface FIRDocumentSnapshot (Internal)
- (const absl::optional<model::Document> &)internalDocument;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,210 @@
/*
* Copyright 2017 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 "FIRDocumentSnapshot+Internal.h"
#include <utility>
#include <vector>
#include "Firestore/core/src/util/warnings.h"
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
#import "Firestore/Source/API/FIRFieldPath+Internal.h"
#import "Firestore/Source/API/FIRFirestore+Internal.h"
#import "Firestore/Source/API/FIRGeoPoint+Internal.h"
#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h"
#import "Firestore/Source/API/FIRTimestamp+Internal.h"
#import "Firestore/Source/API/FSTUserDataWriter.h"
#import "Firestore/Source/API/converters.h"
#include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
#include "Firestore/core/src/api/document_reference.h"
#include "Firestore/core/src/api/document_snapshot.h"
#include "Firestore/core/src/api/firestore.h"
#include "Firestore/core/src/api/settings.h"
#include "Firestore/core/src/model/database_id.h"
#include "Firestore/core/src/model/document_key.h"
#include "Firestore/core/src/model/field_path.h"
#include "Firestore/core/src/nanopb/nanopb_util.h"
#include "Firestore/core/src/remote/serializer.h"
#include "Firestore/core/src/util/exception.h"
#include "Firestore/core/src/util/hard_assert.h"
#include "Firestore/core/src/util/log.h"
#include "Firestore/core/src/util/string_apple.h"
using firebase::firestore::google_firestore_v1_Value;
using firebase::firestore::api::DocumentSnapshot;
using firebase::firestore::api::Firestore;
using firebase::firestore::api::MakeFIRGeoPoint;
using firebase::firestore::api::MakeFIRTimestamp;
using firebase::firestore::api::SnapshotMetadata;
using firebase::firestore::model::DatabaseId;
using firebase::firestore::model::Document;
using firebase::firestore::model::DocumentKey;
using firebase::firestore::model::FieldPath;
using firebase::firestore::model::ObjectValue;
using firebase::firestore::nanopb::MakeNSData;
using firebase::firestore::remote::Serializer;
using firebase::firestore::util::MakeNSString;
using firebase::firestore::util::MakeString;
using firebase::firestore::util::ThrowInvalidArgument;
NS_ASSUME_NONNULL_BEGIN
@implementation FIRDocumentSnapshot {
DocumentSnapshot _snapshot;
std::unique_ptr<Serializer> _serializer;
FIRSnapshotMetadata *_cachedMetadata;
}
- (instancetype)initWithSnapshot:(DocumentSnapshot &&)snapshot {
if (self = [super init]) {
_snapshot = std::move(snapshot);
_serializer.reset(new Serializer(_snapshot.firestore()->database_id()));
}
return self;
}
- (instancetype)initWithFirestore:(FIRFirestore *)firestore
documentKey:(DocumentKey)documentKey
document:(const absl::optional<Document> &)document
metadata:(SnapshotMetadata)metadata {
DocumentSnapshot wrapped;
if (document.has_value()) {
wrapped =
DocumentSnapshot::FromDocument(firestore.wrapped, document.value(), std::move(metadata));
} else {
wrapped = DocumentSnapshot::FromNoDocument(firestore.wrapped, std::move(documentKey),
std::move(metadata));
}
_serializer.reset(new Serializer(firestore.databaseID));
return [self initWithSnapshot:std::move(wrapped)];
}
- (instancetype)initWithFirestore:(FIRFirestore *)firestore
documentKey:(DocumentKey)documentKey
document:(const absl::optional<Document> &)document
fromCache:(bool)fromCache
hasPendingWrites:(bool)hasPendingWrites {
return [self initWithFirestore:firestore
documentKey:std::move(documentKey)
document:document
metadata:SnapshotMetadata(hasPendingWrites, fromCache)];
}
// NSObject Methods
- (BOOL)isEqual:(nullable id)other {
if (other == self) return YES;
// self class could be FIRDocumentSnapshot or subtype. So we compare with base type explicitly.
if (![other isKindOfClass:[FIRDocumentSnapshot class]]) return NO;
return _snapshot == static_cast<FIRDocumentSnapshot *>(other)->_snapshot;
}
- (NSUInteger)hash {
return _snapshot.Hash();
}
@dynamic exists;
- (BOOL)exists {
return _snapshot.exists();
}
- (const absl::optional<Document> &)internalDocument {
return _snapshot.internal_document();
}
- (FIRDocumentReference *)reference {
return [[FIRDocumentReference alloc] initWithReference:_snapshot.CreateReference()];
}
- (NSString *)documentID {
return MakeNSString(_snapshot.document_id());
}
@dynamic metadata;
- (FIRSnapshotMetadata *)metadata {
if (!_cachedMetadata) {
_cachedMetadata = [[FIRSnapshotMetadata alloc] initWithMetadata:_snapshot.metadata()];
}
return _cachedMetadata;
}
- (nullable NSDictionary<NSString *, id> *)data {
return [self dataWithServerTimestampBehavior:FIRServerTimestampBehaviorNone];
}
- (nullable NSDictionary<NSString *, id> *)dataWithServerTimestampBehavior:
(FIRServerTimestampBehavior)serverTimestampBehavior {
absl::optional<google_firestore_v1_Value> data = _snapshot.GetValue(FieldPath::EmptyPath());
if (!data) return nil;
FSTUserDataWriter *dataWriter =
[[FSTUserDataWriter alloc] initWithFirestore:_snapshot.firestore()
serverTimestampBehavior:serverTimestampBehavior];
return [dataWriter convertedValue:*data];
}
- (nullable id)valueForField:(id)field {
return [self valueForField:field serverTimestampBehavior:FIRServerTimestampBehaviorNone];
}
- (nullable id)valueForField:(id)field
serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior {
FieldPath fieldPath;
if ([field isKindOfClass:[NSString class]]) {
fieldPath = FieldPath::FromDotSeparatedString(MakeString(field));
} else if ([field isKindOfClass:[FIRFieldPath class]]) {
fieldPath = ((FIRFieldPath *)field).internalValue;
} else {
ThrowInvalidArgument("Subscript key must be an NSString or FIRFieldPath.");
}
absl::optional<google_firestore_v1_Value> fieldValue = _snapshot.GetValue(fieldPath);
if (!fieldValue) return nil;
FSTUserDataWriter *dataWriter =
[[FSTUserDataWriter alloc] initWithFirestore:_snapshot.firestore()
serverTimestampBehavior:serverTimestampBehavior];
return [dataWriter convertedValue:*fieldValue];
}
- (nullable id)objectForKeyedSubscript:(id)key {
return [self valueForField:key];
}
@end
@implementation FIRQueryDocumentSnapshot
- (NSDictionary<NSString *, id> *)data {
NSDictionary<NSString *, id> *data = [super data];
HARD_ASSERT(data, "Document in a QueryDocumentSnapshot should exist");
return data;
}
- (NSDictionary<NSString *, id> *)dataWithServerTimestampBehavior:
(FIRServerTimestampBehavior)serverTimestampBehavior {
NSDictionary<NSString *, id> *data =
[super dataWithServerTimestampBehavior:serverTimestampBehavior];
HARD_ASSERT(data, "Document in a QueryDocumentSnapshot should exist");
return data;
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -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 "FIRFieldPath.h"
#include "Firestore/core/src/model/model_fwd.h"
namespace model = firebase::firestore::model;
NS_ASSUME_NONNULL_BEGIN
@interface FIRFieldPath ()
/** Internal field path representation */
- (const model::FieldPath &)internalValue;
- (instancetype)initPrivate:(model::FieldPath)path NS_DESIGNATED_INITIALIZER;
@end
/** Internal FIRFieldPath API we don't want exposed in our public header files. */
@interface FIRFieldPath (Internal)
+ (instancetype)pathWithDotSeparatedString:(NSString *)path;
@end
NS_ASSUME_NONNULL_END

View File

@@ -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 "FIRFieldPath.h"
#include <functional>
#include <string>
#include <utility>
#include <vector>
#import "Firestore/Source/API/FIRFieldPath+Internal.h"
#include "Firestore/core/src/model/field_path.h"
#include "Firestore/core/src/util/exception.h"
#include "Firestore/core/src/util/hashing.h"
#include "Firestore/core/src/util/string_apple.h"
using firebase::firestore::model::FieldPath;
using firebase::firestore::util::Hash;
using firebase::firestore::util::MakeString;
using firebase::firestore::util::ThrowInvalidArgument;
NS_ASSUME_NONNULL_BEGIN
@interface FIRFieldPath () {
/** Internal field path representation */
firebase::firestore::model::FieldPath _internalValue;
}
@end
@implementation FIRFieldPath
- (instancetype)initWithFields:(NSArray<NSString *> *)fieldNames {
if (fieldNames.count == 0) {
ThrowInvalidArgument("Invalid field path. Provided names must not be empty.");
}
std::vector<std::string> converted;
converted.reserve(fieldNames.count);
for (NSString *fieldName in fieldNames) {
converted.emplace_back(MakeString(fieldName));
}
return [self initPrivate:FieldPath::FromSegments(std::move(converted))];
}
+ (instancetype)documentID {
return [[FIRFieldPath alloc] initPrivate:FieldPath::KeyFieldPath()];
}
- (instancetype)initPrivate:(FieldPath)fieldPath {
if (self = [super init]) {
_internalValue = std::move(fieldPath);
}
return self;
}
+ (instancetype)pathWithDotSeparatedString:(NSString *)path {
return [[FIRFieldPath alloc] initPrivate:FieldPath::FromDotSeparatedString(MakeString(path))];
}
- (id)copyWithZone:(__unused NSZone *_Nullable)zone {
return [[[self class] alloc] initPrivate:_internalValue];
}
- (BOOL)isEqual:(nullable id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[FIRFieldPath class]]) {
return NO;
}
return _internalValue == ((FIRFieldPath *)object)->_internalValue;
}
- (NSUInteger)hash {
return Hash(_internalValue);
}
- (const firebase::firestore::model::FieldPath &)internalValue {
return _internalValue;
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRFieldValue.h"
NS_ASSUME_NONNULL_BEGIN
@interface FIRFieldValue (Internal)
/**
* The method name (e.g. "FieldValue.delete()") that was used to create this FIRFieldValue
* instance, for use in error messages, etc.
*/
@property(nonatomic, strong, readonly) NSString *methodName;
@end
/**
* FIRFieldValue class for field deletes. Exposed internally so code can do isKindOfClass checks on
* it.
*/
@interface FSTDeleteFieldValue : FIRFieldValue
- (instancetype)init NS_UNAVAILABLE;
@end
/**
* FIRFieldValue class for server timestamps. Exposed internally so code can do isKindOfClass checks
* on it.
*/
@interface FSTServerTimestampFieldValue : FIRFieldValue
- (instancetype)init NS_UNAVAILABLE;
@end
/** FIRFieldValue class for array unions. */
@interface FSTArrayUnionFieldValue : FIRFieldValue
- (instancetype)init NS_UNAVAILABLE;
@property(strong, nonatomic, readonly) NSArray<id> *elements;
@end
/** FIRFieldValue class for array removes. */
@interface FSTArrayRemoveFieldValue : FIRFieldValue
- (instancetype)init NS_UNAVAILABLE;
@property(strong, nonatomic, readonly) NSArray<id> *elements;
@end
/** FIRFieldValue class for number increments. */
@interface FSTNumericIncrementFieldValue : FIRFieldValue
- (instancetype)init NS_UNAVAILABLE;
@property(strong, nonatomic, readonly) NSNumber *operand;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,181 @@
/*
* 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 "Firestore/Source/API/FIRFieldValue+Internal.h"
NS_ASSUME_NONNULL_BEGIN
@interface FIRFieldValue ()
- (instancetype)initPrivate NS_DESIGNATED_INITIALIZER;
@end
#pragma mark - FSTDeleteFieldValue
@interface FSTDeleteFieldValue ()
/** Returns a single shared instance of the class. */
+ (instancetype)deleteFieldValue;
@end
@implementation FSTDeleteFieldValue
- (instancetype)initPrivate {
self = [super initPrivate];
return self;
}
+ (instancetype)deleteFieldValue {
static FSTDeleteFieldValue *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[FSTDeleteFieldValue alloc] initPrivate];
});
return sharedInstance;
}
- (NSString *)methodName {
return @"FieldValue.delete()";
}
@end
#pragma mark - FSTServerTimestampFieldValue
@interface FSTServerTimestampFieldValue ()
/** Returns a single shared instance of the class. */
+ (instancetype)serverTimestampFieldValue;
@end
@implementation FSTServerTimestampFieldValue
- (instancetype)initPrivate {
self = [super initPrivate];
return self;
}
+ (instancetype)serverTimestampFieldValue {
static FSTServerTimestampFieldValue *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[FSTServerTimestampFieldValue alloc] initPrivate];
});
return sharedInstance;
}
- (NSString *)methodName {
return @"FieldValue.serverTimestamp()";
}
@end
#pragma mark - FSTArrayUnionFieldValue
@interface FSTArrayUnionFieldValue ()
- (instancetype)initWithElements:(NSArray<id> *)elements;
@end
@implementation FSTArrayUnionFieldValue
- (instancetype)initWithElements:(NSArray<id> *)elements {
if (self = [super initPrivate]) {
_elements = elements;
}
return self;
}
- (NSString *)methodName {
return @"FieldValue.arrayUnion()";
}
@end
#pragma mark - FSTArrayRemoveFieldValue
@interface FSTArrayRemoveFieldValue ()
- (instancetype)initWithElements:(NSArray<id> *)elements;
@end
@implementation FSTArrayRemoveFieldValue
- (instancetype)initWithElements:(NSArray<id> *)elements {
if (self = [super initPrivate]) {
_elements = elements;
}
return self;
}
- (NSString *)methodName {
return @"FieldValue.arrayRemove()";
}
@end
#pragma mark - FSTNumericIncrementFieldValue
/* FieldValue class for increment() transforms. */
@interface FSTNumericIncrementFieldValue ()
- (instancetype)initWithOperand:(NSNumber *)operand;
@end
@implementation FSTNumericIncrementFieldValue
- (instancetype)initWithOperand:(NSNumber *)operand {
if (self = [super initPrivate]) {
_operand = operand;
}
return self;
}
- (NSString *)methodName {
return @"FieldValue.increment()";
}
@end
#pragma mark - FIRFieldValue
@implementation FIRFieldValue
- (instancetype)initPrivate {
self = [super init];
return self;
}
+ (instancetype)fieldValueForDelete {
return [FSTDeleteFieldValue deleteFieldValue];
}
+ (instancetype)fieldValueForServerTimestamp {
return [FSTServerTimestampFieldValue serverTimestampFieldValue];
}
+ (instancetype)fieldValueForArrayUnion:(NSArray<id> *)elements {
return [[FSTArrayUnionFieldValue alloc] initWithElements:elements];
}
+ (instancetype)fieldValueForArrayRemove:(NSArray<id> *)elements {
return [[FSTArrayRemoveFieldValue alloc] initWithElements:elements];
}
+ (instancetype)fieldValueForDoubleIncrement:(double)d {
return [[FSTNumericIncrementFieldValue alloc] initWithOperand:@(d)];
}
+ (instancetype)fieldValueForIntegerIncrement:(int64_t)l {
return [[FSTNumericIncrementFieldValue alloc] initWithOperand:@(l)];
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2022 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>
#import "FIRFilter.h"
#include "Firestore/core/src/core/composite_filter.h"
#include "Firestore/core/src/core/field_filter.h"
@class FIRFieldPath;
NS_ASSUME_NONNULL_BEGIN
/** Exposed internally */
@interface FSTUnaryFilter : FIRFilter
@property(nonatomic, strong, readonly) FIRFieldPath *fieldPath;
@property(nonatomic, readonly) firebase::firestore::core::FieldFilter::Operator unaryOp;
@property(nonatomic, strong, readonly) id value;
@end
@interface FSTCompositeFilter : FIRFilter
@property(nonatomic, strong, readonly) NSArray<FIRFilter *> *filters;
@property(nonatomic, readonly) firebase::firestore::core::CompositeFilter::Operator compOp;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,196 @@
/*
* Copyright 2022 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 "FIRFilter+Internal.h"
#import "Firestore/Source/API/FIRFieldPath+Internal.h"
using firebase::firestore::core::CompositeFilter;
using firebase::firestore::core::FieldFilter;
using firebase::firestore::model::FieldPath;
using firebase::firestore::util::MakeString;
NS_ASSUME_NONNULL_BEGIN
namespace {
FIRFieldPath *MakeFIRFieldPath(NSString *field) {
return [FIRFieldPath pathWithDotSeparatedString:field];
}
} // namespace
@interface FSTUnaryFilter ()
@property(nonatomic, strong, readwrite) FIRFieldPath *fieldPath;
@property(nonatomic, readwrite) FieldFilter::Operator unaryOp;
@property(nonatomic, strong, readwrite) id value;
@end
@implementation FSTUnaryFilter
- (instancetype)initWithFIRFieldPath:(nonnull FIRFieldPath *)path
op:(FieldFilter::Operator)op
value:(nonnull id)value {
if (self = [super init]) {
self.fieldPath = path;
self.unaryOp = op;
self.value = value;
}
return self;
}
@end
@interface FSTCompositeFilter ()
@property(nonatomic, strong, readwrite) NSArray<FIRFilter *> *filters;
@property(nonatomic, readwrite) CompositeFilter::Operator compOp;
@end
@implementation FSTCompositeFilter
- (instancetype)initWithFilters:(nonnull NSArray<FIRFilter *> *)filters
op:(CompositeFilter::Operator)op {
if (self = [super init]) {
self.filters = filters;
self.compOp = op;
}
return self;
}
@end
@implementation FIRFilter
#pragma mark - Constructor Methods
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field isEqualTo:(nonnull id)value {
return [self filterWhereFieldPath:MakeFIRFieldPath(field) isEqualTo:value];
}
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path isEqualTo:(nonnull id)value {
return [[FSTUnaryFilter alloc] initWithFIRFieldPath:path
op:FieldFilter::Operator::Equal
value:value];
}
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field isNotEqualTo:(nonnull id)value {
return [self filterWhereFieldPath:MakeFIRFieldPath(field) isNotEqualTo:value];
}
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path isNotEqualTo:(nonnull id)value {
return [[FSTUnaryFilter alloc] initWithFIRFieldPath:path
op:FieldFilter::Operator::NotEqual
value:value];
}
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field isGreaterThan:(nonnull id)value {
return [self filterWhereFieldPath:MakeFIRFieldPath(field) isGreaterThan:value];
}
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path isGreaterThan:(nonnull id)value {
return [[FSTUnaryFilter alloc] initWithFIRFieldPath:path
op:FieldFilter::Operator::GreaterThan
value:value];
}
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field isGreaterThanOrEqualTo:(nonnull id)value {
return [self filterWhereFieldPath:MakeFIRFieldPath(field) isGreaterThanOrEqualTo:value];
}
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
isGreaterThanOrEqualTo:(nonnull id)value {
return [[FSTUnaryFilter alloc] initWithFIRFieldPath:path
op:FieldFilter::Operator::GreaterThanOrEqual
value:value];
}
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field isLessThan:(nonnull id)value {
return [self filterWhereFieldPath:MakeFIRFieldPath(field) isLessThan:value];
}
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path isLessThan:(nonnull id)value {
return [[FSTUnaryFilter alloc] initWithFIRFieldPath:path
op:FieldFilter::Operator::LessThan
value:value];
}
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field isLessThanOrEqualTo:(nonnull id)value {
return [self filterWhereFieldPath:MakeFIRFieldPath(field) isLessThanOrEqualTo:value];
}
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
isLessThanOrEqualTo:(nonnull id)value {
return [[FSTUnaryFilter alloc] initWithFIRFieldPath:path
op:FieldFilter::Operator::LessThanOrEqual
value:value];
}
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field arrayContains:(nonnull id)value {
return [self filterWhereFieldPath:MakeFIRFieldPath(field) arrayContains:value];
}
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path arrayContains:(nonnull id)value {
return [[FSTUnaryFilter alloc] initWithFIRFieldPath:path
op:FieldFilter::Operator::ArrayContains
value:value];
}
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field
arrayContainsAny:(nonnull NSArray<id> *)values {
return [self filterWhereFieldPath:MakeFIRFieldPath(field) arrayContainsAny:values];
}
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
arrayContainsAny:(nonnull NSArray<id> *)values {
return [[FSTUnaryFilter alloc] initWithFIRFieldPath:path
op:FieldFilter::Operator::ArrayContainsAny
value:values];
}
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field in:(nonnull NSArray<id> *)values {
return [self filterWhereFieldPath:MakeFIRFieldPath(field) in:values];
}
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path in:(nonnull NSArray<id> *)values {
return [[FSTUnaryFilter alloc] initWithFIRFieldPath:path
op:FieldFilter::Operator::In
value:values];
}
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field notIn:(nonnull NSArray<id> *)values {
return [self filterWhereFieldPath:MakeFIRFieldPath(field) notIn:values];
}
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
notIn:(nonnull NSArray<id> *)values {
return [[FSTUnaryFilter alloc] initWithFIRFieldPath:path
op:FieldFilter::Operator::NotIn
value:values];
}
+ (FIRFilter *)orFilterWithFilters:(NSArray<FIRFilter *> *)filters {
return [[FSTCompositeFilter alloc] initWithFilters:filters op:CompositeFilter::Operator::Or];
}
+ (FIRFilter *)andFilterWithFilters:(NSArray<FIRFilter *> *)filters {
return [[FSTCompositeFilter alloc] initWithFilters:filters op:CompositeFilter::Operator::And];
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,89 @@
/*
* 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 "FIRFirestore.h"
#include <memory>
#include <string>
#include "Firestore/core/src/api/firestore.h"
#include "Firestore/core/src/credentials/credentials_provider.h"
#include "Firestore/core/src/util/async_queue.h"
@class FIRApp;
@class FSTUserDataReader;
@class FIRPersistentCacheIndexManager;
namespace firebase {
namespace firestore {
namespace remote {
class FirebaseMetadataProvider;
} // namespace remote
} // namespace firestore
} // namespace firebase
namespace api = firebase::firestore::api;
namespace credentials = firebase::firestore::credentials;
namespace model = firebase::firestore::model;
namespace remote = firebase::firestore::remote;
NS_ASSUME_NONNULL_BEGIN
/** Provides a registry management interface for FIRFirestore instances. */
@protocol FSTFirestoreInstanceRegistry
/** Removes the FIRFirestore instance with given database name from registry. */
- (void)removeInstanceWithDatabase:(NSString *)database;
@end
@interface FIRFirestore (/* Init */)
/**
* Initializes a Firestore object with all the required parameters directly. This exists so that
* tests can create FIRFirestore objects without needing FIRApp.
*/
- (instancetype)initWithDatabaseID:(model::DatabaseId)databaseID
persistenceKey:(std::string)persistenceKey
authCredentialsProvider:
(std::shared_ptr<credentials::AuthCredentialsProvider>)authCredentialsProvider
appCheckCredentialsProvider:
(std::shared_ptr<credentials::AppCheckCredentialsProvider>)appCheckCredentialsProvider
workerQueue:
(std::shared_ptr<firebase::firestore::util::AsyncQueue>)workerQueue
firebaseMetadataProvider:
(std::unique_ptr<remote::FirebaseMetadataProvider>)firebaseMetadataProvider
firebaseApp:(FIRApp *)app
instanceRegistry:(nullable id<FSTFirestoreInstanceRegistry>)registry;
@end
/** Internal FIRFirestore API we don't want exposed in our public header files. */
@interface FIRFirestore (Internal)
+ (FIRFirestore *)recoverFromFirestore:(std::shared_ptr<api::Firestore>)firestore;
- (void)terminateInternalWithCompletion:(nullable void (^)(NSError *_Nullable error))completion;
- (const std::shared_ptr<firebase::firestore::util::AsyncQueue> &)workerQueue;
@property(nonatomic, assign, readonly) std::shared_ptr<api::Firestore> wrapped;
@property(nonatomic, assign, readonly) const model::DatabaseId &databaseID;
@property(nonatomic, strong, readonly) FSTUserDataReader *dataReader;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,582 @@
/*
* 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.
*/
// TODO(csi): Delete this once setIndexConfigurationFromJSON and setIndexConfigurationFromStream
// are removed.
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#import "FIRFirestore+Internal.h"
#include <memory>
#include <string>
#include <utility>
#import "FIRFirestoreSettings+Internal.h"
#import "FIRPersistentCacheIndexManager+Internal.h"
#import "FIRTransactionOptions+Internal.h"
#import "FIRTransactionOptions.h"
#import "FirebaseCore/Extension/FIRAppInternal.h"
#import "FirebaseCore/Extension/FIRComponentContainer.h"
#import "FirebaseCore/Extension/FIRComponentType.h"
#import "Firestore/Source/API/FIRCollectionReference+Internal.h"
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
#import "Firestore/Source/API/FIRListenerRegistration+Internal.h"
#import "Firestore/Source/API/FIRLoadBundleTask+Internal.h"
#import "Firestore/Source/API/FIRQuery+Internal.h"
#import "Firestore/Source/API/FIRTransaction+Internal.h"
#import "Firestore/Source/API/FIRWriteBatch+Internal.h"
#import "Firestore/Source/API/FSTFirestoreComponent.h"
#import "Firestore/Source/API/FSTUserDataReader.h"
#include "Firestore/core/src/api/collection_reference.h"
#include "Firestore/core/src/api/document_reference.h"
#include "Firestore/core/src/api/firestore.h"
#include "Firestore/core/src/api/write_batch.h"
#include "Firestore/core/src/core/database_info.h"
#include "Firestore/core/src/core/event_listener.h"
#include "Firestore/core/src/core/transaction.h"
#include "Firestore/core/src/credentials/credentials_provider.h"
#include "Firestore/core/src/model/database_id.h"
#include "Firestore/core/src/remote/firebase_metadata_provider.h"
#include "Firestore/core/src/util/async_queue.h"
#include "Firestore/core/src/util/byte_stream_apple.h"
#include "Firestore/core/src/util/config.h"
#include "Firestore/core/src/util/empty.h"
#include "Firestore/core/src/util/error_apple.h"
#include "Firestore/core/src/util/exception.h"
#include "Firestore/core/src/util/exception_apple.h"
#include "Firestore/core/src/util/executor_libdispatch.h"
#include "Firestore/core/src/util/hard_assert.h"
#include "Firestore/core/src/util/log.h"
#include "Firestore/core/src/util/status.h"
#include "Firestore/core/src/util/statusor.h"
#include "Firestore/core/src/util/string_apple.h"
#include "absl/memory/memory.h"
using firebase::firestore::api::DocumentReference;
using firebase::firestore::api::Firestore;
using firebase::firestore::api::ListenerRegistration;
using firebase::firestore::core::EventListener;
using firebase::firestore::credentials::AuthCredentialsProvider;
using firebase::firestore::model::DatabaseId;
using firebase::firestore::remote::FirebaseMetadataProvider;
using firebase::firestore::util::AsyncQueue;
using firebase::firestore::util::ByteStreamApple;
using firebase::firestore::util::Empty;
using firebase::firestore::util::Executor;
using firebase::firestore::util::ExecutorLibdispatch;
using firebase::firestore::util::kLogLevelDebug;
using firebase::firestore::util::kLogLevelNotice;
using firebase::firestore::util::LogSetLevel;
using firebase::firestore::util::MakeCallback;
using firebase::firestore::util::MakeNSError;
using firebase::firestore::util::MakeNSString;
using firebase::firestore::util::MakeString;
using firebase::firestore::util::ObjcThrowHandler;
using firebase::firestore::util::SetThrowHandler;
using firebase::firestore::util::Status;
using firebase::firestore::util::StatusOr;
using firebase::firestore::util::StreamReadResult;
using firebase::firestore::util::ThrowIllegalState;
using firebase::firestore::util::ThrowInvalidArgument;
using UserUpdateBlock = id _Nullable (^)(FIRTransaction *, NSError **);
using UserTransactionCompletion = void (^)(id _Nullable, NSError *_Nullable);
NS_ASSUME_NONNULL_BEGIN
#pragma mark - FIRFirestore
@interface FIRFirestore ()
@property(nonatomic, strong, readonly) FSTUserDataReader *dataReader;
@end
@implementation FIRFirestore {
std::shared_ptr<Firestore> _firestore;
FIRFirestoreSettings *_settings;
__weak id<FSTFirestoreInstanceRegistry> _registry;
FIRPersistentCacheIndexManager *_indexManager;
}
+ (void)initialize {
if (self == [FIRFirestore class]) {
SetThrowHandler(ObjcThrowHandler);
Firestore::SetClientLanguage("gl-objc/");
}
}
+ (instancetype)firestore {
FIRApp *app = [FIRApp defaultApp];
if (!app) {
ThrowIllegalState("Failed to get FirebaseApp instance. Please call FirebaseApp.configure() "
"before using Firestore");
}
return [self firestoreForApp:app database:MakeNSString(DatabaseId::kDefault)];
}
+ (instancetype)firestoreForApp:(FIRApp *)app {
return [self firestoreForApp:app database:MakeNSString(DatabaseId::kDefault)];
}
- (instancetype)initWithDatabaseID:(model::DatabaseId)databaseID
persistenceKey:(std::string)persistenceKey
authCredentialsProvider:
(std::shared_ptr<credentials::AuthCredentialsProvider>)authCredentialsProvider
appCheckCredentialsProvider:
(std::shared_ptr<credentials::AppCheckCredentialsProvider>)appCheckCredentialsProvider
workerQueue:(std::shared_ptr<AsyncQueue>)workerQueue
firebaseMetadataProvider:
(std::unique_ptr<FirebaseMetadataProvider>)firebaseMetadataProvider
firebaseApp:(FIRApp *)app
instanceRegistry:(nullable id<FSTFirestoreInstanceRegistry>)registry {
if (self = [super init]) {
_firestore = std::make_shared<Firestore>(
std::move(databaseID), std::move(persistenceKey), std::move(authCredentialsProvider),
std::move(appCheckCredentialsProvider), std::move(workerQueue),
std::move(firebaseMetadataProvider), (__bridge void *)self);
_app = app;
_registry = registry;
FSTPreConverterBlock block = ^id _Nullable(id _Nullable input) {
if ([input isKindOfClass:[FIRDocumentReference class]]) {
auto documentReference = (FIRDocumentReference *)input;
return [[FSTDocumentKeyReference alloc] initWithKey:documentReference.key
databaseID:documentReference.firestore.databaseID];
} else {
return input;
}
};
_dataReader = [[FSTUserDataReader alloc] initWithDatabaseID:_firestore->database_id()
preConverter:block];
// Use the property setter so the default settings get plumbed into _firestoreClient.
self.settings = [[FIRFirestoreSettings alloc] init];
}
return self;
}
+ (instancetype)firestoreForApp:(FIRApp *)app database:(NSString *)database {
if (!app) {
ThrowInvalidArgument("FirebaseApp instance may not be nil. Use FirebaseApp.app() if you'd like "
"to use the default FirebaseApp instance.");
}
if (!database) {
ThrowInvalidArgument("Database identifier may not be nil. Use '%s' if you want the default "
"database",
DatabaseId::kDefault);
}
id<FSTFirestoreMultiDBProvider> provider =
FIR_COMPONENT(FSTFirestoreMultiDBProvider, app.container);
return [provider firestoreForDatabase:database];
}
+ (instancetype)firestoreForDatabase:(NSString *)database {
FIRApp *app = [FIRApp defaultApp];
if (!app) {
ThrowIllegalState("Failed to get FirebaseApp instance. Please call FirebaseApp.configure() "
"before using Firestore");
}
return [self firestoreForApp:app database:database];
}
- (FIRFirestoreSettings *)settings {
// Disallow mutation of our internal settings
return [_settings copy];
}
- (void)setSettings:(FIRFirestoreSettings *)settings {
if (![settings isEqual:_settings]) {
_settings = settings;
_firestore->set_settings([settings internalSettings]);
#if HAVE_LIBDISPATCH
std::unique_ptr<Executor> user_executor =
absl::make_unique<ExecutorLibdispatch>(settings.dispatchQueue);
#else
// It's possible to build without libdispatch on macOS for testing purposes.
// In this case, avoid breaking the build.
std::unique_ptr<Executor> user_executor =
Executor::CreateSerial("com.google.firebase.firestore.user");
#endif // HAVE_LIBDISPATCH
_firestore->set_user_executor(std::move(user_executor));
}
}
- (void)setIndexConfigurationFromJSON:(NSString *)json
completion:(nullable void (^)(NSError *_Nullable error))completion {
_firestore->SetIndexConfiguration(MakeString(json), MakeCallback(completion));
}
- (void)setIndexConfigurationFromStream:(NSInputStream *)stream
completion:(nullable void (^)(NSError *_Nullable error))completion {
auto input = absl::make_unique<ByteStreamApple>(stream);
auto callback = MakeCallback(completion);
std::string json;
bool eof = false;
while (!eof) {
StreamReadResult result = input->Read(1024ul);
if (!result.ok()) {
callback(result.status());
return;
}
eof = result.eof();
json.append(std::move(result).ValueOrDie());
}
_firestore->SetIndexConfiguration(json, callback);
}
- (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath {
if (!collectionPath) {
ThrowInvalidArgument("Collection path cannot be nil.");
}
if (!collectionPath.length) {
ThrowInvalidArgument("Collection path cannot be empty.");
}
if ([collectionPath containsString:@"//"]) {
ThrowInvalidArgument("Invalid path (%s). Paths must not contain // in them.", collectionPath);
}
return [[FIRCollectionReference alloc]
initWithReference:_firestore->GetCollection(MakeString(collectionPath))];
}
- (FIRDocumentReference *)documentWithPath:(NSString *)documentPath {
if (!documentPath) {
ThrowInvalidArgument("Document path cannot be nil.");
}
if (!documentPath.length) {
ThrowInvalidArgument("Document path cannot be empty.");
}
if ([documentPath containsString:@"//"]) {
ThrowInvalidArgument("Invalid path (%s). Paths must not contain // in them.", documentPath);
}
DocumentReference documentReference = _firestore->GetDocument(MakeString(documentPath));
return [[FIRDocumentReference alloc] initWithReference:std::move(documentReference)];
}
- (FIRQuery *)collectionGroupWithID:(NSString *)collectionID {
if (!collectionID) {
ThrowInvalidArgument("Collection ID cannot be nil.");
}
if (!collectionID.length) {
ThrowInvalidArgument("Collection ID cannot be empty.");
}
if ([collectionID containsString:@"/"]) {
ThrowInvalidArgument("Invalid collection ID (%s). Collection IDs must not contain / in them.",
collectionID);
}
auto query = _firestore->GetCollectionGroup(MakeString(collectionID));
return [[FIRQuery alloc] initWithQuery:std::move(query) firestore:_firestore];
}
- (FIRWriteBatch *)batch {
return [FIRWriteBatch writeBatchWithDataReader:self.dataReader writeBatch:_firestore->GetBatch()];
}
- (void)runTransactionWithOptions:(FIRTransactionOptions *_Nullable)options
block:(UserUpdateBlock)updateBlock
dispatchQueue:(dispatch_queue_t)queue
completion:(UserTransactionCompletion)completion {
if (!updateBlock) {
ThrowInvalidArgument("Transaction block cannot be nil.");
}
if (!completion) {
ThrowInvalidArgument("Transaction completion block cannot be nil.");
}
class TransactionResult {
public:
TransactionResult(FIRFirestore *firestore,
UserUpdateBlock update_block,
dispatch_queue_t queue,
UserTransactionCompletion completion)
: firestore_(firestore),
user_update_block_(update_block),
queue_(queue),
user_completion_(completion) {
}
void RunUpdateBlock(std::shared_ptr<core::Transaction> internalTransaction,
core::TransactionResultCallback internalCallback) {
dispatch_async(queue_, ^{
auto transaction = [FIRTransaction transactionWithInternalTransaction:internalTransaction
firestore:firestore_];
NSError *_Nullable error = nil;
user_result_ = user_update_block_(transaction, &error);
// If the user set an error, disregard the result.
if (error) {
// If the error is a user error, set flag to not retry the transaction.
if (error.domain != FIRFirestoreErrorDomain) {
internalTransaction->MarkPermanentlyFailed();
}
internalCallback(Status::FromNSError(error));
} else {
internalCallback(Status::OK());
}
});
}
void HandleFinalStatus(const Status &status) {
if (!status.ok()) {
user_completion_(nil, MakeNSError(status));
return;
}
user_completion_(user_result_, nil);
}
private:
FIRFirestore *firestore_;
UserUpdateBlock user_update_block_;
dispatch_queue_t queue_;
UserTransactionCompletion user_completion_;
id _Nullable user_result_;
};
auto result_capture = std::make_shared<TransactionResult>(self, updateBlock, queue, completion);
// Wrap the user-supplied updateBlock in a core C++ compatible callback. Wrap the result of the
// updateBlock invocation up in a TransactionResult for tunneling through the internals of the
// system.
auto internalUpdateBlock = [result_capture](
std::shared_ptr<core::Transaction> internalTransaction,
core::TransactionResultCallback internalCallback) {
result_capture->RunUpdateBlock(internalTransaction, internalCallback);
};
// Unpacks the TransactionResult value and calls the user completion handler.
//
// PORTING NOTE: Other platforms where the user return value is internally representable don't
// need this wrapper.
auto objcTranslator = [result_capture](const Status &status) {
result_capture->HandleFinalStatus(status);
};
int max_attempts = [FIRTransactionOptions defaultMaxAttempts];
if (options) {
// Note: The cast of `maxAttempts` from `NSInteger` to `int` is safe (i.e. lossless) because
// `FIRTransactionOptions` does not allow values greater than `INT32_MAX` to be set.
max_attempts = static_cast<int>(options.maxAttempts);
}
_firestore->RunTransaction(std::move(internalUpdateBlock), std::move(objcTranslator),
max_attempts);
}
- (void)runTransactionWithBlock:(id _Nullable (^)(FIRTransaction *, NSError **error))updateBlock
completion:
(void (^)(id _Nullable result, NSError *_Nullable error))completion {
[self runTransactionWithOptions:nil block:updateBlock completion:completion];
}
- (void)runTransactionWithOptions:(FIRTransactionOptions *_Nullable)options
block:(id _Nullable (^)(FIRTransaction *, NSError **))updateBlock
completion:
(void (^)(id _Nullable result, NSError *_Nullable error))completion {
static dispatch_queue_t transactionDispatchQueue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
transactionDispatchQueue = dispatch_queue_create("com.google.firebase.firestore.transaction",
DISPATCH_QUEUE_CONCURRENT);
});
[self runTransactionWithOptions:options
block:updateBlock
dispatchQueue:transactionDispatchQueue
completion:completion];
}
+ (void)enableLogging:(BOOL)logging {
LogSetLevel(logging ? kLogLevelDebug : kLogLevelNotice);
}
- (void)useEmulatorWithHost:(NSString *)host port:(NSInteger)port {
if (!host.length) {
ThrowInvalidArgument("Host cannot be nil or empty.");
}
if (!_settings.isUsingDefaultHost) {
LOG_WARN("Overriding previously-set host value: %@", _settings.host);
}
// Use a new settings so the new settings are automatically plumbed
// to the underlying Firestore objects.
NSString *settingsHost = [NSString stringWithFormat:@"%@:%li", host, (long)port];
FIRFirestoreSettings *newSettings = [_settings copy];
newSettings.host = settingsHost;
self.settings = newSettings;
}
- (void)enableNetworkWithCompletion:(nullable void (^)(NSError *_Nullable error))completion {
_firestore->EnableNetwork(MakeCallback(completion));
}
- (void)disableNetworkWithCompletion:(nullable void (^)(NSError *_Nullable))completion {
_firestore->DisableNetwork(MakeCallback(completion));
}
- (void)clearPersistenceWithCompletion:(nullable void (^)(NSError *_Nullable error))completion {
_firestore->ClearPersistence(MakeCallback(completion));
}
- (void)waitForPendingWritesWithCompletion:(void (^)(NSError *_Nullable error))completion {
_firestore->WaitForPendingWrites(MakeCallback(completion));
}
- (void)terminateWithCompletion:(nullable void (^)(NSError *_Nullable error))completion {
id<FSTFirestoreInstanceRegistry> strongRegistry = _registry;
if (strongRegistry) {
[strongRegistry
removeInstanceWithDatabase:MakeNSString(_firestore->database_id().database_id())];
}
[self terminateInternalWithCompletion:completion];
}
- (id<FIRListenerRegistration>)addSnapshotsInSyncListener:(void (^)(void))listener {
std::unique_ptr<core::EventListener<Empty>> eventListener =
core::EventListener<Empty>::Create([listener](const StatusOr<Empty> &) { listener(); });
std::unique_ptr<ListenerRegistration> result =
_firestore->AddSnapshotsInSyncListener(std::move(eventListener));
return [[FSTListenerRegistration alloc] initWithRegistration:std::move(result)];
}
- (FIRLoadBundleTask *)loadBundle:(nonnull NSData *)bundleData {
auto stream = absl::make_unique<ByteStreamApple>([[NSInputStream alloc] initWithData:bundleData]);
return [self loadBundleStream:[[NSInputStream alloc] initWithData:bundleData] completion:nil];
}
- (FIRLoadBundleTask *)loadBundle:(NSData *)bundleData
completion:(nullable void (^)(FIRLoadBundleTaskProgress *_Nullable progress,
NSError *_Nullable error))completion {
return [self loadBundleStream:[[NSInputStream alloc] initWithData:bundleData]
completion:completion];
}
- (FIRLoadBundleTask *)loadBundleStream:(NSInputStream *)bundleStream {
return [self loadBundleStream:bundleStream completion:nil];
}
- (FIRLoadBundleTask *)loadBundleStream:(NSInputStream *)bundleStream
completion:
(nullable void (^)(FIRLoadBundleTaskProgress *_Nullable progress,
NSError *_Nullable error))completion {
auto stream = absl::make_unique<ByteStreamApple>(bundleStream);
std::shared_ptr<api::LoadBundleTask> task = _firestore->LoadBundle(std::move(stream));
auto callback = [completion](api::LoadBundleTaskProgress progress) {
if (!completion) {
return;
}
// Ignoring `kInProgress` because we are setting up for completion callback.
if (progress.state() == api::LoadBundleTaskState::kSuccess) {
completion([[FIRLoadBundleTaskProgress alloc] initWithInternal:progress], nil);
} else if (progress.state() == api::LoadBundleTaskState::kError) {
NSError *error = nil;
if (!progress.error_status().ok()) {
LOG_WARN("Progress set to Error, but error_status() is ok()");
error = MakeNSError(firebase::firestore::Error::kErrorUnknown,
"Loading bundle failed with unknown error");
} else {
error = MakeNSError(progress.error_status());
}
completion([[FIRLoadBundleTaskProgress alloc] initWithInternal:progress], error);
}
};
task->SetLastObserver(callback);
return [[FIRLoadBundleTask alloc] initWithTask:task];
}
- (void)getQueryNamed:(NSString *)name completion:(void (^)(FIRQuery *_Nullable query))completion {
auto firestore = _firestore;
auto callback = [completion, firestore](core::Query query, bool found) {
if (!completion) {
return;
}
if (found) {
FIRQuery *firQuery = [[FIRQuery alloc] initWithQuery:std::move(query) firestore:firestore];
completion(firQuery);
} else {
completion(nil);
}
};
_firestore->GetNamedQuery(MakeString(name), callback);
}
@end
@implementation FIRFirestore (Internal)
- (std::shared_ptr<Firestore>)wrapped {
return _firestore;
}
- (const std::shared_ptr<AsyncQueue> &)workerQueue {
return _firestore->worker_queue();
}
- (nullable FIRPersistentCacheIndexManager *)persistentCacheIndexManager {
if (!_indexManager) {
auto index_manager = _firestore->persistent_cache_index_manager();
if (index_manager) {
_indexManager = [[FIRPersistentCacheIndexManager alloc]
initWithPersistentCacheIndexManager:index_manager];
} else {
return nil;
}
}
return _indexManager;
}
- (const DatabaseId &)databaseID {
return _firestore->database_id();
}
+ (FIRFirestore *)recoverFromFirestore:(std::shared_ptr<Firestore>)firestore {
return (__bridge FIRFirestore *)firestore->extension();
}
- (void)terminateInternalWithCompletion:(nullable void (^)(NSError *_Nullable error))completion {
_firestore->Terminate(MakeCallback(completion));
}
#pragma mark - Force Link Unreferenced Symbols
extern void FSTIncludeFSTFirestoreComponent(void);
extern void FSTIncludeFIRSnapshotListenOptions(void);
/// This method forces the linker to include all Firestore symbols without requiring app
/// developers to include the '-ObjC' linker flag in their projects. DO NOT CALL THIS METHOD.
+ (void)notCalled {
NSAssert(NO, @"+notCalled should never be called");
FSTIncludeFSTFirestoreComponent();
FSTIncludeFIRSnapshotListenOptions();
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -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 "FIRFirestoreSettings.h"
#import <Foundation/Foundation.h>
#include "Firestore/core/src/api/settings.h"
NS_ASSUME_NONNULL_BEGIN
@interface FIRFirestoreSettings (Internal)
/** Returns whether or not the host has been set to a non-default value. */
@property(nonatomic, readonly) BOOL isUsingDefaultHost;
/** Converts this FIRFirestoreSettings instance into an api::Settings object. */
- (firebase::firestore::api::Settings)internalSettings;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,161 @@
/*
* Copyright 2017 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.
*/
// TODO(wuandy): Delete this once isPersistenceEnabled and cacheSizeBytes are removed.
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#import "FIRFirestoreSettings.h"
#import <Foundation/NSObject.h>
#import "FIRLocalCacheSettings+Internal.h"
#include "Firestore/Source/Public/FirebaseFirestore/FIRLocalCacheSettings.h"
#include "Firestore/core/src/api/settings.h"
#include "Firestore/core/src/util/exception.h"
#include "Firestore/core/src/util/string_apple.h"
NS_ASSUME_NONNULL_BEGIN
namespace api = firebase::firestore::api;
using api::Settings;
using firebase::firestore::util::MakeString;
using firebase::firestore::util::ThrowInvalidArgument;
// Public constant
extern "C" const int64_t kFIRFirestoreCacheSizeUnlimited = Settings::CacheSizeUnlimited;
@implementation FIRFirestoreSettings
- (instancetype)init {
if (self = [super init]) {
_host = [NSString stringWithUTF8String:Settings::DefaultHost];
_sslEnabled = Settings::DefaultSslEnabled;
_dispatchQueue = dispatch_get_main_queue();
_persistenceEnabled = Settings::DefaultPersistenceEnabled;
_cacheSizeBytes = Settings::DefaultCacheSizeBytes;
}
return self;
}
- (BOOL)isEqual:(id)other {
if (self == other) {
return YES;
} else if (![other isKindOfClass:[FIRFirestoreSettings class]]) {
return NO;
}
FIRFirestoreSettings *otherSettings = (FIRFirestoreSettings *)other;
BOOL equal = [self.host isEqual:otherSettings.host] &&
self.isSSLEnabled == otherSettings.isSSLEnabled &&
self.dispatchQueue == otherSettings.dispatchQueue &&
self.isPersistenceEnabled == otherSettings.isPersistenceEnabled &&
self.cacheSizeBytes == otherSettings.cacheSizeBytes;
if (equal && self.cacheSettings != nil && otherSettings.cacheSettings != nil) {
equal = [self.cacheSettings isEqual:otherSettings];
} else if (equal) {
equal = (self.cacheSettings == otherSettings.cacheSettings);
}
return equal;
}
- (NSUInteger)hash {
NSUInteger result = [self.host hash];
result = 31 * result + (self.isSSLEnabled ? 1231 : 1237);
// Ignore the dispatchQueue to avoid having to deal with sizeof(dispatch_queue_t).
result = 31 * result + (self.isPersistenceEnabled ? 1231 : 1237);
result = 31 * result + (NSUInteger)self.cacheSizeBytes;
if ([_cacheSettings isKindOfClass:[FIRPersistentCacheSettings class]]) {
FIRPersistentCacheSettings *casted = (FIRPersistentCacheSettings *)_cacheSettings;
result = 31 * result + casted.internalSettings.Hash();
} else if ([_cacheSettings isKindOfClass:[FIRMemoryCacheSettings class]]) {
FIRMemoryCacheSettings *casted = (FIRMemoryCacheSettings *)_cacheSettings;
result = 31 * result + casted.internalSettings.Hash();
}
return result;
}
- (id)copyWithZone:(__unused NSZone *_Nullable)zone {
FIRFirestoreSettings *copy = [[FIRFirestoreSettings alloc] init];
copy.host = _host;
copy.sslEnabled = _sslEnabled;
copy.dispatchQueue = _dispatchQueue;
copy.persistenceEnabled = _persistenceEnabled;
copy.cacheSizeBytes = _cacheSizeBytes;
copy.cacheSettings = _cacheSettings;
return copy;
}
- (void)setHost:(NSString *)host {
if (!host) {
ThrowInvalidArgument("Host setting may not be nil. You should generally just use the default "
"value (which is %s)",
Settings::DefaultHost);
}
_host = [host mutableCopy];
}
- (void)setDispatchQueue:(dispatch_queue_t)dispatchQueue {
if (!dispatchQueue) {
ThrowInvalidArgument(
"Dispatch queue setting may not be nil. Create a new dispatch queue with "
"dispatch_queue_create(\"com.example.MyQueue\", NULL) or just use the default (which is "
"the main queue, returned from dispatch_get_main_queue())");
}
_dispatchQueue = dispatchQueue;
}
- (void)setCacheSizeBytes:(int64_t)cacheSizeBytes {
if (cacheSizeBytes != kFIRFirestoreCacheSizeUnlimited &&
cacheSizeBytes < Settings::MinimumCacheSizeBytes) {
ThrowInvalidArgument("Cache size must be set to at least %s bytes",
Settings::MinimumCacheSizeBytes);
}
_cacheSizeBytes = cacheSizeBytes;
}
- (void)setCacheSettings:(id<FIRLocalCacheSettings, NSObject>)cacheSettings {
_cacheSettings = cacheSettings;
}
- (BOOL)isUsingDefaultHost {
NSString *defaultHost = [NSString stringWithUTF8String:Settings::DefaultHost];
return [self.host isEqualToString:defaultHost];
}
- (Settings)internalSettings {
Settings settings;
settings.set_host(MakeString(_host));
settings.set_ssl_enabled(_sslEnabled);
settings.set_persistence_enabled(_persistenceEnabled);
settings.set_cache_size_bytes(_cacheSizeBytes);
if ([_cacheSettings isKindOfClass:[FIRPersistentCacheSettings class]]) {
FIRPersistentCacheSettings *casted = (FIRPersistentCacheSettings *)_cacheSettings;
settings.set_local_cache_settings(casted.internalSettings);
} else if ([_cacheSettings isKindOfClass:[FIRMemoryCacheSettings class]]) {
FIRMemoryCacheSettings *casted = (FIRMemoryCacheSettings *)_cacheSettings;
settings.set_local_cache_settings(casted.internalSettings);
}
return settings;
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,29 @@
/*
* 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 "FIRFirestoreSource.h"
namespace firebase {
namespace firestore {
namespace api {
enum class Source;
Source MakeSource(FIRFirestoreSource source);
} // namespace api
} // namespace firestore
} // namespace firebase

View File

@@ -0,0 +1,41 @@
/*
* 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 "Firestore/Source/API/FIRFirestoreSource+Internal.h"
#include "Firestore/core/src/api/source.h"
#include "Firestore/core/src/util/hard_assert.h"
namespace firebase {
namespace firestore {
namespace api {
Source MakeSource(FIRFirestoreSource source) {
switch (source) {
case FIRFirestoreSourceDefault:
return Source::Default;
case FIRFirestoreSourceServer:
return Source::Server;
case FIRFirestoreSourceCache:
return Source::Cache;
}
UNREACHABLE();
}
} // namespace api
} // namespace firestore
} // namespace firebase

View File

@@ -0,0 +1,22 @@
/*
* 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.
*/
/** Version for Firestore. */
#import <Foundation/Foundation.h>
/** Version string for the Firebase Firestore SDK. */
FOUNDATION_EXPORT const char *const FIRFirestoreVersionString;

View File

@@ -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 "Firestore/Source/API/FIRFirestoreVersion.h"
#include "Firestore/core/include/firebase/firestore/firestore_version.h"
using firebase::firestore::kFirestoreVersionString;
// Because `kFirestoreVersionString` is subject to constant initialization, this
// is not affected by static initialization order fiasco.
extern "C" const char *const FIRFirestoreVersionString = kFirestoreVersionString;

View File

@@ -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 "FIRGeoPoint.h"
NS_ASSUME_NONNULL_BEGIN
/** Internal FIRGeoPoint API we don't want exposed in our public header files. */
@interface FIRGeoPoint (Internal)
- (NSComparisonResult)compare:(FIRGeoPoint *)other;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,93 @@
/*
* 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 "Firestore/Source/API/FIRGeoPoint+Internal.h"
#include "Firestore/core/include/firebase/firestore/geo_point.h"
#include "Firestore/core/src/util/comparison.h"
#include "Firestore/core/src/util/exception.h"
using firebase::firestore::util::DoubleBitwiseEquals;
using firebase::firestore::util::DoubleBitwiseHash;
using firebase::firestore::util::ThrowInvalidArgument;
using firebase::firestore::util::WrapCompare;
NS_ASSUME_NONNULL_BEGIN
@implementation FIRGeoPoint
- (instancetype)initWithLatitude:(double)latitude longitude:(double)longitude {
if (self = [super init]) {
if (latitude < -90 || latitude > 90 || !isfinite(latitude)) {
ThrowInvalidArgument("GeoPoint requires a latitude value in the range of [-90, 90], "
"but was %s",
latitude);
}
if (longitude < -180 || longitude > 180 || !isfinite(longitude)) {
ThrowInvalidArgument("GeoPoint requires a longitude value in the range of [-180, 180], "
"but was %s",
longitude);
}
_latitude = latitude;
_longitude = longitude;
}
return self;
}
#pragma mark - NSObject methods
- (NSString *)description {
return [NSString stringWithFormat:@"<FIRGeoPoint: (%f, %f)>", self.latitude, self.longitude];
}
- (BOOL)isEqual:(id)other {
if (self == other) {
return YES;
}
if (![other isKindOfClass:[FIRGeoPoint class]]) {
return NO;
}
FIRGeoPoint *otherGeoPoint = (FIRGeoPoint *)other;
return DoubleBitwiseEquals(self.latitude, otherGeoPoint.latitude) &&
DoubleBitwiseEquals(self.longitude, otherGeoPoint.longitude);
}
- (NSUInteger)hash {
return 31 * DoubleBitwiseHash(self.latitude) + DoubleBitwiseHash(self.longitude);
}
/** Implements NSCopying without actually copying because geopoints are immutable. */
- (id)copyWithZone:(__unused NSZone *_Nullable)zone {
return self;
}
@end
@implementation FIRGeoPoint (Internal)
- (NSComparisonResult)compare:(FIRGeoPoint *)other {
NSComparisonResult result = WrapCompare<double>(self.latitude, other.latitude);
if (result != NSOrderedSame) {
return result;
} else {
return WrapCompare<double>(self.longitude, other.longitude);
}
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -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.
*/
#include <memory>
#import "FIRListenerRegistration.h"
#include "Firestore/core/src/api/listener_registration.h"
namespace api = firebase::firestore::api;
NS_ASSUME_NONNULL_BEGIN
/** Private implementation of the FIRListenerRegistration protocol. */
@interface FSTListenerRegistration : NSObject <FIRListenerRegistration>
- (instancetype)initWithRegistration:(std::unique_ptr<api::ListenerRegistration>)registration;
@end
NS_ASSUME_NONNULL_END

View File

@@ -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 "Firestore/Source/API/FIRListenerRegistration+Internal.h"
NS_ASSUME_NONNULL_BEGIN
@implementation FSTListenerRegistration {
std::unique_ptr<api::ListenerRegistration> _registration;
}
- (instancetype)initWithRegistration:(std::unique_ptr<api::ListenerRegistration>)registration {
if (self = [super init]) {
_registration = std::move(registration);
}
return self;
}
- (void)remove {
_registration->Remove();
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2021 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.
*/
#include <memory>
#import "FIRLoadBundleTask.h"
#include "Firestore/core/src/api/load_bundle_task.h"
namespace api = firebase::firestore::api;
NS_ASSUME_NONNULL_BEGIN
@interface FIRLoadBundleTaskProgress (Internal)
- (instancetype)initWithInternal:(api::LoadBundleTaskProgress)progress;
@end
/** Private implementation of the FIRListenerRegistration protocol. */
@interface FIRLoadBundleTask (Internal)
- (instancetype)initWithTask:(std::shared_ptr<api::LoadBundleTask>)task;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2021 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 "FIRLoadBundleTask.h"
#include <memory>
#import "Firestore/Source/API/FIRLoadBundleTask+Internal.h"
#include "Firestore/core/src/api/load_bundle_task.h"
#include "Firestore/core/src/util/exception.h"
NS_ASSUME_NONNULL_BEGIN
namespace {
using firebase::firestore::util::ThrowInvalidArgument;
} // namespace
@implementation FIRLoadBundleTaskProgress {
}
- (instancetype)initWithInternal:(api::LoadBundleTaskProgress)progress {
if (self = [super init]) {
_bytesLoaded = (NSInteger)progress.bytes_loaded();
_documentsLoaded = progress.documents_loaded();
_totalBytes = (NSInteger)progress.total_bytes();
_totalDocuments = progress.total_documents();
switch (progress.state()) {
case api::LoadBundleTaskState::kInProgress:
_state = FIRLoadBundleTaskStateInProgress;
break;
case api::LoadBundleTaskState::kSuccess:
_state = FIRLoadBundleTaskStateSuccess;
break;
case api::LoadBundleTaskState::kError:
_state = FIRLoadBundleTaskStateError;
break;
}
}
return self;
}
- (BOOL)isEqual:(id)other {
if (self == other) {
return YES;
} else if (![other isKindOfClass:[FIRLoadBundleTaskProgress class]]) {
return NO;
}
FIRLoadBundleTaskProgress *otherProgress = (FIRLoadBundleTaskProgress *)other;
return self.documentsLoaded == otherProgress.documentsLoaded &&
self.totalDocuments == otherProgress.totalDocuments &&
self.bytesLoaded == otherProgress.bytesLoaded &&
self.totalBytes == otherProgress.totalBytes && self.state == otherProgress.state;
}
@end
@implementation FIRLoadBundleTask {
std::shared_ptr<api::LoadBundleTask> _task;
}
- (instancetype)initWithTask:(std::shared_ptr<api::LoadBundleTask>)task {
if (self = [super init]) {
_task = std::move(task);
}
return self;
}
- (FIRLoadBundleObserverHandle)addObserver:(void (^)(FIRLoadBundleTaskProgress *progress))observer {
if (!observer) {
ThrowInvalidArgument("Handler cannot be nil");
}
api::LoadBundleTask::ProgressObserver core_observer =
[observer](api::LoadBundleTaskProgress internal_progress) {
observer([[FIRLoadBundleTaskProgress alloc] initWithInternal:internal_progress]);
};
return (FIRLoadBundleObserverHandle)_task->Observe(std::move(core_observer));
}
- (void)removeObserverWithHandle:(FIRLoadBundleObserverHandle)handle {
_task->RemoveObserver(handle);
}
- (void)removeAllObservers {
_task->RemoveAllObservers();
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2023 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 "FIRLocalCacheSettings.h"
#import <Foundation/Foundation.h>
#include <memory>
#include "Firestore/core/src/api/settings.h"
NS_ASSUME_NONNULL_BEGIN
@interface FIRPersistentCacheSettings (Internal)
- (const firebase::firestore::api::PersistentCacheSettings&)internalSettings;
@end
@interface FIRMemoryCacheSettings (Internal)
@property(nonatomic, assign) const firebase::firestore::api::MemoryCacheSettings& internalSettings;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,238 @@
/*
* Copyright 2023 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.
*/
#include "FIRLocalCacheSettings.h"
#include <memory>
#import "FIRLocalCacheSettings+Internal.h"
#include "absl/memory/memory.h"
#include "Firestore/core/src/api/settings.h"
#include "Firestore/core/src/util/exception.h"
NS_ASSUME_NONNULL_BEGIN
namespace api = firebase::firestore::api;
using api::MemoryCacheSettings;
using api::MemoryEagerGcSettings;
using api::MemoryLruGcSettings;
using api::PersistentCacheSettings;
using api::Settings;
using firebase::firestore::util::ThrowInvalidArgument;
@implementation FIRPersistentCacheSettings {
PersistentCacheSettings _internalSettings;
}
- (BOOL)isEqual:(id)other {
if (self == other) {
return YES;
} else if (![other isKindOfClass:[FIRPersistentCacheSettings class]]) {
return NO;
}
FIRPersistentCacheSettings *otherSettings = (FIRPersistentCacheSettings *)other;
return _internalSettings == otherSettings.internalSettings;
}
- (NSUInteger)hash {
return _internalSettings.Hash();
}
- (id)copyWithZone:(__unused NSZone *_Nullable)zone {
FIRPersistentCacheSettings *copy = [[FIRPersistentCacheSettings alloc] init];
copy.internalSettings = self.internalSettings;
return copy;
}
- (void)setInternalSettings:(const PersistentCacheSettings &)settings {
_internalSettings = settings;
}
- (const PersistentCacheSettings &)internalSettings {
return _internalSettings;
}
- (instancetype)init {
self = [super init];
self.internalSettings = PersistentCacheSettings{};
return self;
}
- (instancetype)initWithSizeBytes:(NSNumber *)size {
self = [super init];
if (size.longLongValue != Settings::CacheSizeUnlimited &&
size.longLongValue < Settings::MinimumCacheSizeBytes) {
ThrowInvalidArgument("Cache size must be set to at least %s bytes",
Settings::MinimumCacheSizeBytes);
}
self.internalSettings = PersistentCacheSettings{}.WithSizeBytes(size.longLongValue);
return self;
}
@end
@implementation FIRMemoryEagerGCSettings {
MemoryEagerGcSettings _internalSettings;
}
- (BOOL)isEqual:(id)other {
if (self == other) {
return YES;
} else if (![other isKindOfClass:[FIRMemoryEagerGCSettings class]]) {
return NO;
}
FIRMemoryEagerGCSettings *otherSettings = (FIRMemoryEagerGCSettings *)other;
return _internalSettings == otherSettings.internalSettings;
}
- (NSUInteger)hash {
return _internalSettings.Hash();
}
- (id)copyWithZone:(__unused NSZone *_Nullable)zone {
FIRMemoryEagerGCSettings *copy = [[FIRMemoryEagerGCSettings alloc] init];
copy.internalSettings = self.internalSettings;
return copy;
}
- (void)setInternalSettings:(const MemoryEagerGcSettings &)settings {
_internalSettings = settings;
}
- (const MemoryEagerGcSettings &)internalSettings {
return _internalSettings;
}
- (instancetype)init {
if (self = [super init]) {
self.internalSettings = MemoryEagerGcSettings{};
}
return self;
}
@end
@implementation FIRMemoryLRUGCSettings {
MemoryLruGcSettings _internalSettings;
}
- (BOOL)isEqual:(id)other {
if (self == other) {
return YES;
} else if (![other isKindOfClass:[FIRMemoryLRUGCSettings class]]) {
return NO;
}
FIRMemoryLRUGCSettings *otherSettings = (FIRMemoryLRUGCSettings *)other;
return _internalSettings == otherSettings.internalSettings;
}
- (NSUInteger)hash {
return _internalSettings.Hash();
}
- (id)copyWithZone:(__unused NSZone *_Nullable)zone {
FIRMemoryLRUGCSettings *copy = [[FIRMemoryLRUGCSettings alloc] init];
copy.internalSettings = self.internalSettings;
return copy;
}
- (void)setInternalSettings:(const MemoryLruGcSettings &)settings {
_internalSettings = settings;
}
- (const MemoryLruGcSettings &)internalSettings {
return _internalSettings;
}
- (instancetype)init {
if (self = [super init]) {
self.internalSettings = MemoryLruGcSettings{};
}
return self;
}
- (instancetype)initWithSizeBytes:(NSNumber *)size {
if (self = [super init]) {
self.internalSettings = MemoryLruGcSettings{}.WithSizeBytes(size.longLongValue);
}
return self;
}
@end
@implementation FIRMemoryCacheSettings {
MemoryCacheSettings _internalSettings;
}
- (BOOL)isEqual:(id)other {
if (self == other) {
return YES;
} else if (![other isKindOfClass:[FIRMemoryCacheSettings class]]) {
return NO;
}
FIRMemoryCacheSettings *otherSettings = (FIRMemoryCacheSettings *)other;
return _internalSettings == otherSettings.internalSettings;
}
- (NSUInteger)hash {
return _internalSettings.Hash();
}
- (id)copyWithZone:(__unused NSZone *_Nullable)zone {
FIRMemoryCacheSettings *copy = [[FIRMemoryCacheSettings alloc] init];
copy.internalSettings = self.internalSettings;
return copy;
}
- (void)setInternalSettings:(const MemoryCacheSettings &)settings {
_internalSettings = settings;
}
- (const MemoryCacheSettings &)internalSettings {
return _internalSettings;
}
- (instancetype)init {
if (self = [super init]) {
self.internalSettings = MemoryCacheSettings{};
}
return self;
}
- (instancetype)initWithGarbageCollectorSettings:
(id<FIRMemoryGarbageCollectorSettings, NSObject>)settings {
if (self = [super init]) {
if ([settings isKindOfClass:[FIRMemoryEagerGCSettings class]]) {
FIRMemoryEagerGCSettings *casted = (FIRMemoryEagerGCSettings *)settings;
self.internalSettings =
MemoryCacheSettings{}.WithMemoryGarbageCollectorSettings(casted.internalSettings);
} else if ([settings isKindOfClass:[FIRMemoryLRUGCSettings class]]) {
FIRMemoryLRUGCSettings *casted = (FIRMemoryLRUGCSettings *)settings;
self.internalSettings =
MemoryCacheSettings{}.WithMemoryGarbageCollectorSettings(casted.internalSettings);
}
}
return self;
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2023 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 "FIRPersistentCacheIndexManager.h"
#import <Foundation/Foundation.h>
#include <memory>
#include "Firestore/core/src/api/persistent_cache_index_manager.h"
NS_ASSUME_NONNULL_BEGIN
@interface FIRPersistentCacheIndexManager (/* Init */)
- (instancetype)initWithPersistentCacheIndexManager:
(std::shared_ptr<const firebase::firestore::api::PersistentCacheIndexManager>)indexManager
NS_DESIGNATED_INITIALIZER;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2023 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 "Firestore/Source/API/FIRFirestore+Internal.h"
#import "Firestore/Source/API/FIRPersistentCacheIndexManager+Internal.h"
using firebase::firestore::api::Firestore;
using firebase::firestore::api::PersistentCacheIndexManager;
NS_ASSUME_NONNULL_BEGIN
@implementation FIRPersistentCacheIndexManager {
/** The `Firestore` instance that created this index manager. */
std::shared_ptr<const PersistentCacheIndexManager> _indexManager;
}
- (instancetype)initWithPersistentCacheIndexManager:
(std::shared_ptr<const PersistentCacheIndexManager>)indexManager {
if (self = [super init]) {
_indexManager = indexManager;
}
return self;
}
- (void)enableIndexAutoCreation {
_indexManager->EnableIndexAutoCreation();
}
- (void)disableIndexAutoCreation {
_indexManager->DisableIndexAutoCreation();
}
- (void)deleteAllIndexes {
_indexManager->DeleteAllFieldIndexes();
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,50 @@
/*
* 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 "FIRAggregateField.h"
#import "FIRQuery.h"
#include <memory>
#include "Firestore/core/src/api/api_fwd.h"
#include "Firestore/core/src/core/core_fwd.h"
@class FIRFilter;
namespace api = firebase::firestore::api;
namespace core = firebase::firestore::core;
NS_ASSUME_NONNULL_BEGIN
@interface FIRQuery (/* Init */)
- (instancetype)initWithQuery:(api::Query &&)query NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithQuery:(core::Query)query
firestore:(std::shared_ptr<api::Firestore>)firestore;
@end
/** Internal FIRQuery API we don't want exposed in our public header files. */
@interface FIRQuery (Internal)
- (const core::Query &)query;
- (const api::Query &)apiQuery;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,696 @@
/*
* Copyright 2017 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 "FIRQuery.h"
#include <memory>
#include <utility>
#include <vector>
#import "FIRDocumentReference.h"
#import "FIRFirestoreErrors.h"
#import "Firestore/Source/API/FIRAggregateField+Internal.h"
#import "Firestore/Source/API/FIRAggregateQuery+Internal.h"
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
#import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h"
#import "Firestore/Source/API/FIRFieldPath+Internal.h"
#import "Firestore/Source/API/FIRFieldValue+Internal.h"
#import "Firestore/Source/API/FIRFilter+Internal.h"
#import "Firestore/Source/API/FIRFirestore+Internal.h"
#import "Firestore/Source/API/FIRFirestoreSource+Internal.h"
#import "Firestore/Source/API/FIRListenerRegistration+Internal.h"
#import "Firestore/Source/API/FIRQuery+Internal.h"
#import "Firestore/Source/API/FIRQuerySnapshot+Internal.h"
#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h"
#import "Firestore/Source/API/FSTUserDataReader.h"
#import "Firestore/Source/API/converters.h"
#include "Firestore/core/src/api/query_core.h"
#include "Firestore/core/src/api/query_listener_registration.h"
#include "Firestore/core/src/api/query_snapshot.h"
#include "Firestore/core/src/api/source.h"
#include "Firestore/core/src/core/bound.h"
#include "Firestore/core/src/core/composite_filter.h"
#include "Firestore/core/src/core/direction.h"
#include "Firestore/core/src/core/field_filter.h"
#include "Firestore/core/src/core/filter.h"
#include "Firestore/core/src/core/firestore_client.h"
#include "Firestore/core/src/core/listen_options.h"
#include "Firestore/core/src/core/order_by.h"
#include "Firestore/core/src/core/query.h"
#include "Firestore/core/src/model/document_key.h"
#include "Firestore/core/src/model/field_path.h"
#include "Firestore/core/src/model/resource_path.h"
#include "Firestore/core/src/model/server_timestamp_util.h"
#include "Firestore/core/src/model/value_util.h"
#include "Firestore/core/src/nanopb/message.h"
#include "Firestore/core/src/nanopb/nanopb_util.h"
#include "Firestore/core/src/util/error_apple.h"
#include "Firestore/core/src/util/exception.h"
#include "Firestore/core/src/util/statusor.h"
#include "Firestore/core/src/util/string_apple.h"
#include "absl/strings/match.h"
namespace nanopb = firebase::firestore::nanopb;
using firebase::firestore::google_firestore_v1_ArrayValue;
using firebase::firestore::google_firestore_v1_Value;
using firebase::firestore::google_firestore_v1_Value_fields;
using firebase::firestore::api::Firestore;
using firebase::firestore::api::MakeListenSource;
using firebase::firestore::api::Query;
using firebase::firestore::api::QueryListenerRegistration;
using firebase::firestore::api::QuerySnapshot;
using firebase::firestore::api::QuerySnapshotListener;
using firebase::firestore::api::SnapshotMetadata;
using firebase::firestore::api::Source;
using firebase::firestore::core::AsyncEventListener;
using firebase::firestore::core::Bound;
using firebase::firestore::core::CompositeFilter;
using firebase::firestore::core::Direction;
using firebase::firestore::core::EventListener;
using firebase::firestore::core::FieldFilter;
using firebase::firestore::core::Filter;
using firebase::firestore::core::ListenOptions;
using firebase::firestore::core::OrderBy;
using firebase::firestore::core::QueryListener;
using firebase::firestore::core::ViewSnapshot;
using firebase::firestore::model::DatabaseId;
using firebase::firestore::model::DeepClone;
using firebase::firestore::model::Document;
using firebase::firestore::model::DocumentKey;
using firebase::firestore::model::FieldPath;
using firebase::firestore::model::GetTypeOrder;
using firebase::firestore::model::IsServerTimestamp;
using firebase::firestore::model::RefValue;
using firebase::firestore::model::ResourcePath;
using firebase::firestore::model::TypeOrder;
using firebase::firestore::nanopb::CheckedSize;
using firebase::firestore::nanopb::MakeArray;
using firebase::firestore::nanopb::MakeSharedMessage;
using firebase::firestore::nanopb::MakeString;
using firebase::firestore::nanopb::Message;
using firebase::firestore::nanopb::SharedMessage;
using firebase::firestore::util::MakeNSError;
using firebase::firestore::util::MakeString;
using firebase::firestore::util::StatusOr;
using firebase::firestore::util::ThrowInvalidArgument;
NS_ASSUME_NONNULL_BEGIN
namespace {
FieldPath MakeFieldPath(NSString *field) {
return FieldPath::FromDotSeparatedString(MakeString(field));
}
FIRQuery *Wrap(Query &&query) {
return [[FIRQuery alloc] initWithQuery:std::move(query)];
}
int32_t SaturatedLimitValue(NSInteger limit) {
int32_t internal_limit;
if (limit == NSNotFound || limit >= core::Target::kNoLimit) {
internal_limit = core::Target::kNoLimit;
} else {
internal_limit = static_cast<int32_t>(limit);
}
return internal_limit;
}
} // namespace
@implementation FIRQuery {
Query _query;
}
#pragma mark - Constructor Methods
- (instancetype)initWithQuery:(Query &&)query {
if (self = [super init]) {
_query = std::move(query);
}
return self;
}
- (instancetype)initWithQuery:(core::Query)query firestore:(std::shared_ptr<Firestore>)firestore {
return [self initWithQuery:Query{std::move(query), std::move(firestore)}];
}
#pragma mark - NSObject Methods
- (BOOL)isEqual:(nullable id)other {
if (other == self) return YES;
if (![[other class] isEqual:[self class]]) return NO;
auto otherQuery = static_cast<FIRQuery *>(other);
return _query == otherQuery->_query;
}
- (NSUInteger)hash {
return _query.Hash();
}
#pragma mark - Public Methods
- (FIRFirestore *)firestore {
return [FIRFirestore recoverFromFirestore:_query.firestore()];
}
- (void)getDocumentsWithCompletion:(void (^)(FIRQuerySnapshot *_Nullable snapshot,
NSError *_Nullable error))completion {
_query.GetDocuments(Source::Default, [self wrapQuerySnapshotBlock:completion]);
}
- (void)getDocumentsWithSource:(FIRFirestoreSource)publicSource
completion:(void (^)(FIRQuerySnapshot *_Nullable snapshot,
NSError *_Nullable error))completion {
Source source = api::MakeSource(publicSource);
_query.GetDocuments(source, [self wrapQuerySnapshotBlock:completion]);
}
- (id<FIRListenerRegistration>)addSnapshotListener:(FIRQuerySnapshotBlock)listener {
return [self addSnapshotListenerWithIncludeMetadataChanges:NO listener:listener];
}
- (id<FIRListenerRegistration>)
addSnapshotListenerWithIncludeMetadataChanges:(BOOL)includeMetadataChanges
listener:(FIRQuerySnapshotBlock)listener {
auto options = ListenOptions::FromIncludeMetadataChanges(includeMetadataChanges);
return [self addSnapshotListenerInternalWithOptions:options listener:listener];
}
- (id<FIRListenerRegistration>)addSnapshotListenerWithOptions:(FIRSnapshotListenOptions *)options
listener:(FIRQuerySnapshotBlock)listener {
ListenOptions listenOptions =
ListenOptions::FromOptions(options.includeMetadataChanges, MakeListenSource(options.source));
return [self addSnapshotListenerInternalWithOptions:listenOptions listener:listener];
}
- (id<FIRListenerRegistration>)addSnapshotListenerInternalWithOptions:(ListenOptions)internalOptions
listener:
(FIRQuerySnapshotBlock)listener {
std::shared_ptr<Firestore> firestore = self.firestore.wrapped;
const core::Query &query = self.query;
// Convert from ViewSnapshots to QuerySnapshots.
auto view_listener = EventListener<ViewSnapshot>::Create(
[listener, firestore, query](StatusOr<ViewSnapshot> maybe_snapshot) {
if (!maybe_snapshot.status().ok()) {
listener(nil, MakeNSError(maybe_snapshot.status()));
return;
}
ViewSnapshot snapshot = std::move(maybe_snapshot).ValueOrDie();
SnapshotMetadata metadata(snapshot.has_pending_writes(), snapshot.from_cache());
listener([[FIRQuerySnapshot alloc] initWithFirestore:firestore
originalQuery:query
snapshot:std::move(snapshot)
metadata:std::move(metadata)],
nil);
});
// Call the view_listener on the user Executor.
auto async_listener = AsyncEventListener<ViewSnapshot>::Create(
firestore->client()->user_executor(), std::move(view_listener));
std::shared_ptr<QueryListener> query_listener =
firestore->client()->ListenToQuery(query, internalOptions, async_listener);
return [[FSTListenerRegistration alloc]
initWithRegistration:absl::make_unique<QueryListenerRegistration>(firestore->client(),
std::move(async_listener),
std::move(query_listener))];
}
- (FIRQuery *)queryWhereFilter:(FIRFilter *)filter {
Filter parsedFilter = [self parseFilter:filter];
if (parsedFilter.IsEmpty()) {
// Return the existing query if not adding any more filters (e.g. an empty composite filter).
return self;
}
return Wrap(_query.AddNewFilter(std::move(parsedFilter)));
}
- (FIRQuery *)queryWhereField:(NSString *)field isEqualTo:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereField:field isEqualTo:value]];
}
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path isEqualTo:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereFieldPath:path isEqualTo:value]];
}
- (FIRQuery *)queryWhereField:(NSString *)field isNotEqualTo:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereField:field isNotEqualTo:value]];
}
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path isNotEqualTo:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereFieldPath:path isNotEqualTo:value]];
}
- (FIRQuery *)queryWhereField:(NSString *)field isGreaterThan:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereField:field isGreaterThan:value]];
}
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path isGreaterThan:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereFieldPath:path isGreaterThan:value]];
}
- (FIRQuery *)queryWhereField:(NSString *)field isGreaterThanOrEqualTo:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereField:field isGreaterThanOrEqualTo:value]];
}
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path isGreaterThanOrEqualTo:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereFieldPath:path isGreaterThanOrEqualTo:value]];
}
- (FIRQuery *)queryWhereField:(NSString *)field isLessThan:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereField:field isLessThan:value]];
}
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path isLessThan:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereFieldPath:path isLessThan:value]];
}
- (FIRQuery *)queryWhereField:(NSString *)field isLessThanOrEqualTo:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereField:field isLessThanOrEqualTo:value]];
}
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path isLessThanOrEqualTo:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereFieldPath:path isLessThanOrEqualTo:value]];
}
- (FIRQuery *)queryWhereField:(NSString *)field arrayContains:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereField:field arrayContains:value]];
}
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path arrayContains:(id)value {
return [self queryWhereFilter:[FIRFilter filterWhereFieldPath:path arrayContains:value]];
}
- (FIRQuery *)queryWhereField:(NSString *)field arrayContainsAny:(NSArray<id> *)values {
return [self queryWhereFilter:[FIRFilter filterWhereField:field arrayContainsAny:values]];
}
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path arrayContainsAny:(NSArray<id> *)values {
return [self queryWhereFilter:[FIRFilter filterWhereFieldPath:path arrayContainsAny:values]];
}
- (FIRQuery *)queryWhereField:(NSString *)field in:(NSArray<id> *)values {
return [self queryWhereFilter:[FIRFilter filterWhereField:field in:values]];
}
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path in:(NSArray<id> *)values {
return [self queryWhereFilter:[FIRFilter filterWhereFieldPath:path in:values]];
}
- (FIRQuery *)queryWhereField:(NSString *)field notIn:(NSArray<id> *)values {
return [self queryWhereFilter:[FIRFilter filterWhereField:field notIn:values]];
}
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path notIn:(NSArray<id> *)values {
return [self queryWhereFilter:[FIRFilter filterWhereFieldPath:path notIn:values]];
}
- (FIRQuery *)queryFilteredUsingComparisonPredicate:(NSPredicate *)predicate {
NSComparisonPredicate *comparison = (NSComparisonPredicate *)predicate;
if (comparison.comparisonPredicateModifier != NSDirectPredicateModifier) {
ThrowInvalidArgument("Invalid query. Predicate cannot have an aggregate modifier.");
}
NSString *path;
id value = nil;
if ([comparison.leftExpression expressionType] == NSKeyPathExpressionType &&
[comparison.rightExpression expressionType] == NSConstantValueExpressionType) {
path = comparison.leftExpression.keyPath;
value = comparison.rightExpression.constantValue;
switch (comparison.predicateOperatorType) {
case NSEqualToPredicateOperatorType:
return [self queryWhereField:path isEqualTo:value];
case NSLessThanPredicateOperatorType:
return [self queryWhereField:path isLessThan:value];
case NSLessThanOrEqualToPredicateOperatorType:
return [self queryWhereField:path isLessThanOrEqualTo:value];
case NSGreaterThanPredicateOperatorType:
return [self queryWhereField:path isGreaterThan:value];
case NSGreaterThanOrEqualToPredicateOperatorType:
return [self queryWhereField:path isGreaterThanOrEqualTo:value];
case NSNotEqualToPredicateOperatorType:
return [self queryWhereField:path isNotEqualTo:value];
case NSContainsPredicateOperatorType:
return [self queryWhereField:path arrayContains:value];
case NSInPredicateOperatorType:
return [self queryWhereField:path in:value];
default:; // Fallback below to throw assertion.
}
} else if ([comparison.leftExpression expressionType] == NSConstantValueExpressionType &&
[comparison.rightExpression expressionType] == NSKeyPathExpressionType) {
path = comparison.rightExpression.keyPath;
value = comparison.leftExpression.constantValue;
switch (comparison.predicateOperatorType) {
case NSEqualToPredicateOperatorType:
return [self queryWhereField:path isEqualTo:value];
case NSLessThanPredicateOperatorType:
return [self queryWhereField:path isGreaterThan:value];
case NSLessThanOrEqualToPredicateOperatorType:
return [self queryWhereField:path isGreaterThanOrEqualTo:value];
case NSGreaterThanPredicateOperatorType:
return [self queryWhereField:path isLessThan:value];
case NSGreaterThanOrEqualToPredicateOperatorType:
return [self queryWhereField:path isLessThanOrEqualTo:value];
case NSNotEqualToPredicateOperatorType:
return [self queryWhereField:path isNotEqualTo:value];
case NSContainsPredicateOperatorType:
return [self queryWhereField:path arrayContains:value];
case NSInPredicateOperatorType:
return [self queryWhereField:path in:value];
default:; // Fallback below to throw assertion.
}
} else {
ThrowInvalidArgument(
"Invalid query. Predicate comparisons must include a key path and a constant.");
}
// Fallback cases of unsupported comparison operator.
switch (comparison.predicateOperatorType) {
case NSCustomSelectorPredicateOperatorType:
ThrowInvalidArgument("Invalid query. Custom predicate filters are not supported.");
break;
default:
ThrowInvalidArgument("Invalid query. Operator type %s is not supported.",
comparison.predicateOperatorType);
}
}
- (FIRQuery *)queryFilteredUsingCompoundPredicate:(NSPredicate *)predicate {
NSCompoundPredicate *compound = (NSCompoundPredicate *)predicate;
if (compound.compoundPredicateType != NSAndPredicateType || compound.subpredicates.count == 0) {
ThrowInvalidArgument("Invalid query. Only compound queries using AND are supported.");
}
FIRQuery *query = self;
for (NSPredicate *pred in compound.subpredicates) {
query = [query queryFilteredUsingPredicate:pred];
}
return query;
}
- (FIRQuery *)queryFilteredUsingPredicate:(NSPredicate *)predicate {
if ([predicate isKindOfClass:[NSComparisonPredicate class]]) {
return [self queryFilteredUsingComparisonPredicate:predicate];
} else if ([predicate isKindOfClass:[NSCompoundPredicate class]]) {
return [self queryFilteredUsingCompoundPredicate:predicate];
} else if ([predicate isKindOfClass:[[NSPredicate predicateWithBlock:^BOOL(id, NSDictionary *) {
return true;
}] class]]) {
ThrowInvalidArgument("Invalid query. Block-based predicates are not supported. Please use "
"predicateWithFormat to create predicates instead.");
} else {
ThrowInvalidArgument("Invalid query. Expect comparison or compound of comparison predicate. "
"Please use predicateWithFormat to create predicates.");
}
}
- (FIRQuery *)queryOrderedByField:(NSString *)field {
return [self queryOrderedByField:field descending:NO];
}
- (FIRQuery *)queryOrderedByFieldPath:(FIRFieldPath *)fieldPath {
return [self queryOrderedByFieldPath:fieldPath descending:NO];
}
- (FIRQuery *)queryOrderedByField:(NSString *)field descending:(BOOL)descending {
return [self queryOrderedByFieldPath:MakeFieldPath(field)
direction:Direction::FromDescending(descending)];
}
- (FIRQuery *)queryOrderedByFieldPath:(FIRFieldPath *)fieldPath descending:(BOOL)descending {
return [self queryOrderedByFieldPath:fieldPath.internalValue
direction:Direction::FromDescending(descending)];
}
- (FIRQuery *)queryOrderedByFieldPath:(model::FieldPath)fieldPath direction:(Direction)direction {
return Wrap(_query.OrderBy(std::move(fieldPath), direction));
}
- (FIRQuery *)queryLimitedTo:(NSInteger)limit {
return Wrap(_query.LimitToFirst(SaturatedLimitValue(limit)));
}
- (FIRQuery *)queryLimitedToLast:(NSInteger)limit {
return Wrap(_query.LimitToLast(SaturatedLimitValue(limit)));
}
- (FIRQuery *)queryStartingAtDocument:(FIRDocumentSnapshot *)snapshot {
Bound bound = [self boundFromSnapshot:snapshot isInclusive:YES];
return Wrap(_query.StartAt(std::move(bound)));
}
- (FIRQuery *)queryStartingAtValues:(NSArray *)fieldValues {
Bound bound = [self boundFromFieldValues:fieldValues isInclusive:YES];
return Wrap(_query.StartAt(std::move(bound)));
}
- (FIRQuery *)queryStartingAfterDocument:(FIRDocumentSnapshot *)snapshot {
Bound bound = [self boundFromSnapshot:snapshot isInclusive:NO];
return Wrap(_query.StartAt(std::move(bound)));
}
- (FIRQuery *)queryStartingAfterValues:(NSArray *)fieldValues {
Bound bound = [self boundFromFieldValues:fieldValues isInclusive:NO];
return Wrap(_query.StartAt(std::move(bound)));
}
- (FIRQuery *)queryEndingBeforeDocument:(FIRDocumentSnapshot *)snapshot {
Bound bound = [self boundFromSnapshot:snapshot isInclusive:NO];
return Wrap(_query.EndAt(std::move(bound)));
}
- (FIRQuery *)queryEndingBeforeValues:(NSArray *)fieldValues {
Bound bound = [self boundFromFieldValues:fieldValues isInclusive:NO];
return Wrap(_query.EndAt(std::move(bound)));
}
- (FIRQuery *)queryEndingAtDocument:(FIRDocumentSnapshot *)snapshot {
Bound bound = [self boundFromSnapshot:snapshot isInclusive:YES];
return Wrap(_query.EndAt(std::move(bound)));
}
- (FIRQuery *)queryEndingAtValues:(NSArray *)fieldValues {
Bound bound = [self boundFromFieldValues:fieldValues isInclusive:YES];
return Wrap(_query.EndAt(std::move(bound)));
}
- (FIRAggregateQuery *)count {
FIRAggregateField *countAF = [FIRAggregateField aggregateFieldForCount];
return [[FIRAggregateQuery alloc] initWithQuery:self aggregateFields:@[ countAF ]];
}
- (FIRAggregateQuery *)aggregate:(NSArray<FIRAggregateField *> *)aggregateFields {
return [[FIRAggregateQuery alloc] initWithQuery:self aggregateFields:aggregateFields];
}
#pragma mark - Private Methods
- (Message<google_firestore_v1_Value>)parsedQueryValue:(id)value {
return [self.firestore.dataReader parsedQueryValue:value];
}
- (Message<google_firestore_v1_Value>)parsedQueryValue:(id)value allowArrays:(bool)allowArrays {
return [self.firestore.dataReader parsedQueryValue:value allowArrays:allowArrays];
}
- (QuerySnapshotListener)wrapQuerySnapshotBlock:(FIRQuerySnapshotBlock)block {
class Converter : public EventListener<QuerySnapshot> {
public:
explicit Converter(FIRQuerySnapshotBlock block) : block_(block) {
}
void OnEvent(StatusOr<QuerySnapshot> maybe_snapshot) override {
if (maybe_snapshot.ok()) {
FIRQuerySnapshot *result =
[[FIRQuerySnapshot alloc] initWithSnapshot:std::move(maybe_snapshot).ValueOrDie()];
block_(result, nil);
} else {
block_(nil, MakeNSError(maybe_snapshot.status()));
}
}
private:
FIRQuerySnapshotBlock block_;
};
return absl::make_unique<Converter>(block);
}
- (Filter)parseFieldFilter:(FSTUnaryFilter *)unaryFilter {
auto describer = [&unaryFilter] {
return MakeString(NSStringFromClass([unaryFilter.value class]));
};
Message<google_firestore_v1_Value> fieldValue =
[self parsedQueryValue:unaryFilter.value
allowArrays:unaryFilter.unaryOp == FieldFilter::Operator::In ||
unaryFilter.unaryOp == FieldFilter::Operator::NotIn];
Filter parsedFieldFilter = _query.ParseFieldFilter(
unaryFilter.fieldPath.internalValue, unaryFilter.unaryOp, std::move(fieldValue), describer);
return parsedFieldFilter;
}
- (Filter)parseCompositeFilter:(FSTCompositeFilter *)compositeFilter {
std::vector<Filter> filters;
for (FIRFilter *filter in compositeFilter.filters) {
Filter parsedFilter = [self parseFilter:filter];
if (!parsedFilter.IsEmpty()) {
filters.push_back(std::move(parsedFilter));
}
}
// For composite filters containing 1 filter, return the only filter.
// For example: AND(FieldFilter1) == FieldFilter1
if (filters.size() == 1u) {
return filters[0];
}
Filter parsedCompositeFilter =
CompositeFilter::Create(std::move(filters), compositeFilter.compOp);
return parsedCompositeFilter;
}
- (Filter)parseFilter:(FIRFilter *)filter {
if ([filter isKindOfClass:[FSTUnaryFilter class]]) {
FSTUnaryFilter *unaryFilter = (FSTUnaryFilter *)filter;
return [self parseFieldFilter:unaryFilter];
} else if ([filter isKindOfClass:[FSTCompositeFilter class]]) {
FSTCompositeFilter *compositeFilter = (FSTCompositeFilter *)filter;
return [self parseCompositeFilter:compositeFilter];
} else {
ThrowInvalidArgument("Parsing only supports Filter.UnaryFilter and Filter.CompositeFilter.");
}
}
/**
* Create a Bound from a query given the document.
*
* Note that the Bound will always include the key of the document and the position will be
* unambiguous.
*
* Will throw if the document does not contain all fields of the order by of
* the query or if any of the fields in the order by are an uncommitted server
* timestamp.
*/
- (Bound)boundFromSnapshot:(FIRDocumentSnapshot *)snapshot isInclusive:(BOOL)isInclusive {
if (![snapshot exists]) {
ThrowInvalidArgument("Invalid query. You are trying to start or end a query using a document "
"that doesn't exist.");
}
const Document &document = *snapshot.internalDocument;
const DatabaseId &databaseID = self.firestore.databaseID;
const std::vector<OrderBy> &order_bys = self.query.normalized_order_bys();
SharedMessage<google_firestore_v1_ArrayValue> components{{}};
components->values_count = CheckedSize(order_bys.size());
components->values = MakeArray<google_firestore_v1_Value>(components->values_count);
// Because people expect to continue/end a query at the exact document provided, we need to
// use the implicit sort order rather than the explicit sort order, because it's guaranteed to
// contain the document key. That way the position becomes unambiguous and the query
// continues/ends exactly at the provided document. Without the key (by using the explicit sort
// orders), multiple documents could match the position, yielding duplicate results.
for (size_t i = 0; i < order_bys.size(); ++i) {
if (order_bys[i].field() == FieldPath::KeyFieldPath()) {
components->values[i] = *RefValue(databaseID, document->key()).release();
} else {
absl::optional<google_firestore_v1_Value> value = document->field(order_bys[i].field());
if (value) {
if (IsServerTimestamp(*value)) {
ThrowInvalidArgument(
"Invalid query. You are trying to start or end a query using a document for which "
"the field '%s' is an uncommitted server timestamp. (Since the value of this field "
"is unknown, you cannot start/end a query with it.)",
order_bys[i].field().CanonicalString());
} else {
components->values[i] = *DeepClone(*value).release();
}
} else {
ThrowInvalidArgument(
"Invalid query. You are trying to start or end a query using a document for which the "
"field '%s' (used as the order by) does not exist.",
order_bys[i].field().CanonicalString());
}
}
}
return Bound::FromValue(std::move(components), isInclusive);
}
/** Converts a list of field values to an Bound. */
- (Bound)boundFromFieldValues:(NSArray<id> *)fieldValues isInclusive:(BOOL)isInclusive {
// Use explicit sort order because it has to match the query the user made
const std::vector<OrderBy> &explicitSortOrders = self.query.explicit_order_bys();
if (fieldValues.count > explicitSortOrders.size()) {
ThrowInvalidArgument("Invalid query. You are trying to start or end a query using more values "
"than were specified in the order by.");
}
SharedMessage<google_firestore_v1_ArrayValue> components{{}};
components->values_count = CheckedSize(fieldValues.count);
components->values = MakeArray<google_firestore_v1_Value>(components->values_count);
for (NSUInteger idx = 0, max = fieldValues.count; idx < max; ++idx) {
id rawValue = fieldValues[idx];
const OrderBy &sortOrder = explicitSortOrders[idx];
Message<google_firestore_v1_Value> fieldValue{[self parsedQueryValue:rawValue]};
if (sortOrder.field().IsKeyFieldPath()) {
if (GetTypeOrder(*fieldValue) != TypeOrder::kString) {
ThrowInvalidArgument("Invalid query. Expected a string for the document ID.");
}
std::string documentID = MakeString(fieldValue->string_value);
if (!self.query.IsCollectionGroupQuery() && absl::StrContains(documentID, "/")) {
ThrowInvalidArgument("Invalid query. When querying a collection and ordering by document "
"ID, you must pass a plain document ID, but '%s' contains a slash.",
documentID);
}
ResourcePath path = self.query.path().Append(ResourcePath::FromString(documentID));
if (!DocumentKey::IsDocumentKey(path)) {
ThrowInvalidArgument("Invalid query. When querying a collection group and ordering by "
"document ID, you must pass a value that results in a valid document "
"path, but '%s' is not because it contains an odd number of segments.",
path.CanonicalString());
}
DocumentKey key{path};
components->values[idx] = *RefValue(self.firestore.databaseID, key).release();
} else {
components->values[idx] = *fieldValue.release();
}
}
return Bound::FromValue(std::move(components), isInclusive);
}
@end
@implementation FIRQuery (Internal)
- (const core::Query &)query {
return _query.query();
}
- (const api::Query &)apiQuery {
return _query;
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,44 @@
/*
* 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 "FIRQuerySnapshot.h"
#include <memory>
#include "Firestore/core/src/api/api_fwd.h"
#include "Firestore/core/src/core/core_fwd.h"
@class FIRFirestore;
@class FIRSnapshotMetadata;
namespace api = firebase::firestore::api;
namespace core = firebase::firestore::core;
NS_ASSUME_NONNULL_BEGIN
/** Internal FIRQuerySnapshot API we don't want exposed in our public header files. */
@interface FIRQuerySnapshot (/* Init */)
- (instancetype)initWithSnapshot:(api::QuerySnapshot &&)snapshot NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithFirestore:(std::shared_ptr<api::Firestore>)firestore
originalQuery:(core::Query)query
snapshot:(core::ViewSnapshot &&)snapshot
metadata:(api::SnapshotMetadata)metadata;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,143 @@
/*
* 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.
*/
#include <utility>
#import "Firestore/Source/API/FIRQuerySnapshot+Internal.h"
#import "FIRSnapshotMetadata.h"
#import "Firestore/Source/API/FIRDocumentChange+Internal.h"
#import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h"
#import "Firestore/Source/API/FIRFirestore+Internal.h"
#import "Firestore/Source/API/FIRQuery+Internal.h"
#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h"
#include "Firestore/core/src/api/query_core.h"
#include "Firestore/core/src/api/query_snapshot.h"
#include "Firestore/core/src/core/view_snapshot.h"
#include "Firestore/core/src/model/document_set.h"
#include "Firestore/core/src/util/delayed_constructor.h"
#include "Firestore/core/src/util/exception.h"
using firebase::firestore::api::DocumentChange;
using firebase::firestore::api::DocumentSnapshot;
using firebase::firestore::api::Firestore;
using firebase::firestore::api::QuerySnapshot;
using firebase::firestore::api::SnapshotMetadata;
using firebase::firestore::core::ViewSnapshot;
using firebase::firestore::util::DelayedConstructor;
NS_ASSUME_NONNULL_BEGIN
@implementation FIRQuerySnapshot {
DelayedConstructor<QuerySnapshot> _snapshot;
FIRSnapshotMetadata *_cached_metadata;
// Cached value of the documents property.
NSArray<FIRQueryDocumentSnapshot *> *_documents;
// Cached value of the documentChanges property.
NSArray<FIRDocumentChange *> *_documentChanges;
BOOL _documentChangesIncludeMetadataChanges;
}
- (instancetype)initWithSnapshot:(QuerySnapshot &&)snapshot {
if (self = [super init]) {
_snapshot.Init(std::move(snapshot));
}
return self;
}
- (instancetype)initWithFirestore:(std::shared_ptr<Firestore>)firestore
originalQuery:(core::Query)query
snapshot:(ViewSnapshot &&)snapshot
metadata:(SnapshotMetadata)metadata {
QuerySnapshot wrapped(firestore, std::move(query), std::move(snapshot), std::move(metadata));
return [self initWithSnapshot:std::move(wrapped)];
}
// NSObject Methods
- (BOOL)isEqual:(nullable id)other {
if (![other isKindOfClass:[FIRQuerySnapshot class]]) return NO;
FIRQuerySnapshot *otherSnapshot = other;
return *_snapshot == *(otherSnapshot->_snapshot);
}
- (NSUInteger)hash {
return _snapshot->Hash();
}
- (FIRQuery *)query {
return [[FIRQuery alloc] initWithQuery:_snapshot->query()];
}
- (FIRSnapshotMetadata *)metadata {
if (!_cached_metadata) {
_cached_metadata = [[FIRSnapshotMetadata alloc] initWithMetadata:_snapshot->metadata()];
}
return _cached_metadata;
}
@dynamic empty;
- (BOOL)isEmpty {
return _snapshot->empty();
}
// This property is exposed as an NSInteger instead of an NSUInteger since (as of Xcode 8.1)
// Swift bridges NSUInteger as UInt, and we want to avoid forcing Swift users to cast their ints
// where we can. See cr/146959032 for additional context.
- (NSInteger)count {
return static_cast<NSInteger>(_snapshot->size());
}
- (NSArray<FIRQueryDocumentSnapshot *> *)documents {
if (!_documents) {
NSMutableArray<FIRQueryDocumentSnapshot *> *result = [NSMutableArray array];
_snapshot->ForEachDocument([&result](DocumentSnapshot snapshot) {
[result addObject:[[FIRQueryDocumentSnapshot alloc] initWithSnapshot:std::move(snapshot)]];
});
_documents = result;
}
return _documents;
}
- (NSArray<FIRDocumentChange *> *)documentChanges {
return [self documentChangesWithIncludeMetadataChanges:NO];
}
- (NSArray<FIRDocumentChange *> *)documentChangesWithIncludeMetadataChanges:
(BOOL)includeMetadataChanges {
if (!_documentChanges || _documentChangesIncludeMetadataChanges != includeMetadataChanges) {
NSMutableArray *documentChanges = [NSMutableArray array];
_snapshot->ForEachChange(
static_cast<bool>(includeMetadataChanges), [&documentChanges](DocumentChange change) {
[documentChanges
addObject:[[FIRDocumentChange alloc] initWithDocumentChange:std::move(change)]];
});
_documentChanges = documentChanges;
_documentChangesIncludeMetadataChanges = includeMetadataChanges;
}
return _documentChanges;
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2024 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 "FIRSnapshotListenOptions.h"
#import <Foundation/Foundation.h>
#include <cstdint>
#include <string>
NS_ASSUME_NONNULL_BEGIN
@implementation FIRSnapshotListenOptions
- (instancetype)initPrivate:(FIRListenSource)source
includeMetadataChanges:(BOOL)includeMetadataChanges {
self = [self init];
if (self) {
_source = source;
_includeMetadataChanges = includeMetadataChanges;
}
return self;
}
- (instancetype)init {
self = [super init];
if (self) {
_source = FIRListenSourceDefault;
_includeMetadataChanges = NO;
}
return self;
}
- (FIRSnapshotListenOptions *)optionsWithIncludeMetadataChanges:(BOOL)includeMetadataChanges {
FIRSnapshotListenOptions *newOptions =
[[FIRSnapshotListenOptions alloc] initPrivate:self.source
includeMetadataChanges:includeMetadataChanges];
return newOptions;
}
- (FIRSnapshotListenOptions *)optionsWithSource:(FIRListenSource)source {
FIRSnapshotListenOptions *newOptions =
[[FIRSnapshotListenOptions alloc] initPrivate:source
includeMetadataChanges:self.includeMetadataChanges];
return newOptions;
}
/// This function forces the linker to include `FIRSnapshotListenOptions`.
/// See `+[FIRFirestore notCalled]`.
void FSTIncludeFIRSnapshotListenOptions(void) {
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -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 "FIRSnapshotMetadata.h"
#import <Foundation/Foundation.h>
#include "Firestore/core/src/api/snapshot_metadata.h"
namespace api = firebase::firestore::api;
NS_ASSUME_NONNULL_BEGIN
@interface FIRSnapshotMetadata (/* Init */)
- (instancetype)initWithMetadata:(api::SnapshotMetadata)metadata NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithPendingWrites:(bool)pendingWrites fromCache:(bool)fromCache;
@end
NS_ASSUME_NONNULL_END

View File

@@ -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 "FIRSnapshotMetadata.h"
#include <utility>
#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h"
#include "Firestore/core/src/api/snapshot_metadata.h"
NS_ASSUME_NONNULL_BEGIN
@implementation FIRSnapshotMetadata {
api::SnapshotMetadata _metadata;
}
- (instancetype)initWithMetadata:(api::SnapshotMetadata)metadata {
if (self = [super init]) {
_metadata = std::move(metadata);
}
return self;
}
- (instancetype)initWithPendingWrites:(bool)pendingWrites fromCache:(bool)fromCache {
api::SnapshotMetadata wrapped(pendingWrites, fromCache);
return [self initWithMetadata:std::move(wrapped)];
}
// NSObject Methods
- (BOOL)isEqual:(nullable id)other {
if (other == self) return YES;
if (![other isKindOfClass:[FIRSnapshotMetadata class]]) return NO;
FIRSnapshotMetadata *otherMetadata = other;
return _metadata == otherMetadata->_metadata;
}
- (NSUInteger)hash {
return _metadata.Hash();
}
- (BOOL)hasPendingWrites {
return _metadata.pending_writes();
}
- (BOOL)isFromCache {
return _metadata.from_cache();
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,35 @@
/*
* 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 "FIRTimestamp.h"
NS_ASSUME_NONNULL_BEGIN
/** Internal FIRTimestamp API we don't want exposed in our public header files. */
@interface FIRTimestamp (Internal)
/**
* Converts the given date to an ISO 8601 timestamp string, useful for rendering in JSON.
*
* ISO 8601 dates times in UTC look like this: "1912-04-14T23:40:00.000000000Z".
*
* @see http://www.ecma-international.org/ecma-262/6.0/#sec-date-time-string-format
*/
- (NSString *)ISO8601String;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,152 @@
/*
* 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 "Firestore/Source/API/FIRTimestamp+Internal.h"
NS_ASSUME_NONNULL_BEGIN
static const int kNanosPerSecond = 1000000000;
@implementation FIRTimestamp (Internal)
#pragma mark - Internal public methods
- (NSString *)ISO8601String {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss";
formatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
NSDate *secondsDate = [NSDate dateWithTimeIntervalSince1970:self.seconds];
NSString *secondsString = [formatter stringFromDate:secondsDate];
if (secondsString.length != 19) {
[NSException raise:@"Invalid ISO string" format:@"Invalid ISO string: %@", secondsString];
}
NSString *nanosString = [NSString stringWithFormat:@"%09d", self.nanoseconds];
return [NSString stringWithFormat:@"%@.%@Z", secondsString, nanosString];
}
@end
@implementation FIRTimestamp
#pragma mark - Constructors
+ (instancetype)timestampWithDate:(NSDate *)date {
double secondsDouble;
double fraction = modf(date.timeIntervalSince1970, &secondsDouble);
// GCP Timestamps always have non-negative nanos.
if (fraction < 0) {
fraction += 1.0;
secondsDouble -= 1.0;
}
int64_t seconds = (int64_t)secondsDouble;
int32_t nanos = (int32_t)(fraction * kNanosPerSecond);
return [[FIRTimestamp alloc] initWithSeconds:seconds nanoseconds:nanos];
}
+ (instancetype)timestampWithSeconds:(int64_t)seconds nanoseconds:(int32_t)nanoseconds {
return [[FIRTimestamp alloc] initWithSeconds:seconds nanoseconds:nanoseconds];
}
+ (instancetype)timestamp {
return [FIRTimestamp timestampWithDate:[NSDate date]];
}
- (instancetype)initWithSeconds:(int64_t)seconds nanoseconds:(int32_t)nanoseconds {
self = [super init];
if (self) {
if (nanoseconds < 0) {
[NSException raise:@"Invalid timestamp"
format:@"Timestamp nanoseconds out of range: %d", nanoseconds];
}
if (nanoseconds >= 1e9) {
[NSException raise:@"Invalid timestamp"
format:@"Timestamp nanoseconds out of range: %d", nanoseconds];
}
// Midnight at the beginning of 1/1/1 is the earliest timestamp supported.
if (seconds < -62135596800L) {
[NSException raise:@"Invalid timestamp"
format:@"Timestamp seconds out of range: %lld", seconds];
}
// This will break in the year 10,000.
if (seconds >= 253402300800L) {
[NSException raise:@"Invalid timestamp"
format:@"Timestamp seconds out of range: %lld", seconds];
}
_seconds = seconds;
_nanoseconds = nanoseconds;
}
return self;
}
#pragma mark - NSObject methods
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[FIRTimestamp class]]) {
return NO;
}
return [self isEqualToTimestamp:(FIRTimestamp *)object];
}
- (NSUInteger)hash {
return (NSUInteger)((self.seconds >> 32) ^ self.seconds ^ self.nanoseconds);
}
- (NSString *)description {
return [NSString stringWithFormat:@"<FIRTimestamp: seconds=%lld nanoseconds=%d>", self.seconds,
self.nanoseconds];
}
/** Implements NSCopying without actually copying because timestamps are immutable. */
- (id)copyWithZone:(__unused NSZone *_Nullable)zone {
return self;
}
#pragma mark - Public methods
- (NSDate *)dateValue {
NSTimeInterval interval = (NSTimeInterval)self.seconds + ((NSTimeInterval)self.nanoseconds) / 1e9;
return [NSDate dateWithTimeIntervalSince1970:interval];
}
- (NSComparisonResult)compare:(FIRTimestamp *)other {
if (self.seconds < other.seconds) {
return NSOrderedAscending;
} else if (self.seconds > other.seconds) {
return NSOrderedDescending;
}
if (self.nanoseconds < other.nanoseconds) {
return NSOrderedAscending;
} else if (self.nanoseconds > other.nanoseconds) {
return NSOrderedDescending;
}
return NSOrderedSame;
}
#pragma mark - Private methods
- (BOOL)isEqualToTimestamp:(FIRTimestamp *)other {
return [self compare:other] == NSOrderedSame;
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -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 "FIRTransaction.h"
#include <memory>
#include "Firestore/core/src/core/transaction.h"
@class FIRFirestore;
namespace core = firebase::firestore::core;
NS_ASSUME_NONNULL_BEGIN
@interface FIRTransaction (Internal)
+ (instancetype)transactionWithInternalTransaction:(std::shared_ptr<core::Transaction>)transaction
firestore:(FIRFirestore *)firestore;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,185 @@
/*
* 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 "FIRTransaction.h"
#include <memory>
#include <utility>
#include <vector>
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
#import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h"
#import "Firestore/Source/API/FIRFirestore+Internal.h"
#import "Firestore/Source/API/FIRTransaction+Internal.h"
#import "Firestore/Source/API/FSTUserDataReader.h"
#include "Firestore/core/src/core/transaction.h"
#include "Firestore/core/src/core/user_data.h"
#include "Firestore/core/src/model/document.h"
#include "Firestore/core/src/util/error_apple.h"
#include "Firestore/core/src/util/exception.h"
#include "Firestore/core/src/util/hard_assert.h"
#include "Firestore/core/src/util/status.h"
#include "Firestore/core/src/util/statusor.h"
using firebase::firestore::core::ParsedSetData;
using firebase::firestore::core::ParsedUpdateData;
using firebase::firestore::core::Transaction;
using firebase::firestore::model::Document;
using firebase::firestore::util::MakeNSError;
using firebase::firestore::util::StatusOr;
using firebase::firestore::util::ThrowInvalidArgument;
NS_ASSUME_NONNULL_BEGIN
#pragma mark - FIRTransaction
@interface FIRTransaction ()
- (instancetype)initWithTransaction:(std::shared_ptr<Transaction>)transaction
firestore:(FIRFirestore *)firestore NS_DESIGNATED_INITIALIZER;
@property(nonatomic, strong, readonly) FIRFirestore *firestore;
@end
@implementation FIRTransaction (Internal)
+ (instancetype)transactionWithInternalTransaction:(std::shared_ptr<Transaction>)transaction
firestore:(FIRFirestore *)firestore {
return [[FIRTransaction alloc] initWithTransaction:std::move(transaction) firestore:firestore];
}
@end
@implementation FIRTransaction {
std::shared_ptr<Transaction> _internalTransaction;
}
- (instancetype)initWithTransaction:(std::shared_ptr<Transaction>)transaction
firestore:(FIRFirestore *)firestore {
self = [super init];
if (self) {
_internalTransaction = std::move(transaction);
_firestore = firestore;
}
return self;
}
- (FIRTransaction *)setData:(NSDictionary<NSString *, id> *)data
forDocument:(FIRDocumentReference *)document {
return [self setData:data forDocument:document merge:NO];
}
- (FIRTransaction *)setData:(NSDictionary<NSString *, id> *)data
forDocument:(FIRDocumentReference *)document
merge:(BOOL)merge {
[self validateReference:document];
ParsedSetData parsed = merge ? [self.firestore.dataReader parsedMergeData:data fieldMask:nil]
: [self.firestore.dataReader parsedSetData:data];
_internalTransaction->Set(document.key, std::move(parsed));
return self;
}
- (FIRTransaction *)setData:(NSDictionary<NSString *, id> *)data
forDocument:(FIRDocumentReference *)document
mergeFields:(NSArray<id> *)mergeFields {
[self validateReference:document];
ParsedSetData parsed = [self.firestore.dataReader parsedMergeData:data fieldMask:mergeFields];
_internalTransaction->Set(document.key, std::move(parsed));
return self;
}
- (FIRTransaction *)updateData:(NSDictionary<id, id> *)fields
forDocument:(FIRDocumentReference *)document {
[self validateReference:document];
ParsedUpdateData parsed = [self.firestore.dataReader parsedUpdateData:fields];
_internalTransaction->Update(document.key, std::move(parsed));
return self;
}
- (FIRTransaction *)deleteDocument:(FIRDocumentReference *)document {
[self validateReference:document];
_internalTransaction->Delete(document.key);
return self;
}
- (void)getDocument:(FIRDocumentReference *)document
completion:(void (^)(FIRDocumentSnapshot *_Nullable document,
NSError *_Nullable error))completion {
[self validateReference:document];
_internalTransaction->Lookup(
{document.key},
[self, document, completion](const StatusOr<std::vector<Document>> &maybe_documents) {
if (!maybe_documents.ok()) {
completion(nil, MakeNSError(maybe_documents.status()));
return;
}
const auto &documents = maybe_documents.ValueOrDie();
HARD_ASSERT(documents.size() == 1, "Mismatch in docs returned from document lookup.");
const Document &internalDoc = documents.front();
if (internalDoc->is_found_document()) {
FIRDocumentSnapshot *doc =
[[FIRDocumentSnapshot alloc] initWithFirestore:self.firestore
documentKey:internalDoc->key()
document:internalDoc
fromCache:false
hasPendingWrites:false];
completion(doc, nil);
} else if (internalDoc->is_no_document()) {
FIRDocumentSnapshot *doc = [[FIRDocumentSnapshot alloc] initWithFirestore:self.firestore
documentKey:document.key
document:absl::nullopt
fromCache:false
hasPendingWrites:false];
completion(doc, nil);
} else {
HARD_FAIL("BatchGetDocumentsRequest returned unexpected document type: %s",
internalDoc.ToString());
}
});
}
- (FIRDocumentSnapshot *_Nullable)getDocument:(FIRDocumentReference *)document
error:(NSError *__autoreleasing *)error {
[self validateReference:document];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block FIRDocumentSnapshot *result;
// We have to explicitly assign the innerError into a local to cause it to retain correctly.
__block NSError *outerError = nil;
[self getDocument:document
completion:^(FIRDocumentSnapshot *_Nullable snapshot, NSError *_Nullable innerError) {
result = snapshot;
outerError = innerError;
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (error) {
*error = outerError;
}
return result;
}
- (void)validateReference:(FIRDocumentReference *)reference {
if (reference.firestore != self.firestore) {
ThrowInvalidArgument("Provided document reference is from a different Cloud Firestore "
"instance.");
}
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2022 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 "FIRTransactionOptions.h"
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface FIRTransactionOptions (Internal)
+ (int)defaultMaxAttempts;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2022 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 "FIRTransactionOptions.h"
#import "FIRTransactionOptions+Internal.h"
#import <Foundation/Foundation.h>
#include <cstdint>
#include <string>
#include "Firestore/core/src/api/firestore.h"
#include "Firestore/core/src/util/exception.h"
NS_ASSUME_NONNULL_BEGIN
using firebase::firestore::api::kDefaultTransactionMaxAttempts;
using firebase::firestore::util::ThrowInvalidArgument;
@implementation FIRTransactionOptions
- (instancetype)init {
if (self = [super init]) {
_maxAttempts = [[self class] defaultMaxAttempts];
}
return self;
}
+ (int)defaultMaxAttempts {
return kDefaultTransactionMaxAttempts;
}
- (BOOL)isEqual:(id)other {
if (self == other) {
return YES;
} else if (![other isKindOfClass:[FIRTransactionOptions class]]) {
return NO;
}
FIRTransactionOptions *otherOptions = (FIRTransactionOptions *)other;
return self.maxAttempts == otherOptions.maxAttempts;
}
- (NSUInteger)hash {
return _maxAttempts * 31;
}
- (id)copyWithZone:(__unused NSZone *_Nullable)zone {
FIRTransactionOptions *copy = [[FIRTransactionOptions alloc] init];
copy.maxAttempts = self.maxAttempts;
return copy;
}
- (void)setMaxAttempts:(NSInteger)maxAttempts {
if (maxAttempts <= 0 || maxAttempts > INT32_MAX) {
ThrowInvalidArgument("Invalid maxAttempts: %s", std::to_string(maxAttempts));
}
_maxAttempts = maxAttempts;
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -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 "FIRWriteBatch.h"
#import <Foundation/Foundation.h>
#include "Firestore/core/src/api/write_batch.h"
@class FSTUserDataReader;
namespace api = firebase::firestore::api;
NS_ASSUME_NONNULL_BEGIN
@interface FIRWriteBatch (Internal)
+ (instancetype)writeBatchWithDataReader:(FSTUserDataReader *)dataReader
writeBatch:(api::WriteBatch &&)writeBatch;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,116 @@
/*
* 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 "Firestore/Source/API/FIRWriteBatch+Internal.h"
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
#import "Firestore/Source/API/FSTUserDataReader.h"
#include "Firestore/core/src/api/write_batch.h"
#include "Firestore/core/src/core/user_data.h"
#include "Firestore/core/src/util/delayed_constructor.h"
#include "Firestore/core/src/util/error_apple.h"
using firebase::firestore::core::ParsedSetData;
using firebase::firestore::core::ParsedUpdateData;
using firebase::firestore::util::DelayedConstructor;
using firebase::firestore::util::MakeCallback;
NS_ASSUME_NONNULL_BEGIN
#pragma mark - FIRWriteBatch
@interface FIRWriteBatch ()
- (instancetype)initWithDataReader:(FSTUserDataReader *)dataReader
writeBatch:(api::WriteBatch &&)writeBatch NS_DESIGNATED_INITIALIZER;
@property(nonatomic, strong, readonly) FSTUserDataReader *dataReader;
@end
@implementation FIRWriteBatch (Internal)
+ (instancetype)writeBatchWithDataReader:(FSTUserDataReader *)dataReader
writeBatch:(api::WriteBatch &&)writeBatch {
return [[FIRWriteBatch alloc] initWithDataReader:dataReader writeBatch:std::move(writeBatch)];
}
@end
@implementation FIRWriteBatch {
DelayedConstructor<api::WriteBatch> _writeBatch;
}
- (instancetype)initWithDataReader:(FSTUserDataReader *)dataReader
writeBatch:(api::WriteBatch &&)writeBatch {
self = [super init];
if (self) {
_dataReader = dataReader;
_writeBatch.Init(std::move(writeBatch));
}
return self;
}
- (FIRWriteBatch *)setData:(NSDictionary<NSString *, id> *)data
forDocument:(FIRDocumentReference *)document {
return [self setData:data forDocument:document merge:NO];
}
- (FIRWriteBatch *)setData:(NSDictionary<NSString *, id> *)data
forDocument:(FIRDocumentReference *)document
merge:(BOOL)merge {
ParsedSetData parsed = merge ? [self.dataReader parsedMergeData:data fieldMask:nil]
: [self.dataReader parsedSetData:data];
_writeBatch->SetData(document.internalReference, std::move(parsed));
return self;
}
- (FIRWriteBatch *)setData:(NSDictionary<NSString *, id> *)data
forDocument:(FIRDocumentReference *)document
mergeFields:(NSArray<id> *)mergeFields {
ParsedSetData parsed = [self.dataReader parsedMergeData:data fieldMask:mergeFields];
_writeBatch->SetData(document.internalReference, std::move(parsed));
return self;
}
- (FIRWriteBatch *)updateData:(NSDictionary<id, id> *)fields
forDocument:(FIRDocumentReference *)document {
ParsedUpdateData parsed = [self.dataReader parsedUpdateData:fields];
_writeBatch->UpdateData(document.internalReference, std::move(parsed));
return self;
}
- (FIRWriteBatch *)deleteDocument:(FIRDocumentReference *)document {
_writeBatch->DeleteData(document.internalReference);
return self;
}
- (void)commit {
[self commitWithCompletion:nil];
}
- (void)commitWithCompletion:(nullable void (^)(NSError *_Nullable error))completion {
_writeBatch->Commit(MakeCallback(completion));
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,61 @@
/*
* 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>
#import "Firestore/Source/API/FIRFirestore+Internal.h"
@class FIRApp;
@class FIRFirestore;
NS_ASSUME_NONNULL_BEGIN
/// Provides and creates instances of Firestore based on a specific key. Used in the interop
/// registration process to keep track of instances for `FIRApp` instances.
@protocol FSTFirestoreMultiDBProvider
/// Cached instances of Firestore objects.
@property(nonatomic, strong) NSMutableDictionary<NSString *, FIRFirestore *> *instances;
/// Default method for retrieving a Firestore instance, or creating one if it doesn't exist.
- (FIRFirestore *)firestoreForDatabase:(NSString *)database;
@end
/// A concrete implementation for FSTInstanceProvider to create Firestore instances and register
/// with Core's component system.
@interface FSTFirestoreComponent
: NSObject <FSTFirestoreInstanceRegistry, FSTFirestoreMultiDBProvider>
/// The FIRApp that instances will be set up with.
@property(nonatomic, weak, readonly) FIRApp *app;
/// Cached instances of Firestore objects.
@property(nonatomic, strong) NSMutableDictionary<NSString *, FIRFirestore *> *instances;
/// Default method for retrieving a Firestore instance, or creating one if it doesn't exist.
- (FIRFirestore *)firestoreForDatabase:(NSString *)database;
- (void)removeInstanceWithDatabase:(NSString *)database;
/// Default initializer.
- (instancetype)initWithApp:(FIRApp *)app NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,185 @@
/*
* 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 "Firestore/Source/API/FSTFirestoreComponent.h"
#import <FirebaseAppCheckInterop/FirebaseAppCheckInterop.h>
#include <memory>
#include <string>
#include <utility>
#import "FirebaseAuth/Interop/FIRAuthInterop.h"
#import "FirebaseCore/Extension/FIRAppInternal.h"
#import "FirebaseCore/Extension/FIRComponent.h"
#import "FirebaseCore/Extension/FIRComponentContainer.h"
#import "FirebaseCore/Extension/FIRComponentType.h"
#import "FirebaseCore/Extension/FIRDependency.h"
#import "FirebaseCore/Extension/FIRLibrary.h"
#import "FirebaseCore/Extension/FIROptionsInternal.h"
#import "Firestore/Source/API/FIRFirestore+Internal.h"
#include "Firestore/core/include/firebase/firestore/firestore_version.h"
#include "Firestore/core/src/api/firestore.h"
#include "Firestore/core/src/credentials/credentials_provider.h"
#include "Firestore/core/src/credentials/firebase_app_check_credentials_provider_apple.h"
#include "Firestore/core/src/credentials/firebase_auth_credentials_provider_apple.h"
#include "Firestore/core/src/remote/firebase_metadata_provider.h"
#include "Firestore/core/src/remote/firebase_metadata_provider_apple.h"
#include "Firestore/core/src/util/async_queue.h"
#include "Firestore/core/src/util/exception.h"
#include "Firestore/core/src/util/executor.h"
#include "Firestore/core/src/util/hard_assert.h"
#include "absl/memory/memory.h"
using firebase::firestore::credentials::FirebaseAppCheckCredentialsProvider;
using firebase::firestore::credentials::FirebaseAuthCredentialsProvider;
using firebase::firestore::remote::FirebaseMetadataProviderApple;
using firebase::firestore::util::AsyncQueue;
using firebase::firestore::util::Executor;
using firebase::firestore::util::MakeString;
using firebase::firestore::util::ThrowInvalidArgument;
NS_ASSUME_NONNULL_BEGIN
@interface FSTFirestoreComponent () <FIRComponentLifecycleMaintainer, FIRLibrary>
@end
@implementation FSTFirestoreComponent
// Explicitly @synthesize because instances is part of the FSTInstanceProvider protocol.
@synthesize instances = _instances;
#pragma mark - Initialization
- (instancetype)initWithApp:(FIRApp *)app {
self = [super init];
if (self) {
_instances = [[NSMutableDictionary alloc] init];
HARD_ASSERT(app, "Cannot initialize Firestore with a nil FIRApp.");
_app = app;
}
return self;
}
- (NSString *)keyForDatabase:(NSString *)database {
return [NSString stringWithFormat:@"%@|%@", self.app.name, database];
}
#pragma mark - FSTInstanceProvider Conformance
- (FIRFirestore *)firestoreForDatabase:(NSString *)database {
if (!database) {
ThrowInvalidArgument("Database identifier may not be nil.");
}
NSString *projectID = self.app.options.projectID;
if (!projectID) {
ThrowInvalidArgument("FIROptions.projectID must be set to a valid project ID.");
}
NSString *key = [self keyForDatabase:database];
// Get the component from the container.
@synchronized(self.instances) {
FIRFirestore *firestore = _instances[key];
if (!firestore) {
std::string queue_name{"com.google.firebase.firestore"};
if (!self.app.isDefaultApp) {
absl::StrAppend(&queue_name, ".", MakeString(self.app.name));
}
auto executor = Executor::CreateSerial(queue_name.c_str());
auto workerQueue = AsyncQueue::Create(std::move(executor));
id<FIRAuthInterop> auth = FIR_COMPONENT(FIRAuthInterop, self.app.container);
id<FIRAppCheckInterop> app_check = FIR_COMPONENT(FIRAppCheckInterop, self.app.container);
auto authCredentialsProvider =
std::make_shared<FirebaseAuthCredentialsProvider>(self.app, auth);
auto appCheckCredentialsProvider =
std::make_shared<FirebaseAppCheckCredentialsProvider>(self.app, app_check);
auto firebaseMetadataProvider = absl::make_unique<FirebaseMetadataProviderApple>(self.app);
model::DatabaseId databaseID{MakeString(projectID), MakeString(database)};
std::string persistenceKey = MakeString(self.app.name);
firestore = [[FIRFirestore alloc] initWithDatabaseID:std::move(databaseID)
persistenceKey:std::move(persistenceKey)
authCredentialsProvider:std::move(authCredentialsProvider)
appCheckCredentialsProvider:std::move(appCheckCredentialsProvider)
workerQueue:std::move(workerQueue)
firebaseMetadataProvider:std::move(firebaseMetadataProvider)
firebaseApp:self.app
instanceRegistry:self];
_instances[key] = firestore;
}
return firestore;
}
}
- (void)removeInstanceWithDatabase:(NSString *)database {
@synchronized(_instances) {
NSString *key = [self keyForDatabase:database];
[_instances removeObjectForKey:key];
}
}
#pragma mark - FIRComponentLifecycleMaintainer
- (void)appWillBeDeleted:(__unused FIRApp *)app {
NSDictionary<NSString *, FIRFirestore *> *instances;
@synchronized(_instances) {
instances = [_instances copy];
[_instances removeAllObjects];
}
for (NSString *key in instances) {
[instances[key] terminateInternalWithCompletion:nil];
}
}
#pragma mark - Object Lifecycle
+ (void)load {
[FIRApp registerInternalLibrary:(Class<FIRLibrary>)self withName:@"fire-fst"];
}
#pragma mark - Interoperability
+ (NSArray<FIRComponent *> *)componentsToRegister {
FIRDependency *auth = [FIRDependency dependencyWithProtocol:@protocol(FIRAuthInterop)
isRequired:NO];
FIRComponent *firestoreProvider = [FIRComponent
componentWithProtocol:@protocol(FSTFirestoreMultiDBProvider)
instantiationTiming:FIRInstantiationTimingLazy
dependencies:@[ auth ]
creationBlock:^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
FSTFirestoreComponent *multiDBComponent =
[[FSTFirestoreComponent alloc] initWithApp:container.app];
*isCacheable = YES;
return multiDBComponent;
}];
return @[ firestoreProvider ];
}
@end
/// This function forces the linker to include `FSTFirestoreComponent`. See `+[FIRFirestore
/// notCalled]`.
void FSTIncludeFSTFirestoreComponent(void) {
}
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2021 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>
#include <vector>
#include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
#include "Firestore/core/src/core/core_fwd.h"
#include "Firestore/core/src/model/database_id.h"
#include "Firestore/core/src/model/model_fwd.h"
#include "Firestore/core/src/nanopb/message.h"
@class FIRTimestamp;
namespace core = firebase::firestore::core;
namespace model = firebase::firestore::model;
namespace nanopb = firebase::firestore::nanopb;
NS_ASSUME_NONNULL_BEGIN
/**
* An internal representation of FIRDocumentReference, representing a key in a specific database.
* This is necessary because keys assume a database from context (usually the current one).
* FSTDocumentKeyReference binds a key to a specific databaseID.
*
* TODO(b/64160088): Make DocumentKey aware of the specific databaseID it is tied to.
*/
@interface FSTDocumentKeyReference : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithKey:(model::DocumentKey)key
databaseID:(model::DatabaseId)databaseID NS_DESIGNATED_INITIALIZER;
- (const model::DocumentKey &)key;
@property(nonatomic, assign, readonly) const model::DatabaseId &databaseID;
@end
/**
* An interface that allows arbitrary pre-converting of user data.
*
* Returns the converted value (can return back the input to act as a no-op).
*/
typedef id _Nullable (^FSTPreConverterBlock)(id _Nullable);
/**
* Helper for parsing raw user input (provided via the API) into internal model classes.
*/
@interface FSTUserDataReader : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithDatabaseID:(model::DatabaseId)databaseID
preConverter:(FSTPreConverterBlock)preConverter NS_DESIGNATED_INITIALIZER;
/** Parse document data from a non-merge setData call.*/
- (core::ParsedSetData)parsedSetData:(id)input;
/** Parse document data from a setData call with `merge:YES`. */
- (core::ParsedSetData)parsedMergeData:(id)input fieldMask:(nullable NSArray<id> *)fieldMask;
/** Parse update data from an updateData call. */
- (core::ParsedUpdateData)parsedUpdateData:(id)input;
/** Parse a "query value" (e.g. value in a where filter or a value in a cursor bound). */
- (nanopb::Message<firebase::firestore::google_firestore_v1_Value>)parsedQueryValue:(id)input;
/**
* Parse a "query value" (e.g. value in a where filter or a value in a cursor bound).
*
* @param allowArrays Whether the query value is an array that may directly contain additional
* arrays (e.g.) the operand of an `in` query).
*/
- (nanopb::Message<firebase::firestore::google_firestore_v1_Value>)parsedQueryValue:(id)input
allowArrays:
(bool)allowArrays;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,628 @@
/*
* 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 "Firestore/Source/API/FSTUserDataReader.h"
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#import "FIRGeoPoint.h"
#import "FIRTimestamp.h"
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
#import "Firestore/Source/API/FIRFieldPath+Internal.h"
#import "Firestore/Source/API/FIRFieldValue+Internal.h"
#import "Firestore/Source/API/FIRFirestore+Internal.h"
#import "Firestore/Source/API/FIRGeoPoint+Internal.h"
#import "Firestore/Source/API/converters.h"
#import "Firestore/core/include/firebase/firestore/geo_point.h"
#include "Firestore/core/src/core/user_data.h"
#include "Firestore/core/src/model/database_id.h"
#include "Firestore/core/src/model/document_key.h"
#include "Firestore/core/src/model/field_mask.h"
#include "Firestore/core/src/model/field_path.h"
#include "Firestore/core/src/model/field_transform.h"
#include "Firestore/core/src/model/object_value.h"
#include "Firestore/core/src/model/precondition.h"
#include "Firestore/core/src/model/resource_path.h"
#include "Firestore/core/src/model/transform_operation.h"
#include "Firestore/core/src/model/value_util.h"
#include "Firestore/core/src/nanopb/nanopb_util.h"
#include "Firestore/core/src/nanopb/reader.h"
#include "Firestore/core/src/remote/serializer.h"
#include "Firestore/core/src/timestamp_internal.h"
#include "Firestore/core/src/util/exception.h"
#include "Firestore/core/src/util/hard_assert.h"
#include "Firestore/core/src/util/read_context.h"
#include "Firestore/core/src/util/string_apple.h"
#include "absl/memory/memory.h"
#include "absl/strings/match.h"
#include "absl/types/optional.h"
namespace nanopb = firebase::firestore::nanopb;
using firebase::Timestamp;
using firebase::TimestampInternal;
using firebase::firestore::GeoPoint;
using firebase::firestore::google_firestore_v1_ArrayValue;
using firebase::firestore::google_firestore_v1_MapValue;
using firebase::firestore::google_firestore_v1_MapValue_FieldsEntry;
using firebase::firestore::google_firestore_v1_Value;
using firebase::firestore::google_protobuf_NullValue_NULL_VALUE;
using firebase::firestore::google_protobuf_Timestamp;
using firebase::firestore::google_type_LatLng;
using firebase::firestore::core::ParseAccumulator;
using firebase::firestore::core::ParseContext;
using firebase::firestore::core::ParsedSetData;
using firebase::firestore::core::ParsedUpdateData;
using firebase::firestore::core::UserDataSource;
using firebase::firestore::model::ArrayTransform;
using firebase::firestore::model::DatabaseId;
using firebase::firestore::model::DeepClone;
using firebase::firestore::model::DocumentKey;
using firebase::firestore::model::FieldMask;
using firebase::firestore::model::FieldPath;
using firebase::firestore::model::FieldTransform;
using firebase::firestore::model::NullValue;
using firebase::firestore::model::NumericIncrementTransform;
using firebase::firestore::model::ObjectValue;
using firebase::firestore::model::ResourcePath;
using firebase::firestore::model::ServerTimestampTransform;
using firebase::firestore::model::TransformOperation;
using firebase::firestore::nanopb::CheckedSize;
using firebase::firestore::nanopb::Message;
using firebase::firestore::remote::Serializer;
using firebase::firestore::util::MakeString;
using firebase::firestore::util::ReadContext;
using firebase::firestore::util::ThrowInvalidArgument;
using nanopb::StringReader;
NS_ASSUME_NONNULL_BEGIN
#pragma mark - FSTDocumentKeyReference
@implementation FSTDocumentKeyReference {
DocumentKey _key;
DatabaseId _databaseID;
}
- (instancetype)initWithKey:(DocumentKey)key databaseID:(DatabaseId)databaseID {
self = [super init];
if (self) {
_key = std::move(key);
_databaseID = std::move(databaseID);
}
return self;
}
- (const model::DocumentKey &)key {
return _key;
}
- (const model::DatabaseId &)databaseID {
return _databaseID;
}
@end
#pragma mark - FSTUserDataReader
@interface FSTUserDataReader ()
@property(strong, nonatomic, readonly) FSTPreConverterBlock preConverter;
@end
@implementation FSTUserDataReader {
DatabaseId _databaseID;
}
- (instancetype)initWithDatabaseID:(DatabaseId)databaseID
preConverter:(FSTPreConverterBlock)preConverter {
self = [super init];
if (self) {
_databaseID = std::move(databaseID);
_preConverter = preConverter;
}
return self;
}
- (ParsedSetData)parsedSetData:(id)input {
// NOTE: The public API is typed as NSDictionary but we type 'input' as 'id' since we can't trust
// Obj-C to verify the type for us.
if (![input isKindOfClass:[NSDictionary class]]) {
ThrowInvalidArgument("Data to be written must be an NSDictionary.");
}
ParseAccumulator accumulator{UserDataSource::Set};
auto updateData = [self parseData:input context:accumulator.RootContext()];
HARD_ASSERT(updateData.has_value(), "Parsed data should not be nil.");
return std::move(accumulator).SetData(ObjectValue{std::move(*updateData)});
}
- (ParsedSetData)parsedMergeData:(id)input fieldMask:(nullable NSArray<id> *)fieldMask {
// NOTE: The public API is typed as NSDictionary but we type 'input' as 'id' since we can't trust
// Obj-C to verify the type for us.
if (![input isKindOfClass:[NSDictionary class]]) {
ThrowInvalidArgument("Data to be written must be an NSDictionary.");
}
ParseAccumulator accumulator{UserDataSource::MergeSet};
auto updateData = [self parseData:input context:accumulator.RootContext()];
HARD_ASSERT(updateData.has_value(), "Parsed data should not be nil.");
ObjectValue updateObject{std::move(*updateData)};
if (fieldMask) {
std::set<FieldPath> validatedFieldPaths;
for (id fieldPath in fieldMask) {
FieldPath path;
if ([fieldPath isKindOfClass:[NSString class]]) {
path = FieldPath::FromDotSeparatedString(MakeString(fieldPath));
} else if ([fieldPath isKindOfClass:[FIRFieldPath class]]) {
path = static_cast<FIRFieldPath *>(fieldPath).internalValue;
} else {
ThrowInvalidArgument("All elements in mergeFields: must be NSStrings or FIRFieldPaths.");
}
// Verify that all elements specified in the field mask are part of the parsed context.
if (!accumulator.Contains(path)) {
ThrowInvalidArgument(
"Field '%s' is specified in your field mask but missing from your input data.",
path.CanonicalString());
}
validatedFieldPaths.insert(path);
}
return std::move(accumulator)
.MergeData(std::move(updateObject), FieldMask{std::move(validatedFieldPaths)});
} else {
return std::move(accumulator).MergeData(std::move(updateObject));
}
}
- (ParsedUpdateData)parsedUpdateData:(id)input {
// NOTE: The public API is typed as NSDictionary but we type 'input' as 'id' since we can't trust
// Obj-C to verify the type for us.
if (![input isKindOfClass:[NSDictionary class]]) {
ThrowInvalidArgument("Data to be written must be an NSDictionary.");
}
NSDictionary *dict = input;
ParseAccumulator accumulator{UserDataSource::Update};
__block ParseContext context = accumulator.RootContext();
__block ObjectValue updateData;
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *) {
FieldPath path;
if ([key isKindOfClass:[NSString class]]) {
path = FieldPath::FromDotSeparatedString(MakeString(key));
} else if ([key isKindOfClass:[FIRFieldPath class]]) {
path = ((FIRFieldPath *)key).internalValue;
} else {
ThrowInvalidArgument("Dictionary keys in updateData: must be NSStrings or FIRFieldPaths.");
}
value = self.preConverter(value);
if ([value isKindOfClass:[FSTDeleteFieldValue class]]) {
// Add it to the field mask, but don't add anything to updateData.
context.AddToFieldMask(std::move(path));
} else {
auto parsedValue = [self parseData:value context:context.ChildContext(path)];
if (parsedValue) {
context.AddToFieldMask(path);
updateData.Set(path, std::move(*parsedValue));
}
}
}];
return std::move(accumulator).UpdateData(std::move(updateData));
}
- (Message<google_firestore_v1_Value>)parsedQueryValue:(id)input {
return [self parsedQueryValue:input allowArrays:false];
}
- (Message<google_firestore_v1_Value>)parsedQueryValue:(id)input allowArrays:(bool)allowArrays {
ParseAccumulator accumulator{allowArrays ? UserDataSource::ArrayArgument
: UserDataSource::Argument};
auto parsed = [self parseData:input context:accumulator.RootContext()];
HARD_ASSERT(parsed, "Parsed data should not be nil.");
HARD_ASSERT(accumulator.field_transforms().empty(),
"Field transforms should have been disallowed.");
return std::move(*parsed);
}
/**
* Internal helper for parsing user data.
*
* @param input Data to be parsed.
* @param context A context object representing the current path being parsed, the source of the
* data being parsed, etc.
*
* @return The parsed value, or nil if the value was a FieldValue sentinel that should not be
* included in the resulting parsed data.
*/
- (absl::optional<Message<google_firestore_v1_Value>>)parseData:(id)input
context:(ParseContext &&)context {
input = self.preConverter(input);
if ([input isKindOfClass:[NSDictionary class]]) {
return [self parseDictionary:(NSDictionary *)input context:std::move(context)];
} else if ([input isKindOfClass:[FIRFieldValue class]]) {
// FieldValues usually parse into transforms (except FieldValue.delete()) in which case we
// do not want to include this field in our parsed data (as doing so will overwrite the field
// directly prior to the transform trying to transform it). So we don't call appendToFieldMask
// and we return nil as our parsing result.
[self parseSentinelFieldValue:(FIRFieldValue *)input context:std::move(context)];
return absl::nullopt;
} else {
// If context path is unset we are already inside an array and we don't support field mask paths
// more granular than the top-level array.
if (context.path()) {
context.AddToFieldMask(*context.path());
}
if ([input isKindOfClass:[NSArray class]]) {
// TODO(b/34871131): Include the path containing the array in the error message.
// In the case of IN queries, the parsed data is an array (representing the set of values to
// be included for the IN query) that may directly contain additional arrays (each
// representing an individual field value), so we disable this validation.
if (context.array_element() && context.data_source() != UserDataSource::ArrayArgument) {
ThrowInvalidArgument("Nested arrays are not supported");
}
return [self parseArray:(NSArray *)input context:std::move(context)];
} else {
return [self parseScalarValue:input context:std::move(context)];
}
}
}
- (Message<google_firestore_v1_Value>)parseDictionary:(NSDictionary<NSString *, id> *)dict
context:(ParseContext &&)context {
__block Message<google_firestore_v1_Value> result;
result->which_value_type = google_firestore_v1_Value_map_value_tag;
result->map_value = {};
if (dict.count == 0) {
const FieldPath *path = context.path();
if (path && !path->empty()) {
context.AddToFieldMask(*path);
}
} else {
// Compute the final size of the fields array, which contains an entry for
// all fields that are not FieldValue sentinels
__block pb_size_t count = 0;
[dict enumerateKeysAndObjectsUsingBlock:^(NSString *, id value, BOOL *) {
if (![value isKindOfClass:[FIRFieldValue class]]) {
++count;
}
}];
result->map_value.fields_count = count;
result->map_value.fields = nanopb::MakeArray<google_firestore_v1_MapValue_FieldsEntry>(count);
__block pb_size_t index = 0;
[dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *) {
auto parsedValue = [self parseData:value context:context.ChildContext(MakeString(key))];
if (parsedValue) {
result->map_value.fields[index].key = nanopb::MakeBytesArray(MakeString(key));
result->map_value.fields[index].value = *parsedValue->release();
++index;
}
}];
}
return std::move(result);
}
- (Message<google_firestore_v1_Value>)parseArray:(NSArray<id> *)array
context:(ParseContext &&)context {
__block Message<google_firestore_v1_Value> result;
result->which_value_type = google_firestore_v1_Value_array_value_tag;
result->array_value.values_count = CheckedSize([array count]);
result->array_value.values =
nanopb::MakeArray<google_firestore_v1_Value>(result->array_value.values_count);
[array enumerateObjectsUsingBlock:^(id entry, NSUInteger idx, BOOL *) {
auto parsedEntry = [self parseData:entry context:context.ChildContext(idx)];
if (!parsedEntry) {
// Just include nulls in the array for fields being replaced with a sentinel.
parsedEntry.emplace(DeepClone(NullValue()));
}
result->array_value.values[idx] = *parsedEntry->release();
}];
return std::move(result);
}
/**
* "Parses" the provided FIRFieldValue, adding any necessary transforms to
* context.fieldTransforms.
*/
- (void)parseSentinelFieldValue:(FIRFieldValue *)fieldValue context:(ParseContext &&)context {
// Sentinels are only supported with writes, and not within arrays.
if (!context.write()) {
ThrowInvalidArgument("%s can only be used with updateData() and setData()%s",
fieldValue.methodName, context.FieldDescription());
}
if (!context.path()) {
ThrowInvalidArgument("%s is not currently supported inside arrays", fieldValue.methodName);
}
if ([fieldValue isKindOfClass:[FSTDeleteFieldValue class]]) {
if (context.data_source() == UserDataSource::MergeSet) {
// No transform to add for a delete, but we need to add it to our fieldMask so it gets
// deleted.
context.AddToFieldMask(*context.path());
} else if (context.data_source() == UserDataSource::Update) {
HARD_ASSERT(!context.path()->empty(),
"FieldValue.delete() at the top level should have already been handled.");
ThrowInvalidArgument("FieldValue.delete() can only appear at the top level of your "
"update data%s",
context.FieldDescription());
} else {
// We shouldn't encounter delete sentinels for queries or non-merge setData calls.
ThrowInvalidArgument(
"FieldValue.delete() can only be used with updateData() and setData() with merge:true%s",
context.FieldDescription());
}
} else if ([fieldValue isKindOfClass:[FSTServerTimestampFieldValue class]]) {
context.AddToFieldTransforms(*context.path(), ServerTimestampTransform());
} else if ([fieldValue isKindOfClass:[FSTArrayUnionFieldValue class]]) {
auto parsedElements =
[self parseArrayTransformElements:((FSTArrayUnionFieldValue *)fieldValue).elements];
ArrayTransform arrayUnion(TransformOperation::Type::ArrayUnion, std::move(parsedElements));
context.AddToFieldTransforms(*context.path(), std::move(arrayUnion));
} else if ([fieldValue isKindOfClass:[FSTArrayRemoveFieldValue class]]) {
auto parsedElements =
[self parseArrayTransformElements:((FSTArrayRemoveFieldValue *)fieldValue).elements];
ArrayTransform arrayRemove(TransformOperation::Type::ArrayRemove, std::move(parsedElements));
context.AddToFieldTransforms(*context.path(), std::move(arrayRemove));
} else if ([fieldValue isKindOfClass:[FSTNumericIncrementFieldValue class]]) {
auto *numericIncrementFieldValue = (FSTNumericIncrementFieldValue *)fieldValue;
auto operand = [self parsedQueryValue:numericIncrementFieldValue.operand];
NumericIncrementTransform numeric_increment(std::move(operand));
context.AddToFieldTransforms(*context.path(), std::move(numeric_increment));
} else {
HARD_FAIL("Unknown FIRFieldValue type: %s", NSStringFromClass([fieldValue class]));
}
}
/**
* Helper to parse a scalar value (i.e. not an NSDictionary, NSArray, or FIRFieldValue).
*
* Note that it handles all NSNumber values that are encodable as int64_t or doubles
* (depending on the underlying type of the NSNumber). Unsigned integer values are handled though
* any value outside what is representable by int64_t (a signed 64-bit value) will throw an
* exception.
*
* @return The parsed value.
*/
- (Message<google_firestore_v1_Value>)parseScalarValue:(nullable id)input
context:(ParseContext &&)context {
if (!input || [input isMemberOfClass:[NSNull class]]) {
return DeepClone(NullValue());
} else if ([input isKindOfClass:[NSNumber class]]) {
// Recover the underlying type of the number, using the method described here:
// http://stackoverflow.com/questions/2518761/get-type-of-nsnumber
const char *cType = [input objCType];
// Type Encoding values taken from
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/
// Articles/ocrtTypeEncodings.html
switch (cType[0]) {
case 'q':
return [self encodeInteger:[input longLongValue]];
case 'i': // Falls through.
case 's': // Falls through.
case 'l': // Falls through.
case 'I': // Falls through.
case 'S':
// Coerce integer values that aren't long long. Allow unsigned integer types that are
// guaranteed small enough to skip a length check.
return [self encodeInteger:[input longLongValue]];
case 'L': // Falls through.
case 'Q':
// Unsigned integers that could be too large. Note that the 'L' (long) case is handled here
// because when compiled for LP64, unsigned long is 64 bits and could overflow int64_t.
{
unsigned long long extended = [input unsignedLongLongValue];
if (extended > LLONG_MAX) {
ThrowInvalidArgument("NSNumber (%s) is too large%s", [input unsignedLongLongValue],
context.FieldDescription());
} else {
return [self encodeInteger:static_cast<int64_t>(extended)];
}
}
case 'f':
return [self encodeDouble:[input doubleValue]];
case 'd':
// Double values are already the right type, so just reuse the existing boxed double.
//
// Note that NSNumber already performs NaN normalization to a single shared instance
// so there's no need to treat NaN specially here.
return [self encodeDouble:[input doubleValue]];
case 'B': // Falls through.
case 'c': // Falls through.
case 'C':
// Boolean values are weird.
//
// On arm64, objCType of a BOOL-valued NSNumber will be "c", even though @encode(BOOL)
// returns "B". "c" is the same as @encode(signed char). Unfortunately this means that
// legitimate usage of signed chars is impossible, but this should be rare.
//
// Additionally, for consistency, map unsigned chars to bools in the same way.
return [self encodeBoolean:[input boolValue]];
default:
// All documented codes should be handled above, so this shouldn't happen.
HARD_FAIL("Unknown NSNumber objCType %s on %s", cType, input);
}
} else if ([input isKindOfClass:[NSString class]]) {
std::string inputString = MakeString(input);
return [self encodeStringValue:inputString];
} else if ([input isKindOfClass:[NSDate class]]) {
NSDate *inputDate = input;
return [self encodeTimestampValue:api::MakeTimestamp(inputDate)];
} else if ([input isKindOfClass:[FIRTimestamp class]]) {
FIRTimestamp *inputTimestamp = input;
Timestamp timestamp = TimestampInternal::Truncate(api::MakeTimestamp(inputTimestamp));
return [self encodeTimestampValue:timestamp];
} else if ([input isKindOfClass:[FIRGeoPoint class]]) {
return [self encodeGeoPoint:api::MakeGeoPoint(input)];
} else if ([input isKindOfClass:[NSData class]]) {
NSData *inputData = input;
return [self encodeBlob:(nanopb::MakeByteString(inputData))];
} else if ([input isKindOfClass:[FSTDocumentKeyReference class]]) {
FSTDocumentKeyReference *reference = input;
if (reference.databaseID != _databaseID) {
const DatabaseId &other = reference.databaseID;
ThrowInvalidArgument(
"Document Reference is for database %s/%s but should be for database %s/%s%s",
other.project_id(), other.database_id(), _databaseID.project_id(),
_databaseID.database_id(), context.FieldDescription());
}
return [self encodeReference:_databaseID key:reference.key];
} else {
ThrowInvalidArgument("Unsupported type: %s%s", NSStringFromClass([input class]),
context.FieldDescription());
}
}
- (Message<google_firestore_v1_Value>)encodeBoolean:(bool)value {
Message<google_firestore_v1_Value> result;
result->which_value_type = google_firestore_v1_Value_boolean_value_tag;
result->boolean_value = value;
return result;
}
- (Message<google_firestore_v1_Value>)encodeInteger:(int64_t)value {
Message<google_firestore_v1_Value> result;
result->which_value_type = google_firestore_v1_Value_integer_value_tag;
result->integer_value = value;
return result;
}
- (Message<google_firestore_v1_Value>)encodeDouble:(double)value {
Message<google_firestore_v1_Value> result;
result->which_value_type = google_firestore_v1_Value_double_value_tag;
result->double_value = value;
return result;
}
- (Message<google_firestore_v1_Value>)encodeTimestampValue:(Timestamp)value {
Message<google_firestore_v1_Value> result;
result->which_value_type = google_firestore_v1_Value_timestamp_value_tag;
result->timestamp_value.seconds = value.seconds();
result->timestamp_value.nanos = value.nanoseconds();
return result;
}
- (Message<google_firestore_v1_Value>)encodeStringValue:(const std::string &)value {
Message<google_firestore_v1_Value> result;
result->which_value_type = google_firestore_v1_Value_string_value_tag;
result->string_value = nanopb::MakeBytesArray(value);
return result;
}
- (Message<google_firestore_v1_Value>)encodeBlob:(const nanopb::ByteString &)value {
Message<google_firestore_v1_Value> result;
result->which_value_type = google_firestore_v1_Value_bytes_value_tag;
// Copy the blob so that pb_release can do the right thing.
result->bytes_value = nanopb::CopyBytesArray(value.get());
return result;
}
- (Message<google_firestore_v1_Value>)encodeReference:(const DatabaseId &)databaseId
key:(const DocumentKey &)key {
HARD_ASSERT(_databaseID == databaseId, "Database %s cannot encode reference from %s",
_databaseID.ToString(), databaseId.ToString());
std::string referenceName = ResourcePath({"projects", databaseId.project_id(), "databases",
databaseId.database_id(), "documents", key.ToString()})
.CanonicalString();
Message<google_firestore_v1_Value> result;
result->which_value_type = google_firestore_v1_Value_reference_value_tag;
result->reference_value = nanopb::MakeBytesArray(referenceName);
return result;
}
- (Message<google_firestore_v1_Value>)encodeGeoPoint:(const GeoPoint &)value {
Message<google_firestore_v1_Value> result;
result->which_value_type = google_firestore_v1_Value_geo_point_value_tag;
result->geo_point_value.latitude = value.latitude();
result->geo_point_value.longitude = value.longitude();
return result;
}
- (Message<google_firestore_v1_ArrayValue>)parseArrayTransformElements:(NSArray<id> *)elements {
ParseAccumulator accumulator{UserDataSource::Argument};
Message<google_firestore_v1_ArrayValue> array_value;
array_value->values_count = CheckedSize(elements.count);
array_value->values = nanopb::MakeArray<google_firestore_v1_Value>(array_value->values_count);
for (NSUInteger i = 0; i < elements.count; i++) {
id element = elements[i];
// Although array transforms are used with writes, the actual elements being unioned or removed
// are not considered writes since they cannot contain any FieldValue sentinels, etc.
ParseContext context = accumulator.RootContext();
auto parsedElement = [self parseData:element context:context.ChildContext(i)];
HARD_ASSERT(parsedElement && accumulator.field_transforms().empty(),
"Failed to properly parse array transform element: %s", element);
array_value->values[i] = *parsedElement->release();
}
return array_value;
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2021 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>
#include <memory>
#include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
#include "Firestore/Source/API/FIRDocumentSnapshot+Internal.h"
#include "Firestore/core/src/api/api_fwd.h"
namespace api = firebase::firestore::api;
/**
* Converts Firestore's internal types to the API types that we expose to the
* user.
*/
@interface FSTUserDataWriter : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithFirestore:(std::shared_ptr<api::Firestore>)firestore
serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior;
- (id)convertedValue:(const firebase::firestore::google_firestore_v1_Value &)value;
@end

View File

@@ -0,0 +1,169 @@
// Copyright 2021 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.
#include "Firestore/Source/API/FSTUserDataWriter.h"
#import <Foundation/Foundation.h>
#include <string>
#include <utility>
#include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
#include "Firestore/Source/API/FIRDocumentReference+Internal.h"
#include "Firestore/Source/API/converters.h"
#include "Firestore/core/include/firebase/firestore/geo_point.h"
#include "Firestore/core/include/firebase/firestore/timestamp.h"
#include "Firestore/core/src/api/firestore.h"
#include "Firestore/core/src/model/database_id.h"
#include "Firestore/core/src/model/document_key.h"
#include "Firestore/core/src/model/server_timestamp_util.h"
#include "Firestore/core/src/model/value_util.h"
#include "Firestore/core/src/nanopb/nanopb_util.h"
#include "Firestore/core/src/util/hard_assert.h"
#include "Firestore/core/src/util/log.h"
#include "Firestore/core/src/util/string_apple.h"
@class FIRTimestamp;
namespace api = firebase::firestore::api;
namespace model = firebase::firestore::model;
namespace nanopb = firebase::firestore::nanopb;
using api::MakeFIRDocumentReference;
using api::MakeFIRGeoPoint;
using api::MakeFIRTimestamp;
using firebase::firestore::GeoPoint;
using firebase::firestore::google_firestore_v1_ArrayValue;
using firebase::firestore::google_firestore_v1_MapValue;
using firebase::firestore::google_firestore_v1_Value;
using firebase::firestore::google_protobuf_Timestamp;
using firebase::firestore::util::MakeNSString;
using model::DatabaseId;
using model::DocumentKey;
using model::GetLocalWriteTime;
using model::GetPreviousValue;
using model::GetTypeOrder;
using model::TypeOrder;
using nanopb::MakeBytesArray;
using nanopb::MakeByteString;
using nanopb::MakeNSData;
using nanopb::MakeString;
using nanopb::MakeStringView;
NS_ASSUME_NONNULL_BEGIN
@implementation FSTUserDataWriter {
std::shared_ptr<api::Firestore> _firestore;
FIRServerTimestampBehavior _serverTimestampBehavior;
}
- (instancetype)initWithFirestore:(std::shared_ptr<api::Firestore>)firestore
serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior {
self = [super init];
if (self) {
_firestore = std::move(firestore);
_serverTimestampBehavior = serverTimestampBehavior;
}
return self;
}
- (id)convertedValue:(const google_firestore_v1_Value &)value {
switch (GetTypeOrder(value)) {
case TypeOrder::kMap:
return [self convertedObject:value.map_value];
case TypeOrder::kArray:
return [self convertedArray:value.array_value];
case TypeOrder::kReference:
return [self convertedReference:value];
case TypeOrder::kTimestamp:
return [self convertedTimestamp:value.timestamp_value];
case TypeOrder::kServerTimestamp:
return [self convertedServerTimestamp:value];
case TypeOrder::kNull:
return [NSNull null];
case TypeOrder::kBoolean:
return value.boolean_value ? @YES : @NO;
case TypeOrder::kNumber:
return value.which_value_type == google_firestore_v1_Value_integer_value_tag
? @(value.integer_value)
: @(value.double_value);
case TypeOrder::kString:
return MakeNSString(MakeStringView(value.string_value));
case TypeOrder::kBlob:
return MakeNSData(value.bytes_value);
case TypeOrder::kGeoPoint:
return MakeFIRGeoPoint(
GeoPoint(value.geo_point_value.latitude, value.geo_point_value.longitude));
case TypeOrder::kMaxValue:
// It is not possible for users to construct a kMaxValue manually.
break;
}
UNREACHABLE();
}
- (NSDictionary<NSString *, id> *)convertedObject:(const google_firestore_v1_MapValue &)mapValue {
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (pb_size_t i = 0; i < mapValue.fields_count; ++i) {
absl::string_view key = MakeStringView(mapValue.fields[i].key);
const google_firestore_v1_Value &value = mapValue.fields[i].value;
result[MakeNSString(key)] = [self convertedValue:value];
}
return result;
}
- (NSArray<id> *)convertedArray:(const google_firestore_v1_ArrayValue &)arrayValue {
NSMutableArray *result = [NSMutableArray arrayWithCapacity:arrayValue.values_count];
for (pb_size_t i = 0; i < arrayValue.values_count; ++i) {
[result addObject:[self convertedValue:arrayValue.values[i]]];
}
return result;
}
- (id)convertedServerTimestamp:(const google_firestore_v1_Value &)serverTimestampValue {
switch (_serverTimestampBehavior) {
case FIRServerTimestampBehavior::FIRServerTimestampBehaviorNone:
return [NSNull null];
case FIRServerTimestampBehavior::FIRServerTimestampBehaviorEstimate:
return [self convertedTimestamp:GetLocalWriteTime(serverTimestampValue)];
case FIRServerTimestampBehavior::FIRServerTimestampBehaviorPrevious: {
auto previous_value = GetPreviousValue(serverTimestampValue);
return previous_value ? [self convertedValue:*previous_value] : [NSNull null];
}
}
UNREACHABLE();
}
- (FIRTimestamp *)convertedTimestamp:(const google_protobuf_Timestamp &)value {
return MakeFIRTimestamp(firebase::Timestamp{value.seconds, value.nanos});
}
- (FIRDocumentReference *)convertedReference:(const google_firestore_v1_Value &)value {
std::string ref = MakeString(value.reference_value);
DatabaseId databaseID = DatabaseId::FromName(ref);
DocumentKey key = DocumentKey::FromName(ref);
if (databaseID != _firestore->database_id()) {
LOG_WARN("Document reference is for a different database (%s/%s) which "
"is not supported. It will be treated as a reference within the current database "
"(%s/%s) instead.",
databaseID.project_id(), databaseID.database_id(), databaseID.project_id(),
databaseID.database_id());
}
return MakeFIRDocumentReference(key, _firestore);
}
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,75 @@
/*
* 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.
*/
#ifndef FIRESTORE_SOURCE_API_CONVERTERS_H_
#define FIRESTORE_SOURCE_API_CONVERTERS_H_
#if !defined(__OBJC__)
#error "This header only supports Objective-C++"
#endif // !defined(__OBJC__)
#import <Foundation/Foundation.h>
#include <memory>
#import "FIRSnapshotListenOptions.h"
#import "Firestore/core/src/api/listen_source.h"
@class FIRGeoPoint;
@class FIRTimestamp;
@class FIRDocumentReference;
NS_ASSUME_NONNULL_BEGIN
namespace firebase {
class Timestamp;
namespace firestore {
class GeoPoint;
namespace model {
class DocumentKey;
}
namespace api {
class Firestore;
/** Converts a user-supplied FIRGeoPoint to the equivalent C++ GeoPoint. */
GeoPoint MakeGeoPoint(FIRGeoPoint* geo_point);
/** Converts a C++ GeoPoint to the equivalent Objective-C FIRGeoPoint. */
FIRGeoPoint* MakeFIRGeoPoint(const GeoPoint& geo_point);
/** Converts a user-supplied FIRTimestamp to the equivalent C++ Timestamp. */
Timestamp MakeTimestamp(FIRTimestamp* timestamp);
Timestamp MakeTimestamp(NSDate* date);
FIRTimestamp* MakeFIRTimestamp(const Timestamp& timestamp);
FIRDocumentReference* MakeFIRDocumentReference(const model::DocumentKey& document_key,
std::shared_ptr<Firestore> firestore);
ListenSource MakeListenSource(const FIRListenSource& source);
} // namespace api
} // namespace firestore
} // namespace firebase
NS_ASSUME_NONNULL_END
#endif // FIRESTORE_SOURCE_API_CONVERTERS_H_

View File

@@ -0,0 +1,80 @@
/*
* 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.
*/
#include "Firestore/Source/API/converters.h"
#include <utility>
#import "FIRGeoPoint.h"
#import "FIRTimestamp.h"
#include "Firestore/Source/API/FIRDocumentReference+Internal.h"
#include "Firestore/core/include/firebase/firestore/geo_point.h"
#include "Firestore/core/include/firebase/firestore/timestamp.h"
#include "Firestore/core/src/api/firestore.h"
#import "Firestore/core/src/api/listen_source.h"
#include "Firestore/core/src/model/document_key.h"
NS_ASSUME_NONNULL_BEGIN
namespace firebase {
namespace firestore {
namespace api {
GeoPoint MakeGeoPoint(FIRGeoPoint* geo_point) {
return GeoPoint(geo_point.latitude, geo_point.longitude);
}
FIRGeoPoint* MakeFIRGeoPoint(const GeoPoint& geo_point) {
return [[FIRGeoPoint alloc] initWithLatitude:geo_point.latitude()
longitude:geo_point.longitude()];
}
Timestamp MakeTimestamp(FIRTimestamp* timestamp) {
return Timestamp(timestamp.seconds, timestamp.nanoseconds);
}
Timestamp MakeTimestamp(NSDate* date) {
FIRTimestamp* timestamp = [FIRTimestamp timestampWithDate:date];
return MakeTimestamp(timestamp);
}
FIRTimestamp* MakeFIRTimestamp(const Timestamp& timestamp) {
return [[FIRTimestamp alloc] initWithSeconds:timestamp.seconds()
nanoseconds:timestamp.nanoseconds()];
}
FIRDocumentReference* MakeFIRDocumentReference(const model::DocumentKey& key,
std::shared_ptr<Firestore> firestore) {
return [[FIRDocumentReference alloc] initWithKey:key firestore:std::move(firestore)];
}
ListenSource MakeListenSource(const FIRListenSource& source) {
switch (source) {
case FIRListenSourceDefault:
return ListenSource::Default;
case FIRListenSourceCache:
return ListenSource::Cache;
default:
return ListenSource::Default;
}
}
} // namespace api
} // namespace firestore
} // namespace firebase
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2023 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
@class FIRFieldPath;
/**
* Represents an aggregation that can be performed by Firestore.
*/
NS_SWIFT_NAME(AggregateField)
@interface FIRAggregateField : NSObject
/** :nodoc: */
- (instancetype)init NS_UNAVAILABLE;
/**
* Create an `AggregateField` object that can be used to compute the count of
* documents in the result set of a query.
*
* The result of a count operation will always be a 64-bit integer value.
*
* @return `AggregateField` object that can be used to compute the count of
* documents in the result set of a query.
*/
+ (instancetype)aggregateFieldForCount NS_SWIFT_NAME(count());
/**
* Create an `AggregateField` object that can be used to compute the sum of
* a specified field over a range of documents in the result set of a query.
*
* The result of a sum operation will always be a 64-bit integer value, a double, or NaN.
*
* - Summing over zero documents or fields will result in 0L.
* - Summing over NaN will result in a double value representing NaN.
* - A sum that overflows the maximum representable 64-bit integer value will result in a double
* return value. This may result in lost precision of the result.
* - A sum that overflows the maximum representable double value will result in a double return
* value representing infinity.
*
* @param field Specifies the field to sum across the result set.
* @return `AggregateField` object that can be used to compute the sum of
* a specified field over a range of documents in the result set of a query.
*/
+ (instancetype)aggregateFieldForSumOfField:(NSString *)field NS_SWIFT_NAME(sum(_:));
/**
* Create an `AggregateField` object that can be used to compute the sum of
* a specified field over a range of documents in the result set of a query.
*
* The result of a sum operation will always be a 64-bit integer value, a double, or NaN.
*
* - Summing over zero documents or fields will result in 0L.
* - Summing over NaN will result in a double value representing NaN.
* - A sum that overflows the maximum representable 64-bit integer value will result in a double
* return value. This may result in lost precision of the result.
* - A sum that overflows the maximum representable double value will result in a double return
* value representing infinity.
*
* @param fieldPath Specifies the field to sum across the result set.
* @return `AggregateField` object that can be used to compute the sum of
* a specified field over a range of documents in the result set of a query.
*/
+ (instancetype)aggregateFieldForSumOfFieldPath:(FIRFieldPath *)fieldPath NS_SWIFT_NAME(sum(_:));
/**
* Create an `AggregateField` object that can be used to compute the average of
* a specified field over a range of documents in the result set of a query.
*
* The result of an average operation will always be a double or NaN.
*
* - Averaging over zero documents or fields will result in a double value representing NaN.
* - Averaging over NaN will result in a double value representing NaN.
*
* @param field Specifies the field to average across the result set.
* @return `AggregateField` object that can be used to compute the average of
* a specified field over a range of documents in the result set of a query.
*/
+ (instancetype)aggregateFieldForAverageOfField:(NSString *)field NS_SWIFT_NAME(average(_:));
/**
* Create an `AggregateField` object that can be used to compute the average of
* a specified field over a range of documents in the result set of a query.
*
* The result of an average operation will always be a double or NaN.
*
* - Averaging over zero documents or fields will result in a double value representing NaN.
* - Averaging over NaN will result in a double value representing NaN.
*
* @param fieldPath Specifies the field to average across the result set.
* @return `AggregateField` object that can be used to compute the average of
* a specified field over a range of documents in the result set of a query.
*/
+ (instancetype)aggregateFieldForAverageOfFieldPath:(FIRFieldPath *)fieldPath
NS_SWIFT_NAME(average(_:));
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2022 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>
#import "FIRAggregateSource.h"
NS_ASSUME_NONNULL_BEGIN
@class FIRQuery;
@class FIRAggregateQuerySnapshot;
/**
* A query that calculates aggregations over an underlying query.
*/
NS_SWIFT_NAME(AggregateQuery)
@interface FIRAggregateQuery : NSObject
/** :nodoc: */
- (instancetype)init __attribute__((unavailable("FIRAggregateQuery cannot be created directly.")));
/** The query whose aggregations will be calculated by this object. */
@property(nonatomic, readonly) FIRQuery *query;
/**
* Executes this query.
*
* @param source The source from which to acquire the aggregate results.
* @param completion a block to execute once the results have been successfully read.
* snapshot will be `nil` only if error is `non-nil`.
*/
- (void)aggregationWithSource:(FIRAggregateSource)source
completion:(void (^)(FIRAggregateQuerySnapshot *_Nullable snapshot,
NSError *_Nullable error))completion
NS_SWIFT_NAME(getAggregation(source:completion:));
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2022 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
@class FIRAggregateQuery;
@class FIRAggregateField;
/**
* The results of executing an `AggregateQuery`.
*/
NS_SWIFT_NAME(AggregateQuerySnapshot)
@interface FIRAggregateQuerySnapshot : NSObject
/** :nodoc: */
- (instancetype)init
__attribute__((unavailable("FIRAggregateQuerySnapshot cannot be created directly.")));
/** The query that was executed to produce this result. */
@property(nonatomic, readonly) FIRAggregateQuery* query;
/** The number of documents in the result set of the underlying query. */
@property(nonatomic, readonly) NSNumber* count;
/**
* Gets the aggregate result for the specified aggregate field without loss of precision. No
* coercion of data types or values is performed.
*
* See the `AggregateField` class for the expected aggregate result values and types. Numeric
* aggregate results will be boxed in an `NSNumber`.
*
* @param aggregateField An instance of `AggregateField` that specifies which aggregate result to
* return.
* @return Returns the aggregate result from the server without loss of precision.
* @warning Throws an `InvalidArgument` exception if the aggregate field was not requested in the
* `AggregateQuery`.
* @see `AggregateField`
*/
- (id)valueForAggregateField:(FIRAggregateField*)aggregateField NS_SWIFT_NAME(get(_:));
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2022 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
/**
* The sources from which an `AggregateQuery` can retrieve its results.
*
* See `AggregateQuery.getAggregation(source:completion:)`.
*/
typedef NS_ENUM(NSUInteger, FIRAggregateSource) {
/**
* Perform the aggregation on the server and download the result.
*
* The result received from the server is presented, unaltered, without considering any local
* state. That is, documents in the local cache are not taken into consideration, neither are
* local modifications not yet synchronized with the server. Previously-downloaded results, if
* any, are not used. Every request using this source necessarily involves a round trip to the
* server.
*
* The `AggregateQuery` will fail if the server cannot be reached, such as if the client is
* offline.
*/
FIRAggregateSourceServer,
} NS_SWIFT_NAME(AggregateSource);
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,100 @@
/*
* 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 "FIRQuery.h"
NS_ASSUME_NONNULL_BEGIN
@class FIRDocumentReference;
/**
* A `CollectionReference` object can be used for adding documents, getting document references,
* and querying for documents (using the methods inherited from `Query`).
*/
NS_SWIFT_NAME(CollectionReference)
@interface FIRCollectionReference : FIRQuery
/** :nodoc: */
- (id)init __attribute__((unavailable("FIRCollectionReference cannot be created directly.")));
/** ID of the referenced collection. */
@property(nonatomic, strong, readonly) NSString *collectionID;
/**
* For subcollections, `parent` returns the containing `DocumentReference`. For root collections,
* `nil` is returned.
*/
@property(nonatomic, strong, nullable, readonly) FIRDocumentReference *parent;
/**
* A string containing the slash-separated path to this this `CollectionReference` (relative to the
* root of the database).
*/
@property(nonatomic, strong, readonly) NSString *path;
/**
* Returns a `DocumentReference` pointing to a new document with an auto-generated ID.
*
* @return A `DocumentReference` pointing to a new document with an auto-generated ID.
*/
- (FIRDocumentReference *)documentWithAutoID NS_SWIFT_NAME(document());
/**
* Gets a `DocumentReference` referring to the document at the specified path, relative to this
* collection's own path.
*
* @param documentPath The slash-separated relative path of the document for which to get a
* `DocumentReference`.
*
* @return The `DocumentReference` for the specified document path.
*/
- (FIRDocumentReference *)documentWithPath:(NSString *)documentPath NS_SWIFT_NAME(document(_:));
/**
* Adds a new document to this collection with the specified data, assigning it a document ID
* automatically.
*
* @param data A `Dictionary` containing the data for the new document.
*
* @return A `DocumentReference` pointing to the newly created document.
*/
- (FIRDocumentReference *)addDocumentWithData:(NSDictionary<NSString *, id> *)data
NS_SWIFT_NAME(addDocument(data:));
/**
* Adds a new document to this collection with the specified data, assigning it a document ID
* automatically.
*
* @param data A `Dictionary` containing the data for the new document.
* @param completion A block to execute once the document has been successfully written to
* the server. This block will not be called while the client is offline, though local
* changes will be visible immediately.
*
* @return A `DocumentReference` pointing to the newly created document.
*/
// clang-format off
// clang-format breaks the NS_SWIFT_NAME attribute
- (FIRDocumentReference *)addDocumentWithData:(NSDictionary<NSString *, id> *)data
completion:
(nullable void (^)(NSError *_Nullable error))completion
NS_SWIFT_NAME(addDocument(data:completion:));
// clang-format on
@end
NS_ASSUME_NONNULL_END

View File

@@ -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>
NS_ASSUME_NONNULL_BEGIN
@class FIRQueryDocumentSnapshot;
#if defined(NS_CLOSED_ENUM)
/** An enumeration of document change types. */
typedef NS_CLOSED_ENUM(NSInteger, FIRDocumentChangeType)
#else
/** An enumeration of document change types. */
typedef NS_ENUM(NSInteger, FIRDocumentChangeType)
#endif
{
/** Indicates a new document was added to the set of documents matching the query. */
FIRDocumentChangeTypeAdded,
/** Indicates a document within the query was modified. */
FIRDocumentChangeTypeModified,
/**
* Indicates a document within the query was removed (either deleted or no longer matches
* the query.
*/
FIRDocumentChangeTypeRemoved
} NS_SWIFT_NAME(DocumentChangeType);
/**
* A `DocumentChange` represents a change to the documents matching a query. It contains the
* document affected and the type of change that occurred (added, modified, or removed).
*/
NS_SWIFT_NAME(DocumentChange)
@interface FIRDocumentChange : NSObject
/** :nodoc: */
- (id)init __attribute__((unavailable("FIRDocumentChange cannot be created directly.")));
/** The type of change that occurred (added, modified, or removed). */
@property(nonatomic, readonly) FIRDocumentChangeType type;
/** The document affected by this change. */
@property(nonatomic, strong, readonly) FIRQueryDocumentSnapshot *document;
/**
* The index of the changed document in the result set immediately prior to this `DocumentChange`
* (i.e. supposing that all prior `DocumentChange` objects have been applied). `NSNotFound` for
* `DocumentChangeTypeAdded` events.
*/
@property(nonatomic, readonly) NSUInteger oldIndex;
/**
* The index of the changed document in the result set immediately after this `DocumentChange`
* (i.e. supposing that all prior `DocumentChange` objects and the current `DocumentChange` object
* have been applied). `NSNotFound` for `DocumentChangeTypeRemoved` events.
*/
@property(nonatomic, readonly) NSUInteger newIndex;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,292 @@
/*
* 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 "FIRFirestoreSource.h"
#import "FIRListenerRegistration.h"
#import "FIRSnapshotListenOptions.h"
@class FIRCollectionReference;
@class FIRDocumentSnapshot;
@class FIRFirestore;
NS_ASSUME_NONNULL_BEGIN
/**
* A block type used to handle snapshot updates.
*/
typedef void (^FIRDocumentSnapshotBlock)(FIRDocumentSnapshot *_Nullable snapshot,
NSError *_Nullable error)
NS_SWIFT_UNAVAILABLE("Use Swift's closure syntax instead.");
/**
* A `DocumentReference` refers to a document location in a Firestore database and can be
* used to write, read, or listen to the location. The document at the referenced location
* may or may not exist. A `DocumentReference` can also be used to create a `CollectionReference` to
* a subcollection.
*/
NS_SWIFT_NAME(DocumentReference)
@interface FIRDocumentReference : NSObject
/** :nodoc: */
- (instancetype)init
__attribute__((unavailable("FIRDocumentReference cannot be created directly.")));
/** The ID of the document referred to. */
@property(nonatomic, strong, readonly) NSString *documentID;
/** A reference to the collection to which this `DocumentReference` belongs. */
@property(nonatomic, strong, readonly) FIRCollectionReference *parent;
/** The `Firestore` for the Firestore database (useful for performing transactions, etc.). */
@property(nonatomic, strong, readonly) FIRFirestore *firestore;
/**
* A string representing the path of the referenced document (relative to the root of the
* database).
*/
@property(nonatomic, strong, readonly) NSString *path;
/**
* Gets a `CollectionReference` referring to the collection at the specified path, relative to this
* document.
*
* @param collectionPath The slash-separated relative path of the collection for which to get a
* `CollectionReference`.
*
* @return The `CollectionReference` at the specified _collectionPath_.
*/
- (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath
NS_SWIFT_NAME(collection(_:));
#pragma mark - Writing Data
/**
* Writes to the document referred to by `DocumentReference`. If the document doesn't yet exist,
* this method creates it and then sets the data. If the document exists, this method overwrites
* the document data with the new values.
*
* @param documentData A `Dictionary` that contains the fields and data to write to the
* document.
*/
- (void)setData:(NSDictionary<NSString *, id> *)documentData;
/**
* Writes to the document referred to by this `DocumentReference`. If the document does not yet
* exist, it will be created. If you pass `merge:true`, the provided data will be merged into
* any existing document.
*
* @param documentData A `Dictionary` that contains the fields and data to write to the
* document.
* @param merge Whether to merge the provided data into any existing document. If enabled,
* all omitted fields remain untouched. If your input sets any field to an empty dictionary, any
* nested field is overwritten.
*/
- (void)setData:(NSDictionary<NSString *, id> *)documentData merge:(BOOL)merge;
/**
* Writes to the document referred to by `document` and only replace the fields
* specified under `mergeFields`. Any field that is not specified in `mergeFields`
* is ignored and remains untouched. If the document doesn't yet exist,
* this method creates it and then sets the data.
*
* It is an error to include a field in `mergeFields` that does not have a corresponding
* value in the `data` dictionary.
*
* @param documentData A `Dictionary` containing the fields that make up the document
* to be written.
* @param mergeFields An `Array` that contains a list of `String` or `FieldPath` elements
* specifying which fields to merge. Fields can contain dots to reference nested fields within
* the document. If your input sets any field to an empty dictionary, any nested field is
* overwritten.
*/
- (void)setData:(NSDictionary<NSString *, id> *)documentData mergeFields:(NSArray<id> *)mergeFields;
/**
* Overwrites the document referred to by this `DocumentReference`. If no document exists, it
* is created. If a document already exists, it is overwritten.
*
* @param documentData A `Dictionary` containing the fields that make up the document
* to be written.
* @param completion A block to execute once the document has been successfully written to the
* server. This block will not be called while the client is offline, though local
* changes will be visible immediately.
*/
- (void)setData:(NSDictionary<NSString *, id> *)documentData
completion:(nullable void (^)(NSError *_Nullable error))completion;
/**
* Writes to the document referred to by this `DocumentReference`. If the document does not yet
* exist, it will be created. If you pass `merge:true`, the provided data will be merged into
* any existing document.
*
* @param documentData A `Dictionary` containing the fields that make up the document
* to be written.
* @param merge Whether to merge the provided data into any existing document. If your input sets
* any field to an empty dictionary, any nested field is overwritten.
* @param completion A block to execute once the document has been successfully written to the
* server. This block will not be called while the client is offline, though local
* changes will be visible immediately.
*/
- (void)setData:(NSDictionary<NSString *, id> *)documentData
merge:(BOOL)merge
completion:(nullable void (^)(NSError *_Nullable error))completion;
/**
* Writes to the document referred to by `document` and only replace the fields
* specified under `mergeFields`. Any field that is not specified in `mergeFields`
* is ignored and remains untouched. If the document doesn't yet exist,
* this method creates it and then sets the data.
*
* It is an error to include a field in `mergeFields` that does not have a corresponding
* value in the `data` dictionary.
*
* @param documentData A `Dictionary` containing the fields that make up the document
* to be written.
* @param mergeFields An `Array` that contains a list of `String` or `FieldPath` elements
* specifying which fields to merge. Fields can contain dots to reference nested fields within
* the document. If your input sets any field to an empty dictionary, any nested field is
* overwritten.
* @param completion A block to execute once the document has been successfully written to the
* server. This block will not be called while the client is offline, though local
* changes will be visible immediately.
*/
- (void)setData:(NSDictionary<NSString *, id> *)documentData
mergeFields:(NSArray<id> *)mergeFields
completion:(nullable void (^)(NSError *_Nullable error))completion;
/**
* Updates fields in the document referred to by this `DocumentReference`.
* If the document does not exist, the update fails (specify a completion block to be notified).
*
* @param fields A `Dictionary` containing the fields (expressed as an `String` or
* `FieldPath`) and values with which to update the document.
*/
- (void)updateData:(NSDictionary<id, id> *)fields;
/**
* Updates fields in the document referred to by this `DocumentReference`. If the document
* does not exist, the update fails and the specified completion block receives an error.
*
* @param fields A `Dictionary` containing the fields (expressed as a `String` or
* `FieldPath`) and values with which to update the document.
* @param completion A block to execute when the update is complete. If the update is successful the
* error parameter will be nil, otherwise it will give an indication of how the update failed.
* This block will only execute when the client is online and the commit has completed against
* the server. The completion handler will not be called when the device is offline, though
* local changes will be visible immediately.
*/
- (void)updateData:(NSDictionary<id, id> *)fields
completion:(nullable void (^)(NSError *_Nullable error))completion;
// NOTE: this method is named 'deleteDocument' in Objective-C because 'delete' is a keyword in
// Objective-C++.
/** Deletes the document referred to by this `DocumentReference`. */
// clang-format off
- (void)deleteDocument NS_SWIFT_NAME(delete());
// clang-format on
/**
* Deletes the document referred to by this `DocumentReference`.
*
* @param completion A block to execute once the document has been successfully written to the
* server. This block will not be called while the client is offline, though local
* changes will be visible immediately.
*/
// clang-format off
- (void)deleteDocumentWithCompletion:(nullable void (^)(NSError *_Nullable error))completion
NS_SWIFT_NAME(delete(completion:));
// clang-format on
#pragma mark - Retrieving Data
/**
* Reads the document referenced by this `DocumentReference`.
*
* This method attempts to provide up-to-date data when possible by waiting for
* data from the server, but it may return cached data or fail if you are
* offline and the server cannot be reached. See the
* `getDocument(source:completion:)` method to change this behavior.
*
* @param completion a block to execute once the document has been successfully read.
*/
- (void)getDocumentWithCompletion:
(void (^)(FIRDocumentSnapshot *_Nullable snapshot, NSError *_Nullable error))completion
NS_SWIFT_NAME(getDocument(completion:));
/**
* Reads the document referenced by this `DocumentReference`.
*
* @param source indicates whether the results should be fetched from the cache
* only (`Source.cache`), the server only (`Source.server`), or to attempt
* the server and fall back to the cache (`Source.default`).
* @param completion a block to execute once the document has been successfully read.
*/
// clang-format off
- (void)getDocumentWithSource:(FIRFirestoreSource)source
completion:(void (^)(FIRDocumentSnapshot *_Nullable snapshot,
NSError *_Nullable error))completion
NS_SWIFT_NAME(getDocument(source:completion:));
// clang-format on
/**
* Attaches a listener for `DocumentSnapshot` events.
*
* @param listener The listener to attach.
*
* @return A `ListenerRegistration` that can be used to remove this listener.
*/
- (id<FIRListenerRegistration>)addSnapshotListener:
(void (^)(FIRDocumentSnapshot *_Nullable snapshot, NSError *_Nullable error))listener
NS_SWIFT_NAME(addSnapshotListener(_:));
/**
* Attaches a listener for `DocumentSnapshot` events.
*
* @param includeMetadataChanges Whether metadata-only changes (i.e. only
* `DocumentSnapshot.metadata` changed) should trigger snapshot events.
* @param listener The listener to attach.
*
* @return A `ListenerRegistration` that can be used to remove this listener.
*/
// clang-format off
- (id<FIRListenerRegistration>)
addSnapshotListenerWithIncludeMetadataChanges:(BOOL)includeMetadataChanges
listener:(void (^)(FIRDocumentSnapshot *_Nullable snapshot,
NSError *_Nullable error))listener
NS_SWIFT_NAME(addSnapshotListener(includeMetadataChanges:listener:));
// clang-format on
/**
* Attaches a listener for `DocumentSnapshot` events.
*
* @param options Sets snapshot listener options, including whether metadata-only changes should
* trigger snapshot events, the source to listen to, the executor to use to call the
* listener, or the activity to scope the listener to.
* @param listener The listener to attach.
*
* @return A `ListenerRegistration` that can be used to remove this listener.
*/
- (id<FIRListenerRegistration>)
addSnapshotListenerWithOptions:(FIRSnapshotListenOptions *)options
listener:(void (^)(FIRDocumentSnapshot *_Nullable snapshot,
NSError *_Nullable error))listener
NS_SWIFT_NAME(addSnapshotListener(options:listener:));
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,180 @@
/*
* 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 FIRDocumentReference;
@class FIRSnapshotMetadata;
NS_ASSUME_NONNULL_BEGIN
/**
* Controls the return value for server timestamps that have not yet been set to
* their final value.
*/
typedef NS_ENUM(NSInteger, FIRServerTimestampBehavior) {
/**
* Return `NSNull` for `FieldValue.serverTimestamp()` fields that have not yet
* been set to their final value.
*/
FIRServerTimestampBehaviorNone,
/**
* Return a local estimates for `FieldValue.serverTimestamp()`
* fields that have not yet been set to their final value. This estimate will
* likely differ from the final value and may cause these pending values to
* change once the server result becomes available.
*/
FIRServerTimestampBehaviorEstimate,
/**
* Return the previous value for `FieldValue.serverTimestamp()` fields that
* have not yet been set to their final value.
*/
FIRServerTimestampBehaviorPrevious
} NS_SWIFT_NAME(ServerTimestampBehavior);
/**
* A `DocumentSnapshot` contains data read from a document in your Firestore database. The data
* can be extracted with the `data` property or by using subscript syntax to access a specific
* field.
*
* For a `DocumentSnapshot` that points to a non-existing document, any data access will return
* `nil`. You can use the `exists` property to explicitly verify a documents existence.
*/
NS_SWIFT_NAME(DocumentSnapshot)
@interface FIRDocumentSnapshot : NSObject
/** :nodoc: */
- (instancetype)init
__attribute__((unavailable("FIRDocumentSnapshot cannot be created directly.")));
/** True if the document exists. */
@property(nonatomic, assign, readonly) BOOL exists;
/** A `DocumentReference` to the document location. */
@property(nonatomic, strong, readonly) FIRDocumentReference *reference;
/** The ID of the document for which this `DocumentSnapshot` contains data. */
@property(nonatomic, copy, readonly) NSString *documentID;
/** Metadata about this snapshot concerning its source and if it has local modifications. */
@property(nonatomic, strong, readonly) FIRSnapshotMetadata *metadata;
/**
* Retrieves all fields in the document as a `Dictionary`. Returns `nil` if the document doesn't
* exist.
*
* Server-provided timestamps that have not yet been set to their final value will be returned as
* `NSNull`. You can use the `data(with:)` method to configure this behavior.
*
* @return A `Dictionary` containing all fields in the document or `nil` if the document doesn't
* exist.
*/
- (nullable NSDictionary<NSString *, id> *)data;
/**
* Retrieves all fields in the document as a `Dictionary`. Returns `nil` if the document doesn't
* exist.
*
* @param serverTimestampBehavior Configures how server timestamps that have not yet been set to
* their final value are returned from the snapshot.
* @return A `Dictionary` containing all fields in the document or `nil` if the document doesn't
* exist.
*/
- (nullable NSDictionary<NSString *, id> *)dataWithServerTimestampBehavior:
(FIRServerTimestampBehavior)serverTimestampBehavior;
/**
* Retrieves a specific field from the document. Returns `nil` if the document or the field doesn't
* exist.
*
* The timestamps that have not yet been set to their final value will be returned as `NSNull`. You
* can use `get(_:serverTimestampBehavior:)` to configure this behavior.
*
* @param field The field to retrieve.
* @return The value contained in the field or `nil` if the document or field doesn't exist.
*/
- (nullable id)valueForField:(id)field NS_SWIFT_NAME(get(_:));
/**
* Retrieves a specific field from the document. Returns `nil` if the document or the field doesn't
* exist.
*
* The timestamps that have not yet been set to their final value will be returned as `NSNull`. You
* can use `get(_:serverTimestampBehavior:)` to configure this behavior.
*
* @param field The field to retrieve.
* @param serverTimestampBehavior Configures how server timestamps that have not yet been set to
* their final value are returned from the snapshot.
* @return The value contained in the field or `nil` if the document or field doesn't exist.
*/
// clang-format off
- (nullable id)valueForField:(id)field
serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior
NS_SWIFT_NAME(get(_:serverTimestampBehavior:));
// clang-format on
/**
* Retrieves a specific field from the document.
*
* @param key The field to retrieve.
*
* @return The value contained in the field or `nil` if the document or field doesn't exist.
*/
- (nullable id)objectForKeyedSubscript:(id)key;
@end
/**
* A `QueryDocumentSnapshot` contains data read from a document in your Firestore database as
* part of a query. The document is guaranteed to exist and its data can be extracted with the
* `data` property or by using subscript syntax to access a specific field.
*
* A `QueryDocumentSnapshot` offers the same API surface as a `DocumentSnapshot`. As
* deleted documents are not returned from queries, its `exists` property will always be true and
* `data()` will never return `nil`.
*/
NS_SWIFT_NAME(QueryDocumentSnapshot)
@interface FIRQueryDocumentSnapshot : FIRDocumentSnapshot
/** :nodoc: */
- (instancetype)init
__attribute__((unavailable("FIRQueryDocumentSnapshot cannot be created directly.")));
/**
* Retrieves all fields in the document as a `Dictionary`.
*
* Server-provided timestamps that have not yet been set to their final value will be returned as
* `NSNull`. You can use the `data(with:)` method to configure this behavior.
*
* @return A `Dictionary` containing all fields in the document.
*/
- (NSDictionary<NSString *, id> *)data;
/**
* Retrieves all fields in the document as a `Dictionary`.
*
* @param serverTimestampBehavior Configures how server timestamps that have not yet been set to
* their final value are returned from the snapshot.
* @return A `Dictionary` containing all fields in the document.
*/
- (NSDictionary<NSString *, id> *)dataWithServerTimestampBehavior:
(FIRServerTimestampBehavior)serverTimestampBehavior;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,49 @@
/*
* 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
/**
* A `FieldPath` refers to a field in a document. The path may consist of a single field name
* (referring to a top level field in the document), or a list of field names (referring to a nested
* field in the document).
*/
NS_SWIFT_NAME(FieldPath)
@interface FIRFieldPath : NSObject <NSCopying>
/** :nodoc: */
- (instancetype)init NS_UNAVAILABLE;
/**
* Creates a `FieldPath` from the provided field names. If more than one field name is provided, the
* path will point to a nested field in a document.
*
* @param fieldNames A list of field names.
* @return A `FieldPath` that points to a field location in a document.
*/
- (instancetype)initWithFields:(NSArray<NSString *> *)fieldNames NS_SWIFT_NAME(init(_:));
/**
* A special sentinel `FieldPath` to refer to the ID of a document. It can be used in queries to
* sort or filter by the document ID.
*/
+ (instancetype)documentID;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,95 @@
/*
* 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
/**
* Sentinel values that can be used when writing document fields with `setData()` or `updateData()`.
*/
NS_SWIFT_NAME(FieldValue)
@interface FIRFieldValue : NSObject
/** :nodoc: */
- (instancetype)init NS_UNAVAILABLE;
/** Used with `updateData()` to mark a field for deletion. */
// clang-format off
+ (instancetype)fieldValueForDelete NS_SWIFT_NAME(delete());
// clang-format on
/**
* Used with `setData()` or `updateData()` to include a server-generated timestamp in the written
* data.
*/
+ (instancetype)fieldValueForServerTimestamp NS_SWIFT_NAME(serverTimestamp());
/**
* Returns a special value that can be used with `setData()` or `updateData()` that tells the server
* to union the given elements with any array value that already exists on the server. Each
* specified element that doesn't already exist in the array will be added to the end. If the
* field being modified is not already an array it will be overwritten with an array containing
* exactly the specified elements.
*
* @param elements The elements to union into the array.
* @return The `FieldValue` sentinel for use in a call to `setData()` or `updateData()`.
*/
+ (instancetype)fieldValueForArrayUnion:(NSArray<id> *)elements NS_SWIFT_NAME(arrayUnion(_:));
/**
* Returns a special value that can be used with `setData()` or `updateData()` that tells the server
* to remove the given elements from any array value that already exists on the server. All
* instances of each element specified will be removed from the array. If the field being
* modified is not already an array it will be overwritten with an empty array.
*
* @param elements The elements to remove from the array.
* @return The `FieldValue` sentinel for use in a call to `setData()` or `updateData()`.
*/
+ (instancetype)fieldValueForArrayRemove:(NSArray<id> *)elements NS_SWIFT_NAME(arrayRemove(_:));
/**
* Returns a special value that can be used with `setData()` or `updateData()` that tells the server
* to increment the field's current value by the given value.
*
* If the current value is an integer or a double, both the current and the given value will be
* interpreted as doubles and all arithmetic will follow IEEE 754 semantics. Otherwise, the
* transformation will set the field to the given value.
*
* @param d The double value to increment by.
* @return The `FieldValue` sentinel for use in a call to `setData()` or `updateData()`.
*/
+ (instancetype)fieldValueForDoubleIncrement:(double)d NS_SWIFT_NAME(increment(_:));
/**
* Returns a special value that can be used with `setData()` or `updateData()` that tells the server
* to increment the field's current value by the given value.
*
* If the current field value is an integer, possible integer overflows are resolved to LONG_MAX or
* LONG_MIN. If the current field value is a double, both values will be interpreted as doubles and
* the arithmetic will follow IEEE 754 semantics.
*
* If field is not an integer or double, or if the field does not yet exist, the transformation
* will set the field to the given value.
*
* @param l The integer value to increment by.
* @return The `FieldValue` sentinel for use in a call to `setData()` or `updateData()`.
*/
+ (instancetype)fieldValueForIntegerIncrement:(int64_t)l NS_SWIFT_NAME(increment(_:));
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,261 @@
/*
* Copyright 2022 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>
@class FIRFieldPath;
NS_ASSUME_NONNULL_BEGIN
/**
* A Filter represents a restriction on one or more field values and can be used to refine
* the results of a Query.
*/
NS_SWIFT_NAME(Filter)
@interface FIRFilter : NSObject
#pragma mark - Create Filter
/**
* Creates a new filter for checking that the given field is equal to the given value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field
isEqualTo:(nonnull id)value NS_SWIFT_NAME(whereField(_:isEqualTo:));
/**
* Creates a new filter for checking that the given field is equal to the given value.
*
* @param path The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
isEqualTo:(nonnull id)value NS_SWIFT_NAME(whereField(_:isEqualTo:));
/**
* Creates a new filter for checking that the given field is not equal to the given value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field
isNotEqualTo:(nonnull id)value NS_SWIFT_NAME(whereField(_:isNotEqualTo:));
/**
* Creates a new filter for checking that the given field is not equal to the given value.
*
* @param path The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
isNotEqualTo:(nonnull id)value NS_SWIFT_NAME(whereField(_:isNotEqualTo:));
/**
* Creates a new filter for checking that the given field is greater than the given value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field
isGreaterThan:(nonnull id)value NS_SWIFT_NAME(whereField(_:isGreaterThan:));
/**
* Creates a new filter for checking that the given field is greater than the given value.
*
* @param path The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
isGreaterThan:(nonnull id)value NS_SWIFT_NAME(whereField(_:isGreaterThan:));
/**
* Creates a new filter for checking that the given field is greater than or equal to the given
* value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field
isGreaterThanOrEqualTo:(nonnull id)value NS_SWIFT_NAME(whereField(_:isGreaterOrEqualTo:));
/**
* Creates a new filter for checking that the given field is greater than or equal to the given
* value.
*
* @param path The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
isGreaterThanOrEqualTo:(nonnull id)value
NS_SWIFT_NAME(whereField(_:isGreaterOrEqualTo:));
/**
* Creates a new filter for checking that the given field is less than the given value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field
isLessThan:(nonnull id)value NS_SWIFT_NAME(whereField(_:isLessThan:));
/**
* Creates a new filter for checking that the given field is less than the given value.
*
* @param path The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
isLessThan:(nonnull id)value NS_SWIFT_NAME(whereField(_:isLessThan:));
/**
* Creates a new filter for checking that the given field is less than or equal to the given
* value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field
isLessThanOrEqualTo:(nonnull id)value NS_SWIFT_NAME(whereField(_:isLessThanOrEqualTo:));
/**
* Creates a new filter for checking that the given field is less than or equal to the given
* value.
*
* @param path The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
isLessThanOrEqualTo:(nonnull id)value
NS_SWIFT_NAME(whereField(_:isLessThanOrEqualTo:));
/**
* Creates a new filter for checking that the given array field contains the given value.
*
* @param field The field used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field
arrayContains:(nonnull id)value NS_SWIFT_NAME(whereField(_:arrayContains:));
/**
* Creates a new filter for checking that the given array field contains the given value.
*
* @param path The field path used for the filter.
* @param value The value used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
arrayContains:(nonnull id)value NS_SWIFT_NAME(whereField(_:arrayContains:));
/**
* Creates a new filter for checking that the given array field contains any of the given values.
*
* @param field The field used for the filter.
* @param values The list of values used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field
arrayContainsAny:(nonnull NSArray<id> *)values
NS_SWIFT_NAME(whereField(_:arrayContainsAny:));
/**
* Creates a new filter for checking that the given array field contains any of the given values.
*
* @param path The field path used for the filter.
* @param values The list of values used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
arrayContainsAny:(nonnull NSArray<id> *)values
NS_SWIFT_NAME(whereField(_:arrayContainsAny:));
/**
* Creates a new filter for checking that the given field equals any of the given values.
*
* @param field The field used for the filter.
* @param values The list of values used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field
in:(nonnull NSArray<id> *)values NS_SWIFT_NAME(whereField(_:in:));
/**
* Creates a new filter for checking that the given field equals any of the given values.
*
* @param path The field path used for the filter.
* @param values The list of values used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
in:(nonnull NSArray<id> *)values NS_SWIFT_NAME(whereField(_:in:));
/**
* Creates a new filter for checking that the given field does not equal any of the given values.
*
* @param field The field path used for the filter.
* @param values The list of values used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereField:(nonnull NSString *)field
notIn:(nonnull NSArray<id> *)values NS_SWIFT_NAME(whereField(_:notIn:));
/**
* Creates a new filter for checking that the given field does not equal any of the given values.
*
* @param path The field path used for the filter.
* @param values The list of values used for the filter.
* @return The newly created filter.
*/
+ (FIRFilter *)filterWhereFieldPath:(nonnull FIRFieldPath *)path
notIn:(nonnull NSArray<id> *)values
NS_SWIFT_NAME(whereField(_:notIn:));
/**
* Creates a new filter that is a disjunction of the given filters. A disjunction filter includes
* a document if it satisfies any of the given filters.
*
* @param filters The list of filters to perform a disjunction for.
* @return The newly created filter.
*/
+ (FIRFilter *)orFilterWithFilters:(NSArray<FIRFilter *> *)filters NS_SWIFT_NAME(orFilter(_:));
/**
* Creates a new filter that is a conjunction of the given filters. A conjunction filter includes
* a document if it satisfies all of the given filters.
*
* @param filters The list of filters to perform a disjunction for.
* @return The newly created filter.
*/
+ (FIRFilter *)andFilterWithFilters:(NSArray<FIRFilter *> *)filters NS_SWIFT_NAME(andFilter(_:));
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,465 @@
/*
* 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 "FIRListenerRegistration.h"
@class FIRApp;
@class FIRCollectionReference;
@class FIRDocumentReference;
@class FIRFirestoreSettings;
@class FIRLoadBundleTask;
@class FIRLoadBundleTaskProgress;
@class FIRQuery;
@class FIRTransaction;
@class FIRTransactionOptions;
@class FIRWriteBatch;
@class FIRPersistentCacheIndexManager;
NS_ASSUME_NONNULL_BEGIN
/**
* `Firestore` represents a Firestore Database and is the entry point for all Firestore
* operations.
*/
NS_SWIFT_NAME(Firestore)
@interface FIRFirestore : NSObject
#pragma mark - Initializing
/** :nodoc: */
- (instancetype)init __attribute__((unavailable("Use a static constructor method.")));
/**
* Creates, caches, and returns the default `Firestore` using the default `FirebaseApp`. Each
* subsequent invocation returns the same `Firestore` object.
*
* @return The default `Firestore` instance.
*/
+ (instancetype)firestore NS_SWIFT_NAME(firestore());
/**
* Creates, caches, and returns the default `Firestore` object for the specified _app_. Each
* subsequent invocation returns the same `Firestore` object.
*
* @param app The `FirebaseApp` instance to use for authentication and as a source of the Google
* Cloud Project ID for your Firestore Database. If you want the default instance, you should
* explicitly set it to `FirebaseApp.app()`.
*
* @return The default `Firestore` instance.
*/
+ (instancetype)firestoreForApp:(FIRApp *)app NS_SWIFT_NAME(firestore(app:));
/**
* This method is in preview. API signature and functionality are subject to change.
*
* Creates, caches, and returns named `Firestore` object for the specified `FirebaseApp`. Each
* subsequent invocation returns the same `Firestore` object.
*
* @param app The `FirebaseApp` instance to use for authentication and as a source of the Google
* Cloud Project ID for your Firestore Database. If you want the default instance, you should
* explicitly set it to `FirebaseApp.app()`.
* @param database The database name.
*
* @return The named `Firestore` instance.
*/
+ (instancetype)firestoreForApp:(FIRApp *)app
database:(NSString *)database NS_SWIFT_NAME(firestore(app:database:));
/**
* This method is in preview. API signature and functionality are subject to change.
*
* Creates, caches, and returns named `Firestore` object for the default _app_. Each subsequent
* invocation returns the same `Firestore` object.
*
* @param database The database name.
*
* @return The named `Firestore` instance.
*/
+ (instancetype)firestoreForDatabase:(NSString *)database NS_SWIFT_NAME(firestore(database:));
/**
* Custom settings used to configure this `Firestore` object.
*/
@property(nonatomic, copy) FIRFirestoreSettings *settings;
/**
* The Firebase App associated with this Firestore instance.
*/
@property(strong, nonatomic, readonly) FIRApp *app;
#pragma mark - Configure FieldIndexes
/**
* A PersistentCacheIndexManager which you can config persistent cache indexes used for
* local query execution.
*/
@property(nonatomic, readonly, nullable)
FIRPersistentCacheIndexManager *persistentCacheIndexManager;
/**
* NOTE: This preview method will be deprecated in a future major release. Consider using
* `PersistentCacheIndexManager.enableIndexAutoCreation()` to let the SDK decide whether to create
* cache indexes for queries running locally.
*
* Configures indexing for local query execution. Any previous index configuration is overridden.
*
* The index entries themselves are created asynchronously. You can continue to use queries
* that require indexing even if the indices are not yet available. Query execution will
* automatically start using the index once the index entries have been written.
*
* The method accepts the JSON format exported by the Firebase CLI (`firebase
* firestore:indexes`). If the JSON format is invalid, the completion block will be
* invoked with an NSError.
*
* @param json The JSON format exported by the Firebase CLI.
* @param completion A block to execute when setting is in a final state. The `error` parameter
* will be set if the block is invoked due to an error.
*/
- (void)setIndexConfigurationFromJSON:(NSString *)json
completion:(nullable void (^)(NSError *_Nullable error))completion
NS_SWIFT_NAME(setIndexConfiguration(_:completion:)) DEPRECATED_MSG_ATTRIBUTE(
"Instead of creating cache indexes manually, consider using "
"`PersistentCacheIndexManager.enableIndexAutoCreation()` to let the SDK decide whether to "
"create cache indexes for queries running locally.");
/**
* NOTE: This preview method will be deprecated in a future major release. Consider using
* `PersistentCacheIndexManager.enableIndexAutoCreation()` to let the SDK decide whether to create
* cache indexes for queries running locally.
*
* Configures indexing for local query execution. Any previous index configuration is overridden.
*
* The index entries themselves are created asynchronously. You can continue to use queries
* that require indexing even if the indices are not yet available. Query execution will
* automatically start using the index once the index entries have been written.
*
* Indexes are only supported with LevelDB persistence. Invoke `set_persistence_enabled(true)`
* before setting an index configuration. If LevelDB is not enabled, any index configuration
* will be rejected.
*
* The method accepts the JSON format exported by the Firebase CLI (`firebase
* firestore:indexes`). If the JSON format is invalid, this method ignores the changes.
*
* @param stream An input stream from which the configuration can be read.
* @param completion A block to execute when setting is in a final state. The `error` parameter
* will be set if the block is invoked due to an error.
*/
- (void)setIndexConfigurationFromStream:(NSInputStream *)stream
completion:(nullable void (^)(NSError *_Nullable error))completion
NS_SWIFT_NAME(setIndexConfiguration(_:completion:)) DEPRECATED_MSG_ATTRIBUTE(
"Instead of creating cache indexes manually, consider using "
"`PersistentCacheIndexManager.enableIndexAutoCreation()` to let the SDK decide whether to "
"create cache indexes for queries running locally.");
#pragma mark - Collections and Documents
/**
* Gets a `CollectionReference` referring to the collection at the specified path within the
* database.
*
* @param collectionPath The slash-separated path of the collection for which to get a
* `CollectionReference`.
*
* @return The `CollectionReference` at the specified _collectionPath_.
*/
- (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath
NS_SWIFT_NAME(collection(_:));
/**
* Gets a `DocumentReference` referring to the document at the specified path within the
* database.
*
* @param documentPath The slash-separated path of the document for which to get a
* `DocumentReference`.
*
* @return The `DocumentReference` for the specified _documentPath_.
*/
- (FIRDocumentReference *)documentWithPath:(NSString *)documentPath NS_SWIFT_NAME(document(_:));
#pragma mark - Collection Group Queries
/**
* Creates and returns a new `Query` that includes all documents in the database that are contained
* in a collection or subcollection with the given collectionID.
*
* @param collectionID Identifies the collections to query over. Every collection or subcollection
* with this ID as the last segment of its path will be included. Cannot contain a slash.
* @return The created `Query`.
*/
- (FIRQuery *)collectionGroupWithID:(NSString *)collectionID NS_SWIFT_NAME(collectionGroup(_:));
#pragma mark - Transactions and Write Batches
/**
* Executes the given updateBlock and then attempts to commit the changes applied within an atomic
* transaction.
*
* The maximum number of writes allowed in a single transaction is 500, but note that each usage of
* `FieldValue.serverTimestamp()`, `FieldValue.arrayUnion()`, `FieldValue.arrayRemove()`, or
* `FieldValue.increment()` inside a transaction counts as an additional write.
*
* In the updateBlock, a set of reads and writes can be performed atomically using the
* `Transaction` object passed to the block. After the updateBlock is run, Firestore will attempt
* to apply the changes to the server. If any of the data read has been modified outside of this
* transaction since being read, then the transaction will be retried by executing the updateBlock
* again. If the transaction still fails after 5 retries, then the transaction will fail.
*
* Since the updateBlock may be executed multiple times, it should avoiding doing anything that
* would cause side effects.
*
* Any value maybe be returned from the updateBlock. If the transaction is successfully committed,
* then the completion block will be passed that value. The updateBlock also has an `NSErrorPointer`
* out parameter. If this is set, then the transaction will not attempt to commit, and the given
* error will be passed to the completion block.
*
* The `Transaction` object passed to the updateBlock contains methods for accessing documents
* and collections. Unlike other firestore access, data accessed with the transaction will not
* reflect local changes that have not been committed. For this reason, it is required that all
* reads are performed before any writes. Transactions must be performed while online. Otherwise,
* reads will fail, the final commit will fail, and the completion block will return an error.
*
* @param updateBlock The block to execute within the transaction context.
* @param completion The block to call with the result or error of the transaction. This
* block will run even if the client is offline, unless the process is killed.
*/
- (void)runTransactionWithBlock:(id _Nullable (^)(FIRTransaction *, NSError **))updateBlock
completion:(void (^)(id _Nullable result, NSError *_Nullable error))completion
__attribute__((swift_async(none))); // Disable async import due to #9426.
/**
* Executes the given updateBlock and then attempts to commit the changes applied within an atomic
* transaction.
*
* The maximum number of writes allowed in a single transaction is 500, but note that each usage of
* `FieldValue.serverTimestamp()`, `FieldValue.arrayUnion()`, `FieldValue.arrayRemove()`, or
* `FieldValue.increment()` inside a transaction counts as an additional write.
*
* In the updateBlock, a set of reads and writes can be performed atomically using the
* `Transaction` object passed to the block. After the updateBlock is run, Firestore will attempt
* to apply the changes to the server. If any of the data read has been modified outside of this
* transaction since being read, then the transaction will be retried by executing the updateBlock
* again. If the transaction still fails after the attempting the number of times specified by the
* `max_attempts` property of the given `TransactionOptions` object, then the transaction will fail.
* If the given `TransactionOptions` is `nil`, then the default `max_attempts` of 5 will be used.
*
* Since the updateBlock may be executed multiple times, it should avoiding doing anything that
* would cause side effects.
*
* Any value maybe be returned from the updateBlock. If the transaction is successfully committed,
* then the completion block will be passed that value. The updateBlock also has an `NSErrorPointer`
* out parameter. If this is set, then the transaction will not attempt to commit, and the given
* error will be passed to the completion block.
*
* The `Transaction` object passed to the updateBlock contains methods for accessing documents
* and collections. Unlike other firestore access, data accessed with the transaction will not
* reflect local changes that have not been committed. For this reason, it is required that all
* reads are performed before any writes. Transactions must be performed while online. Otherwise,
* reads will fail, the final commit will fail, and the completion block will return an error.
*
* @param options The transaction options for controlling execution, or `nil` to use the default
* transaction options.
* @param updateBlock The block to execute within the transaction context.
* @param completion The block to call with the result or error of the transaction. This
* block will run even if the client is offline, unless the process is killed.
*/
- (void)runTransactionWithOptions:(FIRTransactionOptions *_Nullable)options
block:(id _Nullable (^)(FIRTransaction *, NSError **))updateBlock
completion:
(void (^)(id _Nullable result, NSError *_Nullable error))completion
__attribute__((swift_async(none))); // Disable async import due to #9426.
/**
* Creates a write batch, used for performing multiple writes as a single
* atomic operation.
*
* The maximum number of writes allowed in a single batch is 500, but note that each usage of
* `FieldValue.serverTimestamp()`, `FieldValue.arrayUnion()`, `FieldValue.arrayRemove()`, or
* `FieldValue.increment()` inside a batch counts as an additional write.
* Unlike transactions, write batches are persisted offline and therefore are preferable when you
* don't need to condition your writes on read data.
*/
- (FIRWriteBatch *)batch;
#pragma mark - Logging
/** Enables or disables logging from the Firestore client. */
+ (void)enableLogging:(BOOL)logging;
#pragma mark - Network
/**
* Configures Firestore to connect to an emulated host instead of the default remote backend. After
* Firestore has been used (i.e. a document reference has been instantiated), this value cannot be
* changed.
*/
- (void)useEmulatorWithHost:(NSString *)host port:(NSInteger)port;
/**
* Re-enables usage of the network by this Firestore instance after a prior call to
* `disableNetwork(completion:)`. Completion block, if provided, will be called once network uasge
* has been enabled.
*/
- (void)enableNetworkWithCompletion:(nullable void (^)(NSError *_Nullable error))completion;
/**
* Disables usage of the network by this Firestore instance. It can be re-enabled by via
* `enableNetwork`. While the network is disabled, any snapshot listeners or get calls will return
* results from cache and any write operations will be queued until the network is restored. The
* completion block, if provided, will be called once network usage has been disabled.
*/
- (void)disableNetworkWithCompletion:(nullable void (^)(NSError *_Nullable error))completion;
/**
* Clears the persistent storage. This includes pending writes and cached documents.
*
* Must be called while the firestore instance is not started (after the app is shutdown or when
* the app is first initialized). On startup, this method must be called before other methods
* (other than `Firestore.settings`). If the firestore instance is still running, the function
* will complete with an error code of `FailedPrecondition`.
*
* Note: `clearPersistence(completion:)` is primarily intended to help write reliable tests that
* use Firestore. It uses the most efficient mechanism possible for dropping existing data but
* does not attempt to securely overwrite or otherwise make cached data unrecoverable. For
* applications that are sensitive to the disclosure of cache data in between user sessions we
* strongly recommend not to enable persistence in the first place.
*/
- (void)clearPersistenceWithCompletion:(nullable void (^)(NSError *_Nullable error))completion;
/**
* Waits until all currently pending writes for the active user have been acknowledged by the
* backend.
*
* The completion block is called immediately without error if there are no outstanding writes.
* Otherwise, the completion block is called when all previously issued writes (including those
* written in a previous app session) have been acknowledged by the backend. The completion
* block does not wait for writes that were added after the method is called. If you
* wish to wait for additional writes, you have to call `waitForPendingWrites` again.
*
* Any outstanding `waitForPendingWrites(completion:)` completion blocks are called with an error
* during user change.
*/
- (void)waitForPendingWritesWithCompletion:(void (^)(NSError *_Nullable error))completion;
/**
* Attaches a listener for a snapshots-in-sync event. The snapshots-in-sync event indicates that all
* listeners affected by a given change have fired, even if a single server-generated change affects
* multiple listeners.
*
* NOTE: The snapshots-in-sync event only indicates that listeners are in sync with each other, but
* does not relate to whether those snapshots are in sync with the server. Use SnapshotMetadata in
* the individual listeners to determine if a snapshot is from the cache or the server.
*
* @param listener A callback to be called every time all snapshot listeners are in sync with each
* other.
* @return A `ListenerRegistration` object that can be used to remove the listener.
*/
- (id<FIRListenerRegistration>)addSnapshotsInSyncListener:(void (^)(void))listener
NS_SWIFT_NAME(addSnapshotsInSyncListener(_:));
#pragma mark - Terminating
/**
* Terminates this `Firestore` instance.
*
* After calling `terminate` only the `clearPersistence` method may be used. Any other method will
* throw an error.
*
* To restart after termination, simply create a new instance of `Firestore` with the `firestore`
* method.
*
* Termination does not cancel any pending writes and any tasks that are awaiting a response from
* the server will not be resolved. The next time you start this instance, it will resume attempting
* to send these writes to the server.
*
* Note: Under normal circumstances, calling this method is not required. This method is useful only
* when you want to force this instance to release all of its resources or in combination with
* `clearPersistence` to ensure that all local state is destroyed between test runs.
*
* @param completion A block to execute once everything has been terminated.
*/
- (void)terminateWithCompletion:(nullable void (^)(NSError *_Nullable error))completion
NS_SWIFT_NAME(terminate(completion:));
#pragma mark - Bundles
/**
* Loads a Firestore bundle into the local cache.
*
* @param bundleData Data from the bundle to be loaded.
* @return A `LoadBundleTask` which allows registered observers
* to receive progress updates and completion or error events.
*/
- (FIRLoadBundleTask *)loadBundle:(NSData *)bundleData NS_SWIFT_NAME(loadBundle(_:));
/**
* Loads a Firestore bundle into the local cache.
*
* @param bundleData Data from the bundle to be loaded.
* @param completion A block to execute when loading is in a final state. The `error` parameter
* will be set if the block is invoked due to an error. If observers are registered to the
* `LoadBundleTask`, this block will be called after all observers are notified.
* @return A `LoadBundleTask` which allows registered observers to receive progress updates and
* completion or error events.
*/
- (FIRLoadBundleTask *)loadBundle:(NSData *)bundleData
completion:(nullable void (^)(FIRLoadBundleTaskProgress *_Nullable progress,
NSError *_Nullable error))completion
NS_SWIFT_NAME(loadBundle(_:completion:));
/**
* Loads a Firestore bundle into the local cache.
*
* @param bundleStream An input stream from which the bundle can be read.
* @return A `LoadBundleTask` which allows registered observers to receive progress updates and
* completion or error events.
*/
- (FIRLoadBundleTask *)loadBundleStream:(NSInputStream *)bundleStream NS_SWIFT_NAME(loadBundle(_:));
/**
* Loads a Firestore bundle into the local cache.
*
* @param bundleStream An input stream from which the bundle can be read.
* @param completion A block to execute when the loading is in a final state. The `error` parameter
* of the block will be set if it is due to an error. If observers are registered to the returning
* `LoadBundleTask`, this block will be called after all observers are notified.
* @return A `LoadBundleTask` which allow registering observers to receive progress updates, and
* completion or error events.
*/
- (FIRLoadBundleTask *)loadBundleStream:(NSInputStream *)bundleStream
completion:
(nullable void (^)(FIRLoadBundleTaskProgress *_Nullable progress,
NSError *_Nullable error))completion
NS_SWIFT_NAME(loadBundle(_:completion:));
/**
* Reads a `Query` from the local cache, identified by the given name.
*
* Named queries are packaged into bundles on the server side (along with the resulting documents)
* and loaded into local cache using `loadBundle`. Once in the local cache, you can use this method
* to extract a query by name.
*
* @param completion A block to execute with the query read from the local cache. If no query can be
* found, its parameter will be `nil`.
*/
- (void)getQueryNamed:(NSString *)name
completion:(void (^)(FIRQuery *_Nullable query))completion
NS_SWIFT_NAME(getQuery(named:completion:));
@end
NS_ASSUME_NONNULL_END

View File

@@ -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 <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/** The Cloud Firestore error domain. */
FOUNDATION_EXPORT NSString *const FIRFirestoreErrorDomain NS_SWIFT_NAME(FirestoreErrorDomain);
/** Error codes used by Cloud Firestore. */
typedef NS_ERROR_ENUM(FIRFirestoreErrorDomain, FIRFirestoreErrorCode){
/**
* The operation completed successfully. `NSError` objects will never have a code with this
* value.
*/
FIRFirestoreErrorCodeOK = 0,
/** The operation was cancelled (typically by the caller). */
FIRFirestoreErrorCodeCancelled = 1,
/** Unknown error or an error from a different error domain. */
FIRFirestoreErrorCodeUnknown = 2,
/**
* Client specified an invalid argument. Note that this differs from FailedPrecondition.
* InvalidArgument indicates arguments that are problematic regardless of the state of the
* system (e.g., an invalid field name).
*/
FIRFirestoreErrorCodeInvalidArgument = 3,
/**
* Deadline expired before operation could complete. For operations that change the state of the
* system, this error may be returned even if the operation has completed successfully. For
* example, a successful response from a server could have been delayed long enough for the
* deadline to expire.
*/
FIRFirestoreErrorCodeDeadlineExceeded = 4,
/** Some requested document was not found. */
FIRFirestoreErrorCodeNotFound = 5,
/** Some document that we attempted to create already exists. */
FIRFirestoreErrorCodeAlreadyExists = 6,
/** The caller does not have permission to execute the specified operation. */
FIRFirestoreErrorCodePermissionDenied = 7,
/**
* Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system
* is out of space.
*/
FIRFirestoreErrorCodeResourceExhausted = 8,
/**
* Operation was rejected because the system is not in a state required for the operation's
* execution.
*/
FIRFirestoreErrorCodeFailedPrecondition = 9,
/**
* The operation was aborted, typically due to a concurrency issue like transaction aborts, etc.
*/
FIRFirestoreErrorCodeAborted = 10,
/** Operation was attempted past the valid range. */
FIRFirestoreErrorCodeOutOfRange = 11,
/** Operation is not implemented or not supported/enabled. */
FIRFirestoreErrorCodeUnimplemented = 12,
/**
* Internal errors. Means some invariants expected by underlying system has been broken. If you
* see one of these errors, something is very broken.
*/
FIRFirestoreErrorCodeInternal = 13,
/**
* The service is currently unavailable. This is a most likely a transient condition and may be
* corrected by retrying with a backoff.
*/
FIRFirestoreErrorCodeUnavailable = 14,
/** Unrecoverable data loss or corruption. */
FIRFirestoreErrorCodeDataLoss = 15,
/** The request does not have valid authentication credentials for the operation. */
FIRFirestoreErrorCodeUnauthenticated = 16} NS_SWIFT_NAME(FirestoreErrorCode);
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2017 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 FIRLocalCacheSettings;
/** Used to set on-disk cache size to unlimited. Garbage collection will not run. */
FOUNDATION_EXTERN const int64_t
kFIRFirestoreCacheSizeUnlimited NS_SWIFT_NAME(FirestoreCacheSizeUnlimited);
/** Settings used to configure a `Firestore` instance. */
NS_SWIFT_NAME(FirestoreSettings)
@interface FIRFirestoreSettings : NSObject <NSCopying>
/**
* Creates and returns an empty `FirestoreSettings` object.
*
* @return The created `FirestoreSettings` object.
*/
- (instancetype)init NS_DESIGNATED_INITIALIZER;
/** The hostname to connect to. */
@property(nonatomic, copy) NSString* host;
/** Whether to use SSL when connecting. */
@property(nonatomic, getter=isSSLEnabled) BOOL sslEnabled;
/**
* A dispatch queue to be used to execute all completion handlers and event handlers. By default,
* the main queue is used.
*/
@property(nonatomic, strong) dispatch_queue_t dispatchQueue;
/**
* NOTE: This field will be deprecated in a future major release. Use the `cacheSettings` field
* instead to specify cache type, and other cache configurations.
*
* Set to false to disable local persistent storage.
*/
@property(nonatomic, getter=isPersistenceEnabled) BOOL persistenceEnabled DEPRECATED_MSG_ATTRIBUTE(
"This field is deprecated. Use `cacheSettings` instead.");
/**
* NOTE: This field will be deprecated in a future major release. Use the `cacheSettings` field
* instead to specify cache size, and other cache configurations.
*
* Sets the cache size threshold above which the SDK will attempt to collect least-recently-used
* documents. The size is not a guarantee that the cache will stay below that size, only that if
* the cache exceeds the given size, cleanup will be attempted. Cannot be set lower than 1MB.
*
* Set to `FirestoreCacheSizeUnlimited` to disable garbage collection entirely.
*/
@property(nonatomic, assign) int64_t cacheSizeBytes DEPRECATED_MSG_ATTRIBUTE(
"This field is deprecated. Use `cacheSettings` instead.");
/**
* Specifies the cache used by the SDK. Available options are `PersistentCacheSettings`
* and `MemoryCacheSettings`, each with different configuration options.
*
* When unspecified, `PersistentCacheSettings` will be used by default.
*
* NOTE: setting this field and `cacheSizeBytes` or `persistenceEnabled` at the same time will throw
* an exception during SDK initialization. Instead, use the configuration in
* the `PersistentCacheSettings` object to specify the cache size.
*/
@property(nonatomic, strong) id<FIRLocalCacheSettings, NSObject> cacheSettings;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,53 @@
/*
* 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>
/**
* An enum that configures the behavior of `DocumentReference.getDocument()` and
* `Query.getDocuments()`. By providing a source enum the `getDocument[s]`
* methods can be configured to fetch results only from the server, only from
* the local cache, or attempt to fetch results from the server and fall back to
* the cache (which is the default).
*/
typedef NS_ENUM(NSUInteger, FIRFirestoreSource) {
/**
* Causes Firestore to try to retrieve an up-to-date (server-retrieved)
* snapshot, but fall back to returning cached data if the server can't be
* reached.
*/
FIRFirestoreSourceDefault,
/**
* Causes Firestore to avoid the cache, generating an error if the server
* cannot be reached. Note that the cache will still be updated if the
* server request succeeds. Also note that latency-compensation still takes
* effect, so any pending write operations will be visible in the returned
* data (merged into the server-provided data).
*/
FIRFirestoreSourceServer,
/**
* Causes Firestore to immediately return a value from the cache, ignoring
* the server completely (implying that the returned value may be stale with
* respect to the value on the server). If there is no data in the cache to
* satisfy the `getDocument[s]` call, `DocumentReference.getDocument()` will
* return an error and `QuerySnapshot.getDocuments()` will return an empty
* `QuerySnapshot` with no documents.
*/
FIRFirestoreSourceCache
} NS_SWIFT_NAME(FirestoreSource);

View File

@@ -0,0 +1,54 @@
/*
* 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
/**
* An immutable object representing a geographical point in Firestore. The point is represented as
* a latitude/longitude pair.
*
* Latitude values are in the range of [-90, 90].
* Longitude values are in the range of [-180, 180].
*/
NS_SWIFT_NAME(GeoPoint)
@interface FIRGeoPoint : NSObject <NSCopying>
/** :nodoc: */
- (instancetype)init NS_UNAVAILABLE;
/**
* Creates a `GeoPoint` from the provided latitude and longitude degrees.
* @param latitude The latitude as number between -90 and 90.
* @param longitude The longitude as number between -180 and 180.
*/
- (instancetype)initWithLatitude:(double)latitude
longitude:(double)longitude NS_DESIGNATED_INITIALIZER;
/**
* The point's latitude. Must be a value between -90 and 90 (inclusive).
*/
@property(nonatomic, readonly) double latitude;
/**
* The point's longitude. Must be a value between -180 and 180 (inclusive).
*/
@property(nonatomic, readonly) double longitude;
@end
NS_ASSUME_NONNULL_END

View File

@@ -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>
NS_ASSUME_NONNULL_BEGIN
/** Represents a listener that can be removed by calling remove. */
NS_SWIFT_NAME(ListenerRegistration)
@protocol FIRListenerRegistration <NSObject>
/**
* Removes the listener being tracked by this `ListenerRegistration`. After the initial call,
* subsequent calls have no effect.
*/
- (void)remove;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,91 @@
/*
* Copyright 2021 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
/**
* Represents the state of bundle loading tasks.
*
* Both `error` and `inProgress` are final states: the task will be in either an aborted or
* completed state and there will be no more subsequent updates.
*/
typedef NS_ENUM(NSInteger, FIRLoadBundleTaskState) {
FIRLoadBundleTaskStateError,
FIRLoadBundleTaskStateInProgress,
FIRLoadBundleTaskStateSuccess,
} NS_SWIFT_NAME(LoadBundleTaskState);
/** Represents a progress update or a final state from loading bundles. */
NS_SWIFT_NAME(LoadBundleTaskProgress)
@interface FIRLoadBundleTaskProgress : NSObject
/** How many documents have been loaded. */
@property(readonly, nonatomic) NSInteger documentsLoaded;
/** The total number of documents in the bundle. 0 if the bundle failed to parse. */
@property(readonly, nonatomic) NSInteger totalDocuments;
/** How many bytes have been loaded. */
@property(readonly, nonatomic) NSInteger bytesLoaded;
/** The total number of bytes in the bundle. 0 if the bundle failed to parse. */
@property(readonly, nonatomic) NSInteger totalBytes;
/** The current state of `LoadBundleTask`. */
@property(readonly, nonatomic) FIRLoadBundleTaskState state;
@end
/** A handle associated with registered observers that can be used to remove them. */
typedef NSInteger FIRLoadBundleObserverHandle NS_SWIFT_NAME(LoadBundleObserverHandle);
/**
* Represents the task of loading a Firestore bundle. Observers can be registered with this task to
* observe the bundle loading progress, as well as task completion and error events.
*/
NS_SWIFT_NAME(LoadBundleTask)
@interface FIRLoadBundleTask : NSObject
/**
* Registers an observer to observe the progress updates, completion or error events.
*
* @return A handle to the registered observer which can be used to remove the observer once it is
* no longer needed.
*/
- (FIRLoadBundleObserverHandle)addObserver:(void (^)(FIRLoadBundleTaskProgress *progress))observer
NS_SWIFT_NAME(addObserver(_:));
/**
* Removes a registered observer associated with the given handle. If no observer can be found, this
* will be a no-op.
*/
- (void)removeObserverWithHandle:(FIRLoadBundleObserverHandle)handle
NS_SWIFT_NAME(removeObserverWith(handle:));
/**
* Removes all registered observers for this task.
*/
- (void)removeAllObservers NS_SWIFT_NAME(removeAllObservers());
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,142 @@
/*
* Copyright 2023 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
/**
* Marker protocol implemented by all supported cache settings.
*
* The two cache types supported are `PersistentCacheSettings` and `MemoryCacheSettings`. Custom
* implementation is not supported.
*/
NS_SWIFT_NAME(LocalCacheSettings)
@protocol FIRLocalCacheSettings
@end
/**
* Configures the SDK to use a persistent cache. Firestore documents and mutations are persisted
* across App restart.
*
* This is the default cache type unless explicitly specified otherwise.
*
* To use, create an instance using one of the initializers, then set the instance to
* `FirestoreSettings.cacheSettings`, and use `FirestoreSettings` instance to configure Firestore
* SDK.
*/
NS_SWIFT_NAME(PersistentCacheSettings)
@interface FIRPersistentCacheSettings : NSObject <NSCopying, FIRLocalCacheSettings>
/**
* Creates `PersistentCacheSettings` with default cache size: 100MB.
*
* The cache size is not a hard limit, but a target for the SDK's gabarge collector to work towards.
*/
- (instancetype)init;
/**
* Creates `PersistentCacheSettings` with a custom cache size in bytes.
*
* The cache size is not a hard limit, but a target for the SDK's gabarge collector to work towards.
*/
- (instancetype)initWithSizeBytes:(NSNumber *)size;
@end
/**
* Marker protocol implemented by all supported garbage collector settings.
*
* The two cache types supported are `MemoryEagerGCSettings` and `MemoryLRUGCSettings`. Custom
* implementation is not supported.
*/
NS_SWIFT_NAME(MemoryGarbageCollectorSettings)
@protocol FIRMemoryGarbageCollectorSettings
@end
/**
* Configures the SDK to use an eager garbage collector for memory cache.
*
* Once configured, the SDK will remove any Firestore documents from memory as soon as they are not
* used by any active queries.
*
* To use, create an instance using the initializer, then initialize
* `MemoryCacheSettings` with this instance. This is the default garbage collector, so alternatively
* you can use the default initializer of `MemoryCacheSettings`.
*/
NS_SWIFT_NAME(MemoryEagerGCSetting)
@interface FIRMemoryEagerGCSettings : NSObject <NSCopying, FIRMemoryGarbageCollectorSettings>
/**
* Creates an instance of `MemoryEagerGCSettings`.
*/
- (instancetype)init;
@end
/**
* Configures the SDK to use a least-recently-used garbage collector for memory cache.
*
* Once configured, the SDK will attempt to remove documents that are least recently used in
* batches, if the current cache size is larger than the given target cache size. Default cache size
* is 100MB.
*
* To use, create an instance using one of the initializers, then initialize
* `MemoryCacheSettings` with this instance.
*/
NS_SWIFT_NAME(MemoryLRUGCSettings)
@interface FIRMemoryLRUGCSettings : NSObject <NSCopying, FIRMemoryGarbageCollectorSettings>
/**
* Creates an instance of `FIRMemoryLRUGCSettings`, with default target cache size 100MB. The SDK
* will run garbage collection if the current cache size is larger than 100MB.
*/
- (instancetype)init;
/**
* Creates an instance of `FIRMemoryLRUGCSettings`, with a custom target cache size. The SDK will
* run garbage collection if the current cache size is larger than the given size.
*/
- (instancetype)initWithSizeBytes:(NSNumber *)size;
@end
/**
* Configures the SDK to use a memory cache. Firestore documents and mutations are NOT persisted
* across App restart.
*
* To use, create an instance using one of the initializer, then set the instance to
* `FirestoreSettings.cacheSettings`, and use `FirestoreSettings` instance to configure Firestore
* SDK.
*/
NS_SWIFT_NAME(MemoryCacheSettings)
@interface FIRMemoryCacheSettings : NSObject <NSCopying, FIRLocalCacheSettings>
/**
* Creates an instance of `MemoryCacheSettings`.
*/
- (instancetype)init;
/**
* Creates an instance of `MemoryCacheSettings` with given `MemoryGarbageCollectorSettings` to
* custom the gabarge collector.
*/
- (instancetype)initWithGarbageCollectorSettings:
(id<FIRMemoryGarbageCollectorSettings, NSObject>)settings;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2023 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
/**
* A PersistentCacheIndexManager, for configuring persistent cache indexes used for local query
* execution.
*/
NS_SWIFT_NAME(PersistentCacheIndexManager)
@interface FIRPersistentCacheIndexManager : NSObject
/** :nodoc: */
- (instancetype)init
__attribute__((unavailable("FIRPersistentCacheIndexManager cannot be created directly.")));
/**
* Enables the SDK to create persistent cache indexes automatically for local query execution when
* the SDK believes cache indexes can improve performance.
*
* This feature is disabled by default.
*/
- (void)enableIndexAutoCreation NS_SWIFT_NAME(enableIndexAutoCreation());
/**
* Stops creating persistent cache indexes automatically for local query execution. The indexes
* which have been created by calling `enableIndexAutoCreation` still take effect.
*/
- (void)disableIndexAutoCreation NS_SWIFT_NAME(disableIndexAutoCreation());
/**
* Removes all persistent cache indexes. Please note this function also deletes indexes generated by
* [[FIRFirestore firestore] setIndexConfigurationFromJSON] and [[FIRFirestore firestore]
* setIndexConfigurationFromStream], which are deprecated.
*/
- (void)deleteAllIndexes NS_SWIFT_NAME(deleteAllIndexes());
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,604 @@
/*
* 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 "FIRFirestoreSource.h"
#import "FIRListenerRegistration.h"
#import "FIRSnapshotListenOptions.h"
@class FIRAggregateQuery;
@class FIRAggregateField;
@class FIRFieldPath;
@class FIRFirestore;
@class FIRFilter;
@class FIRQuerySnapshot;
@class FIRDocumentSnapshot;
NS_ASSUME_NONNULL_BEGIN
/**
* A block type used to handle failable snapshot method callbacks.
*/
typedef void (^FIRQuerySnapshotBlock)(FIRQuerySnapshot *_Nullable snapshot,
NSError *_Nullable error)
NS_SWIFT_UNAVAILABLE("Use Swift's closure syntax instead.");
/**
* A `Query` refers to a query which you can read or listen to. You can also construct
* refined `Query` objects by adding filters and ordering.
*/
NS_SWIFT_NAME(Query)
@interface FIRQuery : NSObject
/** :nodoc: */
- (id)init __attribute__((unavailable("FIRQuery cannot be created directly.")));
/** The `Firestore` instance that created this query (useful for performing transactions, etc.). */
@property(nonatomic, strong, readonly) FIRFirestore *firestore;
#pragma mark - Retrieving Data
/**
* Reads the documents matching this query.
*
* This method attempts to provide up-to-date data when possible by waiting for
* data from the server, but it may return cached data or fail if you are
* offline and the server cannot be reached. See the
* `getDocuments(source:completion:)` method to change this behavior.
*
* @param completion a block to execute once the documents have been successfully read.
* documentSet will be `nil` only if error is `non-nil`.
*/
- (void)getDocumentsWithCompletion:
(void (^)(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error))completion
NS_SWIFT_NAME(getDocuments(completion:));
/**
* Reads the documents matching this query.
*
* @param source indicates whether the results should be fetched from the cache
* only (`Source.cache`), the server only (`Source.server`), or to attempt
* the server and fall back to the cache (`Source.default`).
* @param completion a block to execute once the documents have been successfully read.
* documentSet will be `nil` only if error is `non-nil`.
*/
- (void)getDocumentsWithSource:(FIRFirestoreSource)source
completion:(void (^)(FIRQuerySnapshot *_Nullable snapshot,
NSError *_Nullable error))completion
NS_SWIFT_NAME(getDocuments(source:completion:));
/**
* Attaches a listener for `QuerySnapshot` events.
*
* @param listener The listener to attach.
*
* @return A `ListenerRegistration` object that can be used to remove this listener.
*/
- (id<FIRListenerRegistration>)addSnapshotListener:
(void (^)(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error))listener
NS_SWIFT_NAME(addSnapshotListener(_:));
/**
* Attaches a listener for `QuerySnapshot` events.
*
* @param includeMetadataChanges Whether metadata-only changes (i.e. only
* `DocumentSnapshot.metadata` changed) should trigger snapshot events.
* @param listener The listener to attach.
*
* @return A `ListenerRegistration` that can be used to remove this listener.
*/
- (id<FIRListenerRegistration>)
addSnapshotListenerWithIncludeMetadataChanges:(BOOL)includeMetadataChanges
listener:(void (^)(FIRQuerySnapshot *_Nullable snapshot,
NSError *_Nullable error))listener
NS_SWIFT_NAME(addSnapshotListener(includeMetadataChanges:listener:));
/**
* Attaches a listener for `QuerySnapshot` events.
* @param options Sets snapshot listener options, including whether metadata-only changes should
* trigger snapshot events, the source to listen to, the executor to use to call the
* listener, or the activity to scope the listener to.
* @param listener The listener to attach.
*
* @return A `ListenerRegistration` that can be used to remove this listener.
*/
- (id<FIRListenerRegistration>)
addSnapshotListenerWithOptions:(FIRSnapshotListenOptions *)options
listener:(void (^)(FIRQuerySnapshot *_Nullable snapshot,
NSError *_Nullable error))listener
NS_SWIFT_NAME(addSnapshotListener(options:listener:));
#pragma mark - Filtering Data
/**
* Creates and returns a new Query with the additional filter.
*
* @param filter The new filter to apply to the existing query.
* @return The newly created Query.
*/
- (FIRQuery *)queryWhereFilter:(FIRFilter *)filter NS_SWIFT_NAME(whereFilter(_:));
/**
* Creates and returns a new `Query` with the additional filter that documents must
* contain the specified field and the value must be equal to the specified value.
*
* @param field The name of the field to compare.
* @param value The value the field must be equal to.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereField:(NSString *)field
isEqualTo:(id)value NS_SWIFT_NAME(whereField(_:isEqualTo:));
/**
* Creates and returns a new `Query` with the additional filter that documents must
* contain the specified field and the value does not equal the specified value.
*
* @param path The path of the field to compare.
* @param value The value the field must be equal to.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path
isNotEqualTo:(id)value NS_SWIFT_NAME(whereField(_:isNotEqualTo:));
/**
* Creates and returns a new `Query` with the additional filter that documents must
* contain the specified field and the value does not equal the specified value.
*
* @param field The name of the field to compare.
* @param value The value the field must be equal to.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereField:(NSString *)field
isNotEqualTo:(id)value NS_SWIFT_NAME(whereField(_:isNotEqualTo:));
/**
* Creates and returns a new `Query` with the additional filter that documents must
* contain the specified field and the value must be equal to the specified value.
*
* @param path The path of the field to compare.
* @param value The value the field must be equal to.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path
isEqualTo:(id)value NS_SWIFT_NAME(whereField(_:isEqualTo:));
/**
* Creates and returns a new `Query` with the additional filter that documents must
* contain the specified field and the value must be less than the specified value.
*
* @param field The name of the field to compare.
* @param value The value the field must be less than.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereField:(NSString *)field
isLessThan:(id)value NS_SWIFT_NAME(whereField(_:isLessThan:));
/**
* Creates and returns a new `Query` with the additional filter that documents must
* contain the specified field and the value must be less than the specified value.
*
* @param path The path of the field to compare.
* @param value The value the field must be less than.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path
isLessThan:(id)value NS_SWIFT_NAME(whereField(_:isLessThan:));
/**
* Creates and returns a new `Query` with the additional filter that documents must
* contain the specified field and the value must be less than or equal to the specified value.
*
* @param field The name of the field to compare
* @param value The value the field must be less than or equal to.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereField:(NSString *)field
isLessThanOrEqualTo:(id)value NS_SWIFT_NAME(whereField(_:isLessThanOrEqualTo:));
/**
* Creates and returns a new `Query` with the additional filter that documents must
* contain the specified field and the value must be less than or equal to the specified value.
*
* @param path The path of the field to compare
* @param value The value the field must be less than or equal to.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path
isLessThanOrEqualTo:(id)value NS_SWIFT_NAME(whereField(_:isLessThanOrEqualTo:));
/**
* Creates and returns a new `Query` with the additional filter that documents must
* contain the specified field and the value must greater than the specified value.
*
* @param field The name of the field to compare
* @param value The value the field must be greater than.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereField:(NSString *)field
isGreaterThan:(id)value NS_SWIFT_NAME(whereField(_:isGreaterThan:));
/**
* Creates and returns a new `Query` with the additional filter that documents must
* contain the specified field and the value must greater than the specified value.
*
* @param path The path of the field to compare
* @param value The value the field must be greater than.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path
isGreaterThan:(id)value NS_SWIFT_NAME(whereField(_:isGreaterThan:));
/**
* Creates and returns a new `Query` with the additional filter that documents must
* contain the specified field and the value must be greater than or equal to the specified value.
*
* @param field The name of the field to compare
* @param value The value the field must be greater than.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereField:(NSString *)field
isGreaterThanOrEqualTo:(id)value NS_SWIFT_NAME(whereField(_:isGreaterThanOrEqualTo:));
/**
* Creates and returns a new `Query` with the additional filter that documents must
* contain the specified field and the value must be greater than or equal to the specified value.
*
* @param path The path of the field to compare
* @param value The value the field must be greater than.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path
isGreaterThanOrEqualTo:(id)value NS_SWIFT_NAME(whereField(_:isGreaterThanOrEqualTo:));
/**
* Creates and returns a new `Query` with the additional filter that documents must contain
* the specified field, it must be an array, and the array must contain the provided value.
*
* A query can have only one `arrayContains` filter.
*
* @param field The name of the field containing an array to search
* @param value The value that must be contained in the array
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereField:(NSString *)field
arrayContains:(id)value NS_SWIFT_NAME(whereField(_:arrayContains:));
/**
* Creates and returns a new `Query` with the additional filter that documents must contain
* the specified field, it must be an array, and the array must contain the provided value.
*
* A query can have only one `arrayContains` filter.
*
* @param path The path of the field containing an array to search
* @param value The value that must be contained in the array
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path
arrayContains:(id)value NS_SWIFT_NAME(whereField(_:arrayContains:));
/**
* Creates and returns a new `Query` with the additional filter that documents must contain
* the specified field, the value must be an array, and that array must contain at least one value
* from the provided array.
*
* A query can have only one `arrayContainsAny` filter and it cannot be combined with
* `arrayContains` or `in` filters.
*
* @param field The name of the field containing an array to search.
* @param values The array that contains the values to match.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereField:(NSString *)field
arrayContainsAny:(NSArray<id> *)values NS_SWIFT_NAME(whereField(_:arrayContainsAny:));
/**
* Creates and returns a new `Query` with the additional filter that documents must contain
* the specified field, the value must be an array, and that array must contain at least one value
* from the provided array.
*
* A query can have only one `arrayContainsAny` filter and it cannot be combined with
* `arrayContains` or `in` filters.
*
* @param path The path of the field containing an array to search.
* @param values The array that contains the values to match.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path
arrayContainsAny:(NSArray<id> *)values
NS_SWIFT_NAME(whereField(_:arrayContainsAny:));
/**
* Creates and returns a new `Query` with the additional filter that documents must contain
* the specified field and the value must equal one of the values from the provided array.
*
* A query can have only one `in` filter, and it cannot be combined with an `arrayContainsAny`
* filter.
*
* @param field The name of the field to search.
* @param values The array that contains the values to match.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereField:(NSString *)field
in:(NSArray<id> *)values NS_SWIFT_NAME(whereField(_:in:));
/**
* Creates and returns a new `Query` with the additional filter that documents must contain
* the specified field and the value must equal one of the values from the provided array.
*
* A query can have only one `in` filter, and it cannot be combined with an `arrayContainsAny`
* filter.
*
* @param path The path of the field to search.
* @param values The array that contains the values to match.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path
in:(NSArray<id> *)values NS_SWIFT_NAME(whereField(_:in:));
/**
* Creates and returns a new `Query` with the additional filter that documents must contain
* the specified field and the value does not equal any of the values from the provided array.
*
* One special case is that `notIn` filters cannot match `nil` values. To query for documents
* where a field exists and is `nil`, use a `notEqual` filter, which can handle this special case.
*
* A query can have only one `notIn` filter, and it cannot be combined with an `arrayContains`,
* `arrayContainsAny`, `in`, or `notEqual` filter.
*
* @param field The name of the field to search.
* @param values The array that contains the values to match.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereField:(NSString *)field
notIn:(NSArray<id> *)values NS_SWIFT_NAME(whereField(_:notIn:));
/**
* Creates and returns a new `Query` with the additional filter that documents must contain
* the specified field and the value does not equal any of the values from the provided array.
*
* One special case is that `notIn` filters cannot match `nil` values. To query for documents
* where a field exists and is `nil`, use a `notEqual` filter, which can handle this special case.
*
* Passing in a `null` value into the `values` array results in no document matches. To query
* for documents where a field is not `null`, use a `notEqual` filter.
*
* @param path The path of the field to search.
* @param values The array that contains the values to match.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryWhereFieldPath:(FIRFieldPath *)path
notIn:(NSArray<id> *)values NS_SWIFT_NAME(whereField(_:notIn:));
/**
* Creates and returns a new `Query` with the additional filter that documents must
* satisfy the specified predicate.
*
* @param predicate The predicate the document must satisfy. Can be either comparison
* or compound of comparison. In particular, block-based predicate is not supported.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryFilteredUsingPredicate:(NSPredicate *)predicate NS_SWIFT_NAME(filter(using:));
#pragma mark - Sorting Data
/**
* Creates and returns a new `Query` that's additionally sorted by the specified field.
*
* @param field The field to sort by.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryOrderedByField:(NSString *)field NS_SWIFT_NAME(order(by:));
/**
* Creates and returns a new `Query` that's additionally sorted by the specified field.
*
* @param path The field to sort by.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryOrderedByFieldPath:(FIRFieldPath *)path NS_SWIFT_NAME(order(by:));
/**
* Creates and returns a new `Query` that's additionally sorted by the specified field,
* optionally in descending order instead of ascending.
*
* @param field The field to sort by.
* @param descending Whether to sort descending.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryOrderedByField:(NSString *)field
descending:(BOOL)descending NS_SWIFT_NAME(order(by:descending:));
/**
* Creates and returns a new `Query` that's additionally sorted by the specified field,
* optionally in descending order instead of ascending.
*
* @param path The field to sort by.
* @param descending Whether to sort descending.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryOrderedByFieldPath:(FIRFieldPath *)path
descending:(BOOL)descending NS_SWIFT_NAME(order(by:descending:));
#pragma mark - Limiting Data
/**
* Creates and returns a new `Query` that only returns the first matching documents up to
* the specified number.
*
* @param limit The maximum number of items to return.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryLimitedTo:(NSInteger)limit NS_SWIFT_NAME(limit(to:));
/**
* Creates and returns a new `Query` that only returns the last matching documents up to
* the specified number.
*
* A query with a `limit(toLast:)` clause must have at least one `orderBy` clause.
*
* @param limit The maximum number of items to return.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryLimitedToLast:(NSInteger)limit NS_SWIFT_NAME(limit(toLast:));
#pragma mark - Choosing Endpoints
/**
* Creates and returns a new `Query` that starts at the provided document (inclusive). The
* starting position is relative to the order of the query. The document must contain all of the
* fields provided in the orderBy of this query.
*
* @param document The snapshot of the document to start at.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryStartingAtDocument:(FIRDocumentSnapshot *)document
NS_SWIFT_NAME(start(atDocument:));
/**
* Creates and returns a new `Query` that starts at the provided fields relative to the order of
* the query. The order of the field values must match the order of the order by clauses of the
* query.
*
* @param fieldValues The field values to start this query at, in order of the query's order by.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryStartingAtValues:(NSArray *)fieldValues NS_SWIFT_NAME(start(at:));
/**
* Creates and returns a new `Query` that starts after the provided document (exclusive). The
* starting position is relative to the order of the query. The document must contain all of the
* fields provided in the orderBy of this query.
*
* @param document The snapshot of the document to start after.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryStartingAfterDocument:(FIRDocumentSnapshot *)document
NS_SWIFT_NAME(start(afterDocument:));
/**
* Creates and returns a new `Query` that starts after the provided fields relative to the order
* of the query. The order of the field values must match the order of the order by clauses of the
* query.
*
* @param fieldValues The field values to start this query after, in order of the query's orderBy.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryStartingAfterValues:(NSArray *)fieldValues NS_SWIFT_NAME(start(after:));
/**
* Creates and returns a new `Query` that ends before the provided document (exclusive). The end
* position is relative to the order of the query. The document must contain all of the fields
* provided in the orderBy of this query.
*
* @param document The snapshot of the document to end before.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryEndingBeforeDocument:(FIRDocumentSnapshot *)document
NS_SWIFT_NAME(end(beforeDocument:));
/**
* Creates and returns a new `Query` that ends before the provided fields relative to the order
* of the query. The order of the field values must match the order of the order by clauses of the
* query.
*
* @param fieldValues The field values to end this query before, in order of the query's order by.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryEndingBeforeValues:(NSArray *)fieldValues NS_SWIFT_NAME(end(before:));
/**
* Creates and returns a new `Query` that ends at the provided document (exclusive). The end
* position is relative to the order of the query. The document must contain all of the fields
* provided in the orderBy of this query.
*
* @param document The snapshot of the document to end at.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryEndingAtDocument:(FIRDocumentSnapshot *)document NS_SWIFT_NAME(end(atDocument:));
/**
* Creates and returns a new `Query` that ends at the provided fields relative to the order of
* the query. The order of the field values must match the order of the order by clauses of the
* query.
*
* @param fieldValues The field values to end this query at, in order of the query's order by.
*
* @return The created `Query`.
*/
- (FIRQuery *)queryEndingAtValues:(NSArray *)fieldValues NS_SWIFT_NAME(end(at:));
#pragma mark - Aggregation
/**
* A query that counts the documents in the result set of this query without actually downloading
* the documents.
*
* Using this `AggregateQuery` to count the documents is efficient because only the final count, not
* the documents' data, is downloaded. The `AggregateQuery` can count the documents in cases where
* the result set is prohibitively large to download entirely (thousands of documents).
*/
@property(nonatomic, readonly) FIRAggregateQuery *count;
/**
* Creates and returns a new `AggregateQuery` that aggregates the documents in the result set
* of this query without actually downloading the documents.
*
* Using an `AggregateQuery` to perform aggregations is efficient because only the final aggregation
* values, not the documents' data, is downloaded. The returned `AggregateQuery` can perform
* aggregations of the documents in cases where the result set is prohibitively large to download
* entirely (thousands of documents).
*
* @param aggregateFields Specifies the aggregate operations to perform on the result set of this
* query.
*
* @return An `AggregateQuery` encapsulating this `Query` and `AggregateField`s, which can be used
* to query the server for the aggregation results.
*/
- (FIRAggregateQuery *)aggregate:(NSArray<FIRAggregateField *> *)aggregateFields
NS_SWIFT_NAME(aggregate(_:));
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,73 @@
/*
* 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
@class FIRDocumentChange;
@class FIRQuery;
@class FIRQueryDocumentSnapshot;
@class FIRSnapshotMetadata;
/**
* A `QuerySnapshot` contains zero or more `DocumentSnapshot` objects. It can be enumerated
* using the `documents` property and its size can be inspected with `isEmpty` and
* `count`.
*/
NS_SWIFT_NAME(QuerySnapshot)
@interface FIRQuerySnapshot : NSObject
/** :nodoc: */
- (id)init __attribute__((unavailable("FIRQuerySnapshot cannot be created directly.")));
/**
* The query on which you called `getDocuments` or listened to in order to get this
* `QuerySnapshot`.
*/
@property(nonatomic, strong, readonly) FIRQuery *query;
/** Metadata about this snapshot, concerning its source and if it has local modifications. */
@property(nonatomic, strong, readonly) FIRSnapshotMetadata *metadata;
/** Indicates whether this `QuerySnapshot` is empty (contains no documents). */
@property(nonatomic, readonly, getter=isEmpty) BOOL empty;
/** The count of documents in this `QuerySnapshot`. */
@property(nonatomic, readonly) NSInteger count;
/** An Array of the `DocumentSnapshots` that make up this document set. */
@property(nonatomic, strong, readonly) NSArray<FIRQueryDocumentSnapshot *> *documents;
/**
* An array of the documents that changed since the last snapshot. If this is the first snapshot,
* all documents will be in the list as Added changes.
*/
@property(nonatomic, strong, readonly) NSArray<FIRDocumentChange *> *documentChanges;
/**
* Returns an array of the documents that changed since the last snapshot. If this is the first
* snapshot, all documents will be in the list as Added changes.
*
* @param includeMetadataChanges Whether metadata-only changes (i.e. only
* `DocumentSnapshot.metadata` changed) should be included.
*/
- (NSArray<FIRDocumentChange *> *)documentChangesWithIncludeMetadataChanges:
(BOOL)includeMetadataChanges NS_SWIFT_NAME(documentChanges(includeMetadataChanges:));
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2024 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
/**
* The source the snapshot listener retrieves data from.
*/
typedef NS_ENUM(NSUInteger, FIRListenSource) {
/**
* The default behavior. The listener attempts to return initial snapshot from cache and retrieve
* up-to-date snapshots from the Firestore server. Snapshot events will be triggered on local
* mutations and server-side updates.
*/
FIRListenSourceDefault,
/**
* The listener retrieves data and listens to updates from the local Firestore cache without
* attempting to send the query to the server. If some documents gets updated as a result from
* other queries, they will be picked up by listeners using the cache.
*
* Note that the data might be stale if the cache hasn't synchronized with recent server-side
* changes.
*/
FIRListenSourceCache
} NS_SWIFT_NAME(ListenSource);
/**
* Options to configure the behavior of `Firestore.addSnapshotListenerWithOptions()`. Instances
* of this class control settings like whether metadata-only changes trigger events and the
* preferred data source.
*/
NS_SWIFT_NAME(SnapshotListenOptions)
@interface FIRSnapshotListenOptions : NSObject
/** The source the snapshot listener retrieves data from. */
@property(nonatomic, readonly) FIRListenSource source;
/** Indicates whether metadata-only changes should trigger snapshot events. */
@property(nonatomic, readonly) BOOL includeMetadataChanges;
/**
* Creates and returns a new `SnapshotListenOptions` object with all properties initialized to their
* default values.
*
* @return The created `SnapshotListenOptions` object.
*/
- (instancetype)init NS_DESIGNATED_INITIALIZER;
/**
* Creates and returns a new `SnapshotListenOptions` object with with all properties of the current
* `SnapshotListenOptions` object plus the new property specifying whether metadata-only changes
* should trigger snapshot events
*
* @return The created `SnapshotListenOptions` object.
*/
- (FIRSnapshotListenOptions *)optionsWithIncludeMetadataChanges:(BOOL)includeMetadataChanges;
/**
* Creates and returns a new `SnapshotListenOptions` object with with all properties of the current
* `SnapshotListenOptions` object plus the new property specifying the source that the snapshot
* listener listens to.
*
* @return The created `SnapshotListenOptions` object.
*/
- (FIRSnapshotListenOptions *)optionsWithSource:(FIRListenSource)source;
@end
NS_ASSUME_NONNULL_END

View File

@@ -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>
NS_ASSUME_NONNULL_BEGIN
/** Metadata about a snapshot, describing the state of the snapshot. */
NS_SWIFT_NAME(SnapshotMetadata)
@interface FIRSnapshotMetadata : NSObject
/** :nodoc: */
- (instancetype)init NS_UNAVAILABLE;
/**
* Returns `true` if the snapshot contains the result of local writes (e.g. set() or update() calls)
* that have not yet been committed to the backend. If your listener has opted into metadata updates
* (via `includeMetadataChanges:true`) you will receive another snapshot with `hasPendingWrites`
* equal to `false` once the writes have been committed to the backend.
*/
@property(nonatomic, assign, readonly, getter=hasPendingWrites) BOOL pendingWrites;
/**
* Returns `true` if the snapshot was created from cached data rather than guaranteed up-to-date
* server data. If your listener has opted into metadata updates (via `includeMetadataChanges:true`)
* you will receive another snapshot with `isFromCache` equal to `false` once the client has
* received up-to-date data from the backend.
*/
@property(nonatomic, assign, readonly, getter=isFromCache) BOOL fromCache;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,89 @@
/*
* 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 Timestamp represents a point in time independent of any time zone or calendar, represented as
* seconds and fractions of seconds at nanosecond resolution in UTC Epoch time. It is encoded using
* the Proleptic Gregorian Calendar which extends the Gregorian calendar backwards to year one. It
* is encoded assuming all minutes are 60 seconds long, i.e. leap seconds are "smeared" so that no
* leap second table is needed for interpretation. Range is from 0001-01-01T00:00:00Z to
* 9999-12-31T23:59:59.999999999Z. By restricting to that range, we ensure that we can convert to
* and from RFC 3339 date strings.
*
* @see https://github.com/google/protobuf/blob/main/src/google/protobuf/timestamp.proto for the
* reference timestamp definition.
*/
NS_SWIFT_NAME(Timestamp)
@interface FIRTimestamp : NSObject <NSCopying>
/** :nodoc: */
- (instancetype)init NS_UNAVAILABLE;
/**
* Creates a new timestamp.
*
* @param seconds the number of seconds since epoch.
* @param nanoseconds the number of nanoseconds after the seconds.
*/
- (instancetype)initWithSeconds:(int64_t)seconds
nanoseconds:(int32_t)nanoseconds NS_DESIGNATED_INITIALIZER;
/**
* Creates a new timestamp.
*
* @param seconds the number of seconds since epoch.
* @param nanoseconds the number of nanoseconds after the seconds.
*/
+ (instancetype)timestampWithSeconds:(int64_t)seconds nanoseconds:(int32_t)nanoseconds;
/** Creates a new timestamp from the given date. */
+ (instancetype)timestampWithDate:(NSDate *)date;
/** Creates a new timestamp with the current date / time. */
+ (instancetype)timestamp;
/** Returns a new `Date` corresponding to this timestamp. This may lose precision. */
- (NSDate *)dateValue;
/**
* Returns the result of comparing the receiver with another timestamp.
* @param other the other timestamp to compare.
* @return `orderedAscending` if `other` is chronologically following self,
* `orderedDescending` if `other` is chronologically preceding self,
* `orderedSame` otherwise.
*/
- (NSComparisonResult)compare:(FIRTimestamp *)other;
/**
* Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z.
* Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.
*/
@property(nonatomic, assign, readonly) int64_t seconds;
/**
* Non-negative fractions of a second at nanosecond resolution. Negative second values with
* fractions must still have non-negative nanos values that count forward in time.
* Must be from 0 to 999,999,999 inclusive.
*/
@property(nonatomic, assign, readonly) int32_t nanoseconds;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class FIRDocumentReference;
@class FIRDocumentSnapshot;
/**
* `Transaction` provides methods to read and write data within a transaction.
*
* @see `Firestore.runTransaction(_:)`
*/
NS_SWIFT_NAME(Transaction)
@interface FIRTransaction : NSObject
/** :nodoc: */
- (id)init __attribute__((unavailable("FIRTransaction cannot be created directly.")));
/**
* Writes to the document referred to by `document`. If the document doesn't yet exist,
* this method creates it and then sets the data. If the document exists, this method overwrites
* the document data with the new values.
*
* @param data A `Dictionary` that contains the fields and data to write to the document.
* @param document A reference to the document whose data should be overwritten.
* @return This `Transaction` instance. Used for chaining method calls.
*/
// clang-format off
- (FIRTransaction *)setData:(NSDictionary<NSString *, id> *)data
forDocument:(FIRDocumentReference *)document
NS_SWIFT_NAME(setData(_:forDocument:));
// clang-format on
/**
* Writes to the document referred to by `document`. If the document doesn't yet exist,
* this method creates it and then sets the data. If you pass `merge:true`, the provided data will
* be merged into any existing document.
*
* @param data A `Dictionary` that contains the fields and data to write to the document.
* @param document A reference to the document whose data should be overwritten.
* @param merge Whether to merge the provided data into any existing document. If enabled,
* all omitted fields remain untouched. If your input sets any field to an empty dictionary, any
* nested field is overwritten.
* @return This `Transaction` instance. Used for chaining method calls.
*/
// clang-format off
- (FIRTransaction *)setData:(NSDictionary<NSString *, id> *)data
forDocument:(FIRDocumentReference *)document
merge:(BOOL)merge
NS_SWIFT_NAME(setData(_:forDocument:merge:));
// clang-format on
/**
* Writes to the document referred to by `document` and only replace the fields
* specified under `mergeFields`. Any field that is not specified in `mergeFields`
* is ignored and remains untouched. If the document doesn't yet exist,
* this method creates it and then sets the data.
*
* It is an error to include a field in `mergeFields` that does not have a corresponding
* value in the `data` dictionary.
*
* @param data A `Dictionary` containing the fields that make up the document
* to be written.
* @param document A reference to the document whose data should be overwritten.
* @param mergeFields An `Array` that contains a list of `String` or `FieldPath` elements
* specifying which fields to merge. Fields can contain dots to reference nested fields within
* the document. If your input sets any field to an empty dictionary, any nested field is
* overwritten.
* @return This `Transaction` instance. Used for chaining method calls.
*/
// clang-format off
- (FIRTransaction *)setData:(NSDictionary<NSString *, id> *)data
forDocument:(FIRDocumentReference *)document
mergeFields:(NSArray<id> *)mergeFields
NS_SWIFT_NAME(setData(_:forDocument:mergeFields:));
// clang-format on
/**
* Updates fields in the document referred to by `document`.
* If the document does not exist, the transaction will fail.
*
* @param fields A `Dictionary` containing the fields (expressed as an `String` or
* `FieldPath`) and values with which to update the document.
* @param document A reference to the document whose data should be updated.
* @return This `Transaction` instance. Used for chaining method calls.
*/
// clang-format off
- (FIRTransaction *)updateData:(NSDictionary<id, id> *)fields
forDocument:(FIRDocumentReference *)document
NS_SWIFT_NAME(updateData(_:forDocument:));
// clang-format on
/**
* Deletes the document referred to by `document`.
*
* @param document A reference to the document that should be deleted.
* @return This `Transaction` instance. Used for chaining method calls.
*/
- (FIRTransaction *)deleteDocument:(FIRDocumentReference *)document
NS_SWIFT_NAME(deleteDocument(_:));
/**
* Reads the document referenced by `document`.
*
* @param document A reference to the document to be read.
* @param error An out parameter to capture an error, if one occurred.
*/
- (FIRDocumentSnapshot *_Nullable)getDocument:(FIRDocumentReference *)document
error:(NSError *__autoreleasing *)error
NS_SWIFT_NAME(getDocument(_:));
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2022 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
/**
* Options to customize the behavior of `Firestore.runTransactionWithOptions()`.
*/
NS_SWIFT_NAME(TransactionOptions)
@interface FIRTransactionOptions : NSObject <NSCopying>
/**
* Creates and returns a new `TransactionOptions` object with all properties initialized to their
* default values.
*
* @return The created `TransactionOptions` object.
*/
- (instancetype)init NS_DESIGNATED_INITIALIZER;
/** The maximum number of attempts to commit, after which transaction fails. Default is 5. */
@property(nonatomic, assign) NSInteger maxAttempts;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,138 @@
/*
* 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
@class FIRDocumentReference;
/**
* A write batch is used to perform multiple writes as a single atomic unit.
*
* A WriteBatch object can be acquired by calling `Firestore.batch()`. It provides methods for
* adding writes to the write batch. None of the writes will be committed (or visible locally)
* until `WriteBatch.commit()` is called.
*
* Unlike transactions, write batches are persisted offline and therefore are preferable when you
* don't need to condition your writes on read data.
*/
NS_SWIFT_NAME(WriteBatch)
@interface FIRWriteBatch : NSObject
/** :nodoc: */
- (id)init __attribute__((unavailable("FIRWriteBatch cannot be created directly.")));
/**
* Writes to the document referred to by `document`. If the document doesn't yet exist,
* this method creates it and then sets the data. If the document exists, this method overwrites
* the document data with the new values.
*
* @param data A `Dictionary` that contains the fields and data to write to the document.
* @param document A reference to the document whose data should be overwritten.
* @return This `WriteBatch` instance. Used for chaining method calls.
*/
// clang-format off
- (FIRWriteBatch *)setData:(NSDictionary<NSString *, id> *)data
forDocument:(FIRDocumentReference *)document NS_SWIFT_NAME(setData(_:forDocument:));
// clang-format on
/**
* Writes to the document referred to by `document`. If the document doesn't yet exist,
* this method creates it and then sets the data. If you pass `merge:true`, the provided data will
* be merged into any existing document.
*
* @param data A `Dictionary` that contains the fields and data to write to the document.
* @param document A reference to the document whose data should be overwritten.
* @param merge Whether to merge the provided data into any existing document. If enabled,
* all omitted fields remain untouched. If your input sets any field to an empty dictionary, any
* nested field is overwritten.
* @return This `WriteBatch` instance. Used for chaining method calls.
*/
// clang-format off
- (FIRWriteBatch *)setData:(NSDictionary<NSString *, id> *)data
forDocument:(FIRDocumentReference *)document
merge:(BOOL)merge
NS_SWIFT_NAME(setData(_:forDocument:merge:));
// clang-format on
/**
* Writes to the document referred to by `document` and only replace the fields
* specified under `mergeFields`. Any field that is not specified in `mergeFields`
* is ignored and remains untouched. If the document doesn't yet exist,
* this method creates it and then sets the data.
*
* It is an error to include a field in `mergeFields` that does not have a corresponding
* value in the `data` dictionary.
*
* @param data A `Dictionary` that contains the fields and data to write to the document.
* @param document A reference to the document whose data should be overwritten.
* @param mergeFields An `Array` that contains a list of `String` or `FieldPath` elements
* specifying which fields to merge. Fields can contain dots to reference nested fields within
* the document. If your input sets any field to an empty dictionary, any nested field is
* overwritten.
* @return This `WriteBatch` instance. Used for chaining method calls.
*/
// clang-format off
- (FIRWriteBatch *)setData:(NSDictionary<NSString *, id> *)data
forDocument:(FIRDocumentReference *)document
mergeFields:(NSArray<id> *)mergeFields
NS_SWIFT_NAME(setData(_:forDocument:mergeFields:));
// clang-format on
/**
* Updates fields in the document referred to by `document`.
* If document does not exist, the write batch will fail.
*
* @param fields A `Dictionary` containing the fields (expressed as an `String` or
* `FieldPath`) and values with which to update the document.
* @param document A reference to the document whose data should be updated.
* @return This `WriteBatch` instance. Used for chaining method calls.
*/
// clang-format off
- (FIRWriteBatch *)updateData:(NSDictionary<id, id> *)fields
forDocument:(FIRDocumentReference *)document
NS_SWIFT_NAME(updateData(_:forDocument:));
// clang-format on
/**
* Deletes the document referred to by `document`.
*
* @param document A reference to the document that should be deleted.
* @return This `WriteBatch` instance. Used for chaining method calls.
*/
- (FIRWriteBatch *)deleteDocument:(FIRDocumentReference *)document
NS_SWIFT_NAME(deleteDocument(_:));
/**
* Commits all of the writes in this write batch as a single atomic unit.
*/
- (void)commit;
/**
* Commits all of the writes in this write batch as a single atomic unit.
*
* @param completion A block to be called once all of the writes in the batch have been
* successfully written to the backend as an atomic unit. This block will only execute
* when the client is online and the commit has completed against the server. The
* completion handler will not be called when the device is offline, though local
* changes will be visible immediately.
*/
- (void)commitWithCompletion:(nullable void (^)(NSError *_Nullable error))completion;
@end
NS_ASSUME_NONNULL_END

View File

@@ -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 "FIRAggregateField.h"
#import "FIRAggregateQuery.h"
#import "FIRAggregateQuerySnapshot.h"
#import "FIRAggregateSource.h"
#import "FIRCollectionReference.h"
#import "FIRDocumentChange.h"
#import "FIRDocumentReference.h"
#import "FIRDocumentSnapshot.h"
#import "FIRFieldPath.h"
#import "FIRFieldValue.h"
#import "FIRFilter.h"
#import "FIRFirestore.h"
#import "FIRFirestoreErrors.h"
#import "FIRFirestoreSettings.h"
#import "FIRGeoPoint.h"
#import "FIRListenerRegistration.h"
#import "FIRLoadBundleTask.h"
#import "FIRLocalCacheSettings.h"
#import "FIRQuery.h"
#import "FIRQuerySnapshot.h"
#import "FIRSnapshotListenOptions.h"
#import "FIRSnapshotMetadata.h"
#import "FIRTimestamp.h"
#import "FIRTransaction.h"
#import "FIRTransactionOptions.h"
#import "FIRWriteBatch.h"

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyTracking</key>
<false/>
<key>NSPrivacyTrackingDomains</key>
<array>
</array>
<key>NSPrivacyCollectedDataTypes</key>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeOtherDiagnosticData</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAnalytics</string>
</array>
</dict>
</array>
<key>NSPrivacyAccessedAPITypes</key>
<array>
</array>
</dict>
</plist>