|
24 | 24 | #import "Firestore/Source/API/FIRDocumentReference+Internal.h"
|
25 | 25 | #import "Firestore/Source/API/FIRFieldPath+Internal.h"
|
26 | 26 | #import "Firestore/Source/API/FIRFirestore+Internal.h"
|
| 27 | +#import "Firestore/Source/API/FIRListenerRegistration+Internal.h" |
27 | 28 | #import "Firestore/Source/API/FIRPipelineBridge+Internal.h"
|
| 29 | +#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" |
28 | 30 | #import "Firestore/Source/API/FSTUserDataReader.h"
|
29 | 31 | #import "Firestore/Source/API/FSTUserDataWriter.h"
|
30 | 32 | #import "Firestore/Source/API/converters.h"
|
|
38 | 40 | #include "Firestore/core/src/api/ordering.h"
|
39 | 41 | #include "Firestore/core/src/api/pipeline.h"
|
40 | 42 | #include "Firestore/core/src/api/pipeline_result.h"
|
| 43 | +#include "Firestore/core/src/api/pipeline_result_change.h" |
41 | 44 | #include "Firestore/core/src/api/pipeline_snapshot.h"
|
| 45 | +#include "Firestore/core/src/api/query_listener_registration.h" |
| 46 | +#include "Firestore/core/src/api/realtime_pipeline.h" |
| 47 | +#include "Firestore/core/src/api/realtime_pipeline_snapshot.h" |
| 48 | +#include "Firestore/core/src/api/snapshot_metadata.h" |
42 | 49 | #include "Firestore/core/src/api/stages.h"
|
| 50 | +#include "Firestore/core/src/core/event_listener.h" |
| 51 | +#include "Firestore/core/src/core/firestore_client.h" |
| 52 | +#include "Firestore/core/src/core/listen_options.h" |
| 53 | +#include "Firestore/core/src/core/view_snapshot.h" |
43 | 54 | #include "Firestore/core/src/util/comparison.h"
|
44 | 55 | #include "Firestore/core/src/util/error_apple.h"
|
45 | 56 | #include "Firestore/core/src/util/status.h"
|
|
53 | 64 | using firebase::firestore::api::Constant;
|
54 | 65 | using firebase::firestore::api::DatabaseSource;
|
55 | 66 | using firebase::firestore::api::DistinctStage;
|
| 67 | +using firebase::firestore::api::DocumentChange; |
56 | 68 | using firebase::firestore::api::DocumentReference;
|
57 | 69 | using firebase::firestore::api::DocumentsSource;
|
58 | 70 | using firebase::firestore::api::Expr;
|
|
64 | 76 | using firebase::firestore::api::OffsetStage;
|
65 | 77 | using firebase::firestore::api::Ordering;
|
66 | 78 | using firebase::firestore::api::Pipeline;
|
| 79 | +using firebase::firestore::api::PipelineResultChange; |
| 80 | +using firebase::firestore::api::QueryListenerRegistration; |
| 81 | +using firebase::firestore::api::RealtimePipeline; |
| 82 | +using firebase::firestore::api::RealtimePipelineSnapshot; |
67 | 83 | using firebase::firestore::api::RawStage;
|
68 | 84 | using firebase::firestore::api::RemoveFieldsStage;
|
69 | 85 | using firebase::firestore::api::ReplaceWith;
|
70 | 86 | using firebase::firestore::api::Sample;
|
71 | 87 | using firebase::firestore::api::SelectStage;
|
| 88 | +using firebase::firestore::api::SnapshotMetadata; |
72 | 89 | using firebase::firestore::api::SortStage;
|
73 | 90 | using firebase::firestore::api::Union;
|
74 | 91 | using firebase::firestore::api::Unnest;
|
75 | 92 | using firebase::firestore::api::Where;
|
| 93 | +using firebase::firestore::core::EventListener; |
| 94 | +using firebase::firestore::core::ViewSnapshot; |
76 | 95 | using firebase::firestore::model::DeepClone;
|
77 | 96 | using firebase::firestore::model::FieldPath;
|
78 | 97 | using firebase::firestore::nanopb::MakeSharedMessage;
|
@@ -1016,6 +1035,48 @@ - (nullable id)get:(id)field
|
1016 | 1035 |
|
1017 | 1036 | @end
|
1018 | 1037 |
|
| 1038 | +@implementation __FIRPipelineResultChangeBridge { |
| 1039 | + api::PipelineResultChange change_; |
| 1040 | + std::shared_ptr<api::Firestore> db_; |
| 1041 | +} |
| 1042 | + |
| 1043 | +- (FIRDocumentChangeType)type { |
| 1044 | + switch (change_.type()) { |
| 1045 | + case PipelineResultChange::Type::Added: |
| 1046 | + return FIRDocumentChangeTypeAdded; |
| 1047 | + case PipelineResultChange::Type::Modified: |
| 1048 | + return FIRDocumentChangeTypeModified; |
| 1049 | + case PipelineResultChange::Type::Removed: |
| 1050 | + return FIRDocumentChangeTypeRemoved; |
| 1051 | + } |
| 1052 | + |
| 1053 | + HARD_FAIL("Unknown PipelineResultChange::Type: %s", change_.type()); |
| 1054 | +} |
| 1055 | + |
| 1056 | +- (__FIRPipelineResultBridge *)result { |
| 1057 | + return [[__FIRPipelineResultBridge alloc] initWithCppResult:change_.result() db:db_]; |
| 1058 | +} |
| 1059 | + |
| 1060 | +- (NSUInteger)oldIndex { |
| 1061 | + return change_.old_index() == PipelineResultChange::npos ? NSNotFound : change_.old_index(); |
| 1062 | +} |
| 1063 | + |
| 1064 | +- (NSUInteger)newIndex { |
| 1065 | + return change_.new_index() == PipelineResultChange::npos ? NSNotFound : change_.new_index(); |
| 1066 | +} |
| 1067 | + |
| 1068 | +- (id)initWithCppChange:(api::PipelineResultChange)change db:(std::shared_ptr<api::Firestore>)db { |
| 1069 | + self = [super init]; |
| 1070 | + if (self) { |
| 1071 | + change_ = std::move(change); |
| 1072 | + db_ = std::move(db); |
| 1073 | + } |
| 1074 | + |
| 1075 | + return self; |
| 1076 | +} |
| 1077 | + |
| 1078 | +@end |
| 1079 | + |
1019 | 1080 | @implementation FIRPipelineBridge {
|
1020 | 1081 | NSArray<FIRStageBridge *> *_stages;
|
1021 | 1082 | FIRFirestore *firestore;
|
@@ -1059,4 +1120,195 @@ - (void)executeWithCompletion:(void (^)(__FIRPipelineSnapshotBridge *_Nullable r
|
1059 | 1120 |
|
1060 | 1121 | @end
|
1061 | 1122 |
|
| 1123 | +@interface __FIRRealtimePipelineSnapshotBridge () |
| 1124 | + |
| 1125 | +@property(nonatomic, strong, readwrite) NSArray<__FIRPipelineResultBridge *> *results; |
| 1126 | + |
| 1127 | +@property(nonatomic, strong, readwrite) NSArray<__FIRPipelineResultChangeBridge *> *changes; |
| 1128 | + |
| 1129 | +@end |
| 1130 | + |
| 1131 | +@implementation __FIRRealtimePipelineSnapshotBridge { |
| 1132 | + absl::optional<api::RealtimePipelineSnapshot> snapshot_; |
| 1133 | + NSMutableArray<__FIRPipelineResultBridge *> *results_; |
| 1134 | + NSMutableArray<__FIRPipelineResultChangeBridge *> *changes_; |
| 1135 | + FIRSnapshotMetadata *_metadata; |
| 1136 | +} |
| 1137 | + |
| 1138 | +- (id)initWithCppSnapshot:(api::RealtimePipelineSnapshot)snapshot { |
| 1139 | + self = [super init]; |
| 1140 | + if (self) { |
| 1141 | + snapshot_ = std::move(snapshot); |
| 1142 | + if (!snapshot_.has_value()) { |
| 1143 | + results_ = nil; |
| 1144 | + } else { |
| 1145 | + _metadata = |
| 1146 | + [[FIRSnapshotMetadata alloc] initWithMetadata:snapshot_.value().snapshot_metadata()]; |
| 1147 | + |
| 1148 | + NSMutableArray<__FIRPipelineResultBridge *> *results = [NSMutableArray array]; |
| 1149 | + for (auto &result : snapshot_.value().view_snapshot().documents()) { |
| 1150 | + [results addObject:[[__FIRPipelineResultBridge alloc] |
| 1151 | + initWithCppResult:api::PipelineResult(result) |
| 1152 | + db:snapshot_.value().firestore()]]; |
| 1153 | + } |
| 1154 | + results_ = results; |
| 1155 | + |
| 1156 | + NSMutableArray<__FIRPipelineResultChangeBridge *> *changes = [NSMutableArray array]; |
| 1157 | + for (auto &change : snapshot_.value().CalculateResultChanges(false)) { |
| 1158 | + [changes addObject:[[__FIRPipelineResultChangeBridge alloc] |
| 1159 | + initWithCppChange:change |
| 1160 | + db:snapshot_.value().firestore()]]; |
| 1161 | + } |
| 1162 | + changes_ = changes; |
| 1163 | + } |
| 1164 | + } |
| 1165 | + |
| 1166 | + return self; |
| 1167 | +} |
| 1168 | + |
| 1169 | +- (NSArray<__FIRPipelineResultBridge *> *)results { |
| 1170 | + return results_; |
| 1171 | +} |
| 1172 | + |
| 1173 | +- (NSArray<__FIRPipelineResultChangeBridge *> *)changes { |
| 1174 | + return changes_; |
| 1175 | +} |
| 1176 | + |
| 1177 | +- (FIRSnapshotMetadata *)metadata { |
| 1178 | + return _metadata; |
| 1179 | +} |
| 1180 | + |
| 1181 | +@end |
| 1182 | + |
| 1183 | +@implementation __FIRPipelineListenOptionsBridge |
| 1184 | + |
| 1185 | +- (instancetype)initWithServerTimestampBehavior:(NSString *)serverTimestampBehavior |
| 1186 | + includeMetadata:(BOOL)includeMetadata |
| 1187 | + source:(FIRListenSource)source { |
| 1188 | + // Call the designated initializer of the superclass (NSObject). |
| 1189 | + self = [super init]; |
| 1190 | + if (self) { |
| 1191 | + // Assign the passed-in values to the backing instance variables |
| 1192 | + // for the readonly properties. |
| 1193 | + // We use `copy` here for the string to ensure our object owns an immutable version. |
| 1194 | + _serverTimestampBehavior = [serverTimestampBehavior copy]; |
| 1195 | + _includeMetadata = includeMetadata; |
| 1196 | + _source = source; |
| 1197 | + } |
| 1198 | + return self; |
| 1199 | +} |
| 1200 | + |
| 1201 | +@end |
| 1202 | + |
| 1203 | +@implementation FIRRealtimePipelineBridge { |
| 1204 | + NSArray<FIRStageBridge *> *_stages; |
| 1205 | + FIRFirestore *firestore; |
| 1206 | + std::shared_ptr<api::RealtimePipeline> cpp_pipeline; |
| 1207 | +} |
| 1208 | + |
| 1209 | +- (id)initWithStages:(NSArray<FIRStageBridge *> *)stages db:(FIRFirestore *)db { |
| 1210 | + _stages = stages; |
| 1211 | + firestore = db; |
| 1212 | + return [super init]; |
| 1213 | +} |
| 1214 | + |
| 1215 | +core::ListenOptions ToListenOptions(__FIRPipelineListenOptionsBridge *_Nullable bridge) { |
| 1216 | + // If the bridge object is nil, return a default-constructed ListenOptions. |
| 1217 | + if (bridge == nil) { |
| 1218 | + return core::ListenOptions::DefaultOptions(); |
| 1219 | + } |
| 1220 | + |
| 1221 | + // 1. Translate include_metadata_changes |
| 1222 | + bool include_metadata = bridge.includeMetadata; |
| 1223 | + |
| 1224 | + // 2. Translate ListenSource |
| 1225 | + core::ListenSource source = core::ListenSource::Default; |
| 1226 | + switch (bridge.source) { |
| 1227 | + case FIRListenSourceDefault: |
| 1228 | + source = core::ListenSource::Default; |
| 1229 | + break; |
| 1230 | + case FIRListenSourceCache: |
| 1231 | + source = core::ListenSource::Cache; |
| 1232 | + break; |
| 1233 | + } |
| 1234 | + |
| 1235 | + // 3. Translate ServerTimestampBehavior |
| 1236 | + core::ListenOptions::ServerTimestampBehavior behavior = |
| 1237 | + core::ListenOptions::ServerTimestampBehavior::kNone; |
| 1238 | + if ([bridge.serverTimestampBehavior isEqual:@"estimate"]) { |
| 1239 | + behavior = core::ListenOptions::ServerTimestampBehavior::kEstimate; |
| 1240 | + } else if ([bridge.serverTimestampBehavior isEqual:@"previous"]) { |
| 1241 | + behavior = core::ListenOptions::ServerTimestampBehavior::kPrevious; |
| 1242 | + } else { |
| 1243 | + // "none" or any other value defaults to kNone. |
| 1244 | + behavior = core::ListenOptions::ServerTimestampBehavior::kNone; |
| 1245 | + } |
| 1246 | + |
| 1247 | + // 4. Construct the final C++ object using the canonical private constructor. |
| 1248 | + // Note: wait_for_sync_when_online is not part of the bridge, so we use 'false' |
| 1249 | + // to match the behavior of the existing static factories. |
| 1250 | + return core::ListenOptions( |
| 1251 | + /*include_query_metadata_changes=*/include_metadata, |
| 1252 | + /*include_document_metadata_changes=*/include_metadata, |
| 1253 | + /*wait_for_sync_when_online=*/false, source, behavior); |
| 1254 | +} |
| 1255 | + |
| 1256 | +- (id<FIRListenerRegistration>) |
| 1257 | + addSnapshotListenerWithOptions:(__FIRPipelineListenOptionsBridge *)options |
| 1258 | + listener: |
| 1259 | + (void (^)(__FIRRealtimePipelineSnapshotBridge *_Nullable snapshot, |
| 1260 | + NSError *_Nullable error))listener { |
| 1261 | + std::shared_ptr<api::Firestore> wrapped_firestore = firestore.wrapped; |
| 1262 | + |
| 1263 | + std::vector<std::shared_ptr<firebase::firestore::api::EvaluableStage>> cpp_stages; |
| 1264 | + for (FIRStageBridge *stage in _stages) { |
| 1265 | + auto evaluable_stage = std::dynamic_pointer_cast<api::EvaluableStage>( |
| 1266 | + [stage cppStageWithReader:firestore.dataReader]); |
| 1267 | + if (evaluable_stage) { |
| 1268 | + cpp_stages.push_back(evaluable_stage); |
| 1269 | + } else { |
| 1270 | + HARD_FAIL("Failed to convert cpp stage to EvaluableStage for RealtimePipeline"); |
| 1271 | + } |
| 1272 | + } |
| 1273 | + |
| 1274 | + cpp_pipeline = std::make_shared<RealtimePipeline>( |
| 1275 | + cpp_stages, std::make_unique<remote::Serializer>(wrapped_firestore->database_id())); |
| 1276 | + |
| 1277 | + // Convert from ViewSnapshots to RealtimePipelineSnapshots. |
| 1278 | + auto view_listener = EventListener<ViewSnapshot>::Create( |
| 1279 | + [listener, wrapped_firestore](StatusOr<ViewSnapshot> maybe_snapshot) { |
| 1280 | + if (!maybe_snapshot.status().ok()) { |
| 1281 | + listener(nil, MakeNSError(maybe_snapshot.status())); |
| 1282 | + return; |
| 1283 | + } |
| 1284 | + |
| 1285 | + ViewSnapshot snapshot = std::move(maybe_snapshot).ValueOrDie(); |
| 1286 | + SnapshotMetadata metadata(snapshot.has_pending_writes(), snapshot.from_cache()); |
| 1287 | + |
| 1288 | + listener( |
| 1289 | + [[__FIRRealtimePipelineSnapshotBridge alloc] |
| 1290 | + initWithCppSnapshot:RealtimePipelineSnapshot(wrapped_firestore, std::move(snapshot), |
| 1291 | + std::move(metadata))], |
| 1292 | + nil); |
| 1293 | + }); |
| 1294 | + |
| 1295 | + // Call the view_listener on the user Executor. |
| 1296 | + auto async_listener = core::AsyncEventListener<ViewSnapshot>::Create( |
| 1297 | + wrapped_firestore->client()->user_executor(), std::move(view_listener)); |
| 1298 | + |
| 1299 | + std::shared_ptr<core::QueryListener> query_listener = wrapped_firestore->client()->ListenToQuery( |
| 1300 | + *cpp_pipeline, ToListenOptions(options), async_listener); |
| 1301 | + |
| 1302 | + return [[FSTListenerRegistration alloc] |
| 1303 | + initWithRegistration:absl::make_unique<QueryListenerRegistration>(wrapped_firestore->client(), |
| 1304 | + std::move(async_listener), |
| 1305 | + std::move(query_listener))]; |
| 1306 | +} |
| 1307 | + |
| 1308 | +- (std::shared_ptr<api::RealtimePipeline>)cppPipelineWithReader:(FSTUserDataReader *)reader { |
| 1309 | + return cpp_pipeline; |
| 1310 | +} |
| 1311 | + |
| 1312 | +@end |
| 1313 | + |
1062 | 1314 | NS_ASSUME_NONNULL_END
|
0 commit comments