Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Firestore/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
- [feature] Queries can now be created from an NSPredicate.
- [added] Added SnapshotOptions API to control how DocumentSnapshots return unresolved
server timestamps.
- [changed] For non-existing documents, DocumentSnapshot.data() now returns `nil`
instead of throwing an exception. A non-nullable QueryDocumentSnapshot is
introduced for Queries to reduce the number of nil-checks in your code.

# v0.9.4
- [changed] Firestore no longer has a direct dependency on FirebaseAuth.
Expand Down
7 changes: 7 additions & 0 deletions Firestore/Example/Tests/Integration/API/FIRDatabaseTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ - (void)testDeleteDocument {
XCTAssertFalse(result.exists);
}

- (void)testCanRetrieveDocumentThatDoesNotExist {
FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
XCTAssertNil(result.data);
XCTAssertNil(result[@"foo"]);
}

- (void)testCannotUpdateNonexistentDocument {
FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];

Expand Down
22 changes: 11 additions & 11 deletions Firestore/Source/API/FIRDocumentChange.m
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ + (FIRDocumentChangeType)documentChangeTypeForChange:(FSTDocumentViewChange *)ch
NSUInteger index = 0;
NSMutableArray<FIRDocumentChange *> *changes = [NSMutableArray array];
for (FSTDocumentViewChange *change in snapshot.documentChanges) {
FIRDocumentSnapshot *document =
[FIRDocumentSnapshot snapshotWithFirestore:firestore
documentKey:change.document.key
document:change.document
fromCache:snapshot.isFromCache];
FIRQueryDocumentSnapshot *document =
[FIRQueryDocumentSnapshot snapshotWithFirestore:firestore
documentKey:change.document.key
document:change.document
fromCache:snapshot.isFromCache];
FSTAssert(change.type == FSTDocumentViewChangeTypeAdded,
@"Invalid event type for first snapshot");
FSTAssert(!lastDocument ||
Expand All @@ -79,11 +79,11 @@ + (FIRDocumentChangeType)documentChangeTypeForChange:(FSTDocumentViewChange *)ch
FSTDocumentSet *indexTracker = snapshot.oldDocuments;
NSMutableArray<FIRDocumentChange *> *changes = [NSMutableArray array];
for (FSTDocumentViewChange *change in snapshot.documentChanges) {
FIRDocumentSnapshot *document =
[FIRDocumentSnapshot snapshotWithFirestore:firestore
documentKey:change.document.key
document:change.document
fromCache:snapshot.isFromCache];
FIRQueryDocumentSnapshot *document =
[FIRQueryDocumentSnapshot snapshotWithFirestore:firestore
documentKey:change.document.key
document:change.document
fromCache:snapshot.isFromCache];

NSUInteger oldIndex = NSNotFound;
NSUInteger newIndex = NSNotFound;
Expand Down Expand Up @@ -112,7 +112,7 @@ + (FIRDocumentChangeType)documentChangeTypeForChange:(FSTDocumentViewChange *)ch
@implementation FIRDocumentChange

- (instancetype)initWithType:(FIRDocumentChangeType)type
document:(FIRDocumentSnapshot *)document
document:(FIRQueryDocumentSnapshot *)document
oldIndex:(NSUInteger)oldIndex
newIndex:(NSUInteger)newIndex {
if (self = [super init]) {
Expand Down
57 changes: 41 additions & 16 deletions Firestore/Source/API/FIRDocumentSnapshot.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#import "Firestore/Source/Model/FSTDocumentKey.h"
#import "Firestore/Source/Model/FSTFieldValue.h"
#import "Firestore/Source/Model/FSTPath.h"
#import "Firestore/Source/Util/FSTAssert.h"
#import "Firestore/Source/Util/FSTUsageValidation.h"

NS_ASSUME_NONNULL_BEGIN
Expand Down Expand Up @@ -100,23 +101,15 @@ - (FIRSnapshotMetadata *)metadata {
return _cachedMetadata;
}

- (NSDictionary<NSString *, id> *)data {
- (nullable NSDictionary<NSString *, id> *)data {
return [self dataWithOptions:[FIRSnapshotOptions defaultOptions]];
}

- (NSDictionary<NSString *, id> *)dataWithOptions:(FIRSnapshotOptions *)options {
FSTDocument *document = self.internalDocument;

if (!document) {
FSTThrowInvalidUsage(
@"NonExistentDocumentException",
@"Document '%@' doesn't exist. "
@"Check document.exists to make sure the document exists before calling document.data.",
self.internalKey);
}

return [self convertedObject:[self.internalDocument data]
options:[FSTFieldValueOptions optionsForSnapshotOptions:options]];
- (nullable NSDictionary<NSString *, id> *)dataWithOptions:(FIRSnapshotOptions *)options {
return self.internalDocument == nil
? nil
: [self convertedObject:[self.internalDocument data]
options:[FSTFieldValueOptions optionsForSnapshotOptions:options]];
}

- (nullable id)valueForField:(id)field {
Expand All @@ -135,8 +128,10 @@ - (nullable id)valueForField:(id)field options:(FIRSnapshotOptions *)options {
}

FSTFieldValue *fieldValue = [[self.internalDocument data] valueForPath:fieldPath.internalValue];
return [self convertedValue:fieldValue
options:[FSTFieldValueOptions optionsForSnapshotOptions:options]];
return fieldValue == nil
? nil
: [self convertedValue:fieldValue
options:[FSTFieldValueOptions optionsForSnapshotOptions:options]];
}

- (nullable id)objectForKeyedSubscript:(id)key {
Expand Down Expand Up @@ -190,4 +185,34 @@ - (id)convertedValue:(FSTFieldValue *)value options:(FSTFieldValueOptions *)opti

@end

@interface FIRQueryDocumentSnapshot ()

- (instancetype)initWithFirestore:(FIRFirestore *)firestore
documentKey:(FSTDocumentKey *)documentKey
document:(FSTDocument *)document
fromCache:(BOOL)fromCache NS_DESIGNATED_INITIALIZER;

@end

@implementation FIRQueryDocumentSnapshot

- (instancetype)initWithFirestore:(FIRFirestore *)firestore
documentKey:(FSTDocumentKey *)documentKey
document:(FSTDocument *)document
fromCache:(BOOL)fromCache {
self = [super initWithFirestore:firestore
documentKey:documentKey
document:document
fromCache:fromCache];
return self;
}

- (NSDictionary<NSString *, id> *)data {
NSDictionary<NSString *, id> *data = [super data];
FSTAssert(data, @"Document in a QueryDocumentSnapshot should exist");
return data;
}

@end

NS_ASSUME_NONNULL_END
14 changes: 7 additions & 7 deletions Firestore/Source/API/FIRQuerySnapshot.m
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ + (instancetype)snapshotWithFirestore:(FIRFirestore *)firestore

@implementation FIRQuerySnapshot {
// Cached value of the documents property.
NSArray<FIRDocumentSnapshot *> *_documents;
NSArray<FIRQueryDocumentSnapshot *> *_documents;

// Cached value of the documentChanges property.
NSArray<FIRDocumentChange *> *_documentChanges;
Expand Down Expand Up @@ -93,18 +93,18 @@ - (NSInteger)count {
return self.snapshot.documents.count;
}

- (NSArray<FIRDocumentSnapshot *> *)documents {
- (NSArray<FIRQueryDocumentSnapshot *> *)documents {
if (!_documents) {
FSTDocumentSet *documentSet = self.snapshot.documents;
FIRFirestore *firestore = self.firestore;
BOOL fromCache = self.metadata.fromCache;

NSMutableArray<FIRDocumentSnapshot *> *result = [NSMutableArray array];
NSMutableArray<FIRQueryDocumentSnapshot *> *result = [NSMutableArray array];
for (FSTDocument *document in documentSet.documentEnumerator) {
[result addObject:[FIRDocumentSnapshot snapshotWithFirestore:firestore
documentKey:document.key
document:document
fromCache:fromCache]];
[result addObject:[FIRQueryDocumentSnapshot snapshotWithFirestore:firestore
documentKey:document.key
document:document
fromCache:fromCache]];
}

_documents = result;
Expand Down
4 changes: 2 additions & 2 deletions Firestore/Source/Public/FIRDocumentChange.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

NS_ASSUME_NONNULL_BEGIN

@class FIRDocumentSnapshot;
@class FIRQueryDocumentSnapshot;

/** An enumeration of document change types. */
typedef NS_ENUM(NSInteger, FIRDocumentChangeType) {
Expand Down Expand Up @@ -47,7 +47,7 @@ NS_SWIFT_NAME(DocumentChange)
@property(nonatomic, readonly) FIRDocumentChangeType type;

/** The document affected by this change. */
@property(nonatomic, strong, readonly) FIRDocumentSnapshot *document;
@property(nonatomic, strong, readonly) FIRQueryDocumentSnapshot *document;

/**
* The index of the changed document in the result set immediately prior to this FIRDocumentChange
Expand Down
81 changes: 55 additions & 26 deletions Firestore/Source/Public/FIRDocumentSnapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ NS_SWIFT_NAME(SnapshotOptions)
* A `FIRDocumentSnapshot` contains data read from a document in your Firestore database. The data
* can be extracted with the `data` property or by using subscript syntax to access a specific
* field.
*
* For a `FIRDocumentSnapshot` that points to non-existing document, any data access will return
* `nil`. You can use the `exists` property to explicitly verify a documents existence.
*/
NS_SWIFT_NAME(DocumentSnapshot)
@interface FIRDocumentSnapshot : NSObject
Expand All @@ -95,50 +98,51 @@ NS_SWIFT_NAME(DocumentSnapshot)
@property(nonatomic, strong, readonly) FIRSnapshotMetadata *metadata;

/**
* Retrieves all fields in the document as an `NSDictionary`.
* Retrieves all fields in the document as an `NSDictionary`. Returns `nil` if the document doesn't
* exist.
*
* Server-provided timestamps that have not yet been set to their final value
* will be returned as `NSNull`. You can use `dataWithOptions()` to configure this
* behavior.
* Server-provided timestamps that have not yet been set to their final value will be returned as
* `NSNull`. You can use `dataWithOptions()` to configure this behavior.
*
* @return An `NSDictionary` containing all fields in the document.
* @return An `NSDictionary` containing all fields in the document or `nil` if the document doesn't
* exist.
*/
- (NSDictionary<NSString *, id> *)data;
- (nullable NSDictionary<NSString *, id> *)data;

/**
* Retrieves all fields in the document as a `Dictionary`.
* Retrieves all fields in the document as a `Dictionary`. Returns `nil` if the document doesn't
* exist.
*
* @param options `SnapshotOptions` to configure how data is returned from the
* snapshot (e.g. the desired behavior for server timestamps that have not
* yet been set to their final value).
* @return A `Dictionary` containing all fields in the document.
* @param options `SnapshotOptions` to configure how data is returned from the snapshot (e.g. the
* desired behavior for server timestamps that have not yet been set to their final value).
* @return A `Dictionary` containing all fields in the document or `nil` if the document doesn't
* exist.
*/
- (NSDictionary<NSString *, id> *)dataWithOptions:(FIRSnapshotOptions *)options;
- (nullable NSDictionary<NSString *, id> *)dataWithOptions:(FIRSnapshotOptions *)options;

/**
* Retrieves a specific field from the document.
* Retrieves a specific field from the document. Returns `nil` if the document or the field doesn't
* exist.
*
* The timestamps that have not yet been set to their final value
* will be returned as `NSNull`. The can use `get(_:options:)` to
* configure this behavior.
* The timestamps that have not yet been set to their final value will be returned as `NSNull`. The
* can use `get(_:options:)` to configure this behavior.
*
* @param field The field to retrieve.
* @return The value contained in the field or `nil` if the field doesn't exist.
* @return The value contained in the field or `nil` if the document or field doesn't exist.
*/
- (nullable id)valueForField:(id)field NS_SWIFT_NAME(get(_:));

/**
* Retrieves a specific field from the document.
* Retrieves a specific field from the document. Returns `nil` if the document or the field doesn't
* exist.
*
* The timestamps that have not yet been set to their final value
* will be returned as `NSNull`. The can use `get(_:options:)` to
* configure this behavior.
* The timestamps that have not yet been set to their final value will be returned as `NSNull`. The
* can use `get(_:options:)` to configure this behavior.
*
* @param field The field to retrieve.
* @param options `SnapshotOptions` to configure how data is returned from the
* snapshot (e.g. the desired behavior for server timestamps that have not
* yet been set to their final value).
* @return The value contained in the field or `nil` if the field doesn't exist.
* @param options `SnapshotOptions` to configure how data is returned from the snapshot (e.g. the
* desired behavior for server timestamps that have not yet been set to their final value).
* @return The value contained in the field or `nil` if the document or field doesn't exist.
*/
// clang-format off
- (nullable id)valueForField:(id)field
Expand All @@ -151,10 +155,35 @@ NS_SWIFT_NAME(DocumentSnapshot)
*
* @param key The field to retrieve.
*
* @return The value contained in the field or `nil` if the field doesn't exist.
* @return The value contained in the field or `nil` if the document or field doesn't exist.
*/
- (nullable id)objectForKeyedSubscript:(id)key;

@end

/**
* A `FIRQueryDocumentSnapshot` contains data read from a document in your Firestore database as
* part of a query. The document is guaranteed to exist and its data can be extracted with the
* `data` property or by using subscript syntax to access a specific field.
*
* A `FIRQueryDocumentSnapshot` offers the same API surface as a `FIRDocumentSnapshot`. As
* deleted documents are not returned from queries, its `exists` property will always be true and
* `data:` will never return `nil`.
*/
NS_SWIFT_NAME(QueryDocumentSnapshot)
@interface FIRQueryDocumentSnapshot : FIRDocumentSnapshot

/** */
- (instancetype)init
__attribute__((unavailable("FIRQueryDocumentSnapshot cannot be created directly.")));

/**
* Retrieves all fields in the document as an `NSDictionary`.
*
* @return An `NSDictionary` containing all fields in the document.
*/
- (NSDictionary<NSString *, id> *)data;

@end

NS_ASSUME_NONNULL_END
4 changes: 2 additions & 2 deletions Firestore/Source/Public/FIRQuerySnapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
NS_ASSUME_NONNULL_BEGIN

@class FIRDocumentChange;
@class FIRDocumentSnapshot;
@class FIRQuery;
@class FIRQueryDocumentSnapshot;
@class FIRSnapshotMetadata;

/**
Expand Down Expand Up @@ -50,7 +50,7 @@ NS_SWIFT_NAME(QuerySnapshot)
@property(nonatomic, readonly) NSInteger count;

/** An Array of the `FIRDocumentSnapshots` that make up this document set. */
@property(nonatomic, strong, readonly) NSArray<FIRDocumentSnapshot *> *documents;
@property(nonatomic, strong, readonly) NSArray<FIRQueryDocumentSnapshot *> *documents;

/**
* An array of the documents that changed since the last snapshot. If this is the first snapshot,
Expand Down