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