FIRDocumentSnapshot.mm 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "FIRDocumentSnapshot.h"
  17. #include <utility>
  18. #import "FIRFirestoreSettings.h"
  19. #import "Firestore/Source/API/FIRDocumentReference+Internal.h"
  20. #import "Firestore/Source/API/FIRFieldPath+Internal.h"
  21. #import "Firestore/Source/API/FIRFirestore+Internal.h"
  22. #import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h"
  23. #import "Firestore/Source/API/FIRSnapshotOptions+Internal.h"
  24. #import "Firestore/Source/Model/FSTDocument.h"
  25. #import "Firestore/Source/Model/FSTFieldValue.h"
  26. #import "Firestore/Source/Util/FSTAssert.h"
  27. #import "Firestore/Source/Util/FSTUsageValidation.h"
  28. #include "Firestore/core/src/firebase/firestore/model/database_id.h"
  29. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  30. #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
  31. namespace util = firebase::firestore::util;
  32. using firebase::firestore::model::DatabaseId;
  33. using firebase::firestore::model::DocumentKey;
  34. NS_ASSUME_NONNULL_BEGIN
  35. @interface FIRDocumentSnapshot ()
  36. - (instancetype)initWithFirestore:(FIRFirestore *)firestore
  37. documentKey:(DocumentKey)documentKey
  38. document:(nullable FSTDocument *)document
  39. fromCache:(BOOL)fromCache NS_DESIGNATED_INITIALIZER;
  40. - (const DocumentKey &)internalKey;
  41. @property(nonatomic, strong, readonly) FIRFirestore *firestore;
  42. @property(nonatomic, strong, readonly, nullable) FSTDocument *internalDocument;
  43. @property(nonatomic, assign, readonly) BOOL fromCache;
  44. @end
  45. @implementation FIRDocumentSnapshot (Internal)
  46. + (instancetype)snapshotWithFirestore:(FIRFirestore *)firestore
  47. documentKey:(DocumentKey)documentKey
  48. document:(nullable FSTDocument *)document
  49. fromCache:(BOOL)fromCache {
  50. return [[[self class] alloc] initWithFirestore:firestore
  51. documentKey:std::move(documentKey)
  52. document:document
  53. fromCache:fromCache];
  54. }
  55. @end
  56. @implementation FIRDocumentSnapshot {
  57. FIRSnapshotMetadata *_cachedMetadata;
  58. DocumentKey _internalKey;
  59. }
  60. @dynamic metadata;
  61. - (instancetype)initWithFirestore:(FIRFirestore *)firestore
  62. documentKey:(DocumentKey)documentKey
  63. document:(nullable FSTDocument *)document
  64. fromCache:(BOOL)fromCache {
  65. if (self = [super init]) {
  66. _firestore = firestore;
  67. _internalKey = std::move(documentKey);
  68. _internalDocument = document;
  69. _fromCache = fromCache;
  70. }
  71. return self;
  72. }
  73. - (const DocumentKey &)internalKey {
  74. return _internalKey;
  75. }
  76. // NSObject Methods
  77. - (BOOL)isEqual:(nullable id)other {
  78. if (other == self) return YES;
  79. // self class could be FIRDocumentSnapshot or subtype. So we compare with base type explicitly.
  80. if (![other isKindOfClass:[FIRDocumentSnapshot class]]) return NO;
  81. return [self isEqualToSnapshot:other];
  82. }
  83. - (BOOL)isEqualToSnapshot:(nullable FIRDocumentSnapshot *)snapshot {
  84. if (self == snapshot) return YES;
  85. if (snapshot == nil) return NO;
  86. return [self.firestore isEqual:snapshot.firestore] && self.internalKey == snapshot.internalKey &&
  87. (self.internalDocument == snapshot.internalDocument ||
  88. [self.internalDocument isEqual:snapshot.internalDocument]) &&
  89. self.fromCache == snapshot.fromCache;
  90. }
  91. - (NSUInteger)hash {
  92. NSUInteger hash = [self.firestore hash];
  93. hash = hash * 31u + self.internalKey.Hash();
  94. hash = hash * 31u + [self.internalDocument hash];
  95. hash = hash * 31u + (self.fromCache ? 1 : 0);
  96. return hash;
  97. }
  98. @dynamic exists;
  99. - (BOOL)exists {
  100. return _internalDocument != nil;
  101. }
  102. - (FIRDocumentReference *)reference {
  103. return [FIRDocumentReference referenceWithKey:self.internalKey firestore:self.firestore];
  104. }
  105. - (NSString *)documentID {
  106. return util::WrapNSString(self.internalKey.path().last_segment());
  107. }
  108. - (FIRSnapshotMetadata *)metadata {
  109. if (!_cachedMetadata) {
  110. _cachedMetadata = [FIRSnapshotMetadata
  111. snapshotMetadataWithPendingWrites:self.internalDocument.hasLocalMutations
  112. fromCache:self.fromCache];
  113. }
  114. return _cachedMetadata;
  115. }
  116. - (nullable NSDictionary<NSString *, id> *)data {
  117. return [self dataWithOptions:[FIRSnapshotOptions defaultOptions]];
  118. }
  119. - (nullable NSDictionary<NSString *, id> *)dataWithOptions:(FIRSnapshotOptions *)options {
  120. return self.internalDocument == nil
  121. ? nil
  122. : [self convertedObject:[self.internalDocument data]
  123. options:[FSTFieldValueOptions
  124. optionsForSnapshotOptions:options
  125. timestampsInSnapshotsEnabled:
  126. self.firestore.settings.timestampsInSnapshotsEnabled]];
  127. }
  128. - (nullable id)valueForField:(id)field {
  129. return [self valueForField:field options:[FIRSnapshotOptions defaultOptions]];
  130. }
  131. - (nullable id)valueForField:(id)field options:(FIRSnapshotOptions *)options {
  132. FIRFieldPath *fieldPath;
  133. if ([field isKindOfClass:[NSString class]]) {
  134. fieldPath = [FIRFieldPath pathWithDotSeparatedString:field];
  135. } else if ([field isKindOfClass:[FIRFieldPath class]]) {
  136. fieldPath = field;
  137. } else {
  138. FSTThrowInvalidArgument(@"Subscript key must be an NSString or FIRFieldPath.");
  139. }
  140. FSTFieldValue *fieldValue = [[self.internalDocument data] valueForPath:fieldPath.internalValue];
  141. return fieldValue == nil
  142. ? nil
  143. : [self convertedValue:fieldValue
  144. options:[FSTFieldValueOptions
  145. optionsForSnapshotOptions:options
  146. timestampsInSnapshotsEnabled:
  147. self.firestore.settings.timestampsInSnapshotsEnabled]];
  148. }
  149. - (nullable id)objectForKeyedSubscript:(id)key {
  150. return [self valueForField:key];
  151. }
  152. - (id)convertedValue:(FSTFieldValue *)value options:(FSTFieldValueOptions *)options {
  153. if ([value isKindOfClass:[FSTObjectValue class]]) {
  154. return [self convertedObject:(FSTObjectValue *)value options:options];
  155. } else if ([value isKindOfClass:[FSTArrayValue class]]) {
  156. return [self convertedArray:(FSTArrayValue *)value options:options];
  157. } else if ([value isKindOfClass:[FSTReferenceValue class]]) {
  158. FSTReferenceValue *ref = (FSTReferenceValue *)value;
  159. const DatabaseId *refDatabase = ref.databaseID;
  160. const DatabaseId *database = self.firestore.databaseID;
  161. if (*refDatabase != *database) {
  162. // TODO(b/32073923): Log this as a proper warning.
  163. NSLog(
  164. @"WARNING: Document %@ contains a document reference within a different database "
  165. "(%s/%s) which is not supported. It will be treated as a reference within the "
  166. "current database (%s/%s) instead.",
  167. self.reference.path, refDatabase->project_id().c_str(),
  168. refDatabase->database_id().c_str(), database->project_id().c_str(),
  169. database->database_id().c_str());
  170. }
  171. return [FIRDocumentReference referenceWithKey:[ref valueWithOptions:options]
  172. firestore:self.firestore];
  173. } else {
  174. return [value valueWithOptions:options];
  175. }
  176. }
  177. - (NSDictionary<NSString *, id> *)convertedObject:(FSTObjectValue *)objectValue
  178. options:(FSTFieldValueOptions *)options {
  179. NSMutableDictionary *result = [NSMutableDictionary dictionary];
  180. [objectValue.internalValue
  181. enumerateKeysAndObjectsUsingBlock:^(NSString *key, FSTFieldValue *value, BOOL *stop) {
  182. result[key] = [self convertedValue:value options:options];
  183. }];
  184. return result;
  185. }
  186. - (NSArray<id> *)convertedArray:(FSTArrayValue *)arrayValue
  187. options:(FSTFieldValueOptions *)options {
  188. NSArray<FSTFieldValue *> *internalValue = arrayValue.internalValue;
  189. NSMutableArray *result = [NSMutableArray arrayWithCapacity:internalValue.count];
  190. [internalValue enumerateObjectsUsingBlock:^(id value, NSUInteger idx, BOOL *stop) {
  191. [result addObject:[self convertedValue:value options:options]];
  192. }];
  193. return result;
  194. }
  195. @end
  196. @interface FIRQueryDocumentSnapshot ()
  197. - (instancetype)initWithFirestore:(FIRFirestore *)firestore
  198. documentKey:(DocumentKey)documentKey
  199. document:(FSTDocument *)document
  200. fromCache:(BOOL)fromCache NS_DESIGNATED_INITIALIZER;
  201. @end
  202. @implementation FIRQueryDocumentSnapshot
  203. - (instancetype)initWithFirestore:(FIRFirestore *)firestore
  204. documentKey:(DocumentKey)documentKey
  205. document:(FSTDocument *)document
  206. fromCache:(BOOL)fromCache {
  207. self = [super initWithFirestore:firestore
  208. documentKey:std::move(documentKey)
  209. document:document
  210. fromCache:fromCache];
  211. return self;
  212. }
  213. - (NSDictionary<NSString *, id> *)data {
  214. NSDictionary<NSString *, id> *data = [super data];
  215. FSTAssert(data, @"Document in a QueryDocumentSnapshot should exist");
  216. return data;
  217. }
  218. - (NSDictionary<NSString *, id> *)dataWithOptions:(FIRSnapshotOptions *)options {
  219. NSDictionary<NSString *, id> *data = [super dataWithOptions:options];
  220. FSTAssert(data, @"Document in a QueryDocumentSnapshot should exist");
  221. return data;
  222. }
  223. @end
  224. NS_ASSUME_NONNULL_END