|
23 | 23 | #import "Firestore/Source/API/FIRDocumentReference+Internal.h"
|
24 | 24 | #import "Firestore/Source/API/FIRFieldPath+Internal.h"
|
25 | 25 | #import "Firestore/Source/API/FIRFirestore+Internal.h"
|
| 26 | +#import "Firestore/Source/API/FIRListenerRegistration+Internal.h" |
26 | 27 | #import "Firestore/Source/API/FIRPipelineBridge+Internal.h"
|
| 28 | +#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h" |
27 | 29 | #import "Firestore/Source/API/FSTUserDataReader.h"
|
28 | 30 | #import "Firestore/Source/API/FSTUserDataWriter.h"
|
29 | 31 | #import "Firestore/Source/API/converters.h"
|
|
37 | 39 | #include "Firestore/core/src/api/ordering.h"
|
38 | 40 | #include "Firestore/core/src/api/pipeline.h"
|
39 | 41 | #include "Firestore/core/src/api/pipeline_result.h"
|
| 42 | +#include "Firestore/core/src/api/pipeline_result_change.h" |
40 | 43 | #include "Firestore/core/src/api/pipeline_snapshot.h"
|
| 44 | +#include "Firestore/core/src/api/query_listener_registration.h" |
| 45 | +#include "Firestore/core/src/api/realtime_pipeline.h" |
| 46 | +#include "Firestore/core/src/api/realtime_pipeline_snapshot.h" |
| 47 | +#include "Firestore/core/src/api/snapshot_metadata.h" |
41 | 48 | #include "Firestore/core/src/api/stages.h"
|
| 49 | +#include "Firestore/core/src/core/event_listener.h" |
| 50 | +#include "Firestore/core/src/core/firestore_client.h" |
| 51 | +#include "Firestore/core/src/core/listen_options.h" |
| 52 | +#include "Firestore/core/src/core/view_snapshot.h" |
42 | 53 | #include "Firestore/core/src/util/error_apple.h"
|
43 | 54 | #include "Firestore/core/src/util/status.h"
|
44 | 55 | #include "Firestore/core/src/util/string_apple.h"
|
|
51 | 62 | using firebase::firestore::api::Constant;
|
52 | 63 | using firebase::firestore::api::DatabaseSource;
|
53 | 64 | using firebase::firestore::api::DistinctStage;
|
| 65 | +using firebase::firestore::api::DocumentChange; |
54 | 66 | using firebase::firestore::api::DocumentReference;
|
55 | 67 | using firebase::firestore::api::DocumentsSource;
|
56 | 68 | using firebase::firestore::api::Expr;
|
|
63 | 75 | using firebase::firestore::api::OffsetStage;
|
64 | 76 | using firebase::firestore::api::Ordering;
|
65 | 77 | using firebase::firestore::api::Pipeline;
|
| 78 | +using firebase::firestore::api::PipelineResultChange; |
| 79 | +using firebase::firestore::api::QueryListenerRegistration; |
| 80 | +using firebase::firestore::api::RealtimePipeline; |
| 81 | +using firebase::firestore::api::RealtimePipelineSnapshot; |
66 | 82 | using firebase::firestore::api::RemoveFieldsStage;
|
67 | 83 | using firebase::firestore::api::ReplaceWith;
|
68 | 84 | using firebase::firestore::api::Sample;
|
69 | 85 | using firebase::firestore::api::SelectStage;
|
| 86 | +using firebase::firestore::api::SnapshotMetadata; |
70 | 87 | using firebase::firestore::api::SortStage;
|
71 | 88 | using firebase::firestore::api::Union;
|
72 | 89 | using firebase::firestore::api::Unnest;
|
73 | 90 | using firebase::firestore::api::Where;
|
| 91 | +using firebase::firestore::core::EventListener; |
| 92 | +using firebase::firestore::core::ViewSnapshot; |
74 | 93 | using firebase::firestore::model::FieldPath;
|
75 | 94 | using firebase::firestore::nanopb::SharedMessage;
|
76 | 95 | using firebase::firestore::util::MakeCallback;
|
@@ -928,6 +947,48 @@ - (nullable id)get:(id)field
|
928 | 947 |
|
929 | 948 | @end
|
930 | 949 |
|
| 950 | +@implementation __FIRPipelineResultChangeBridge { |
| 951 | + api::PipelineResultChange change_; |
| 952 | + std::shared_ptr<api::Firestore> db_; |
| 953 | +} |
| 954 | + |
| 955 | +- (FIRDocumentChangeType)type { |
| 956 | + switch (change_.type()) { |
| 957 | + case PipelineResultChange::Type::Added: |
| 958 | + return FIRDocumentChangeTypeAdded; |
| 959 | + case PipelineResultChange::Type::Modified: |
| 960 | + return FIRDocumentChangeTypeModified; |
| 961 | + case PipelineResultChange::Type::Removed: |
| 962 | + return FIRDocumentChangeTypeRemoved; |
| 963 | + } |
| 964 | + |
| 965 | + HARD_FAIL("Unknown PipelineResultChange::Type: %s", change_.type()); |
| 966 | +} |
| 967 | + |
| 968 | +- (__FIRPipelineResultBridge *)result { |
| 969 | + return [[__FIRPipelineResultBridge alloc] initWithCppResult:change_.result() db:db_]; |
| 970 | +} |
| 971 | + |
| 972 | +- (NSUInteger)oldIndex { |
| 973 | + return change_.old_index() == PipelineResultChange::npos ? NSNotFound : change_.old_index(); |
| 974 | +} |
| 975 | + |
| 976 | +- (NSUInteger)newIndex { |
| 977 | + return change_.new_index() == PipelineResultChange::npos ? NSNotFound : change_.new_index(); |
| 978 | +} |
| 979 | + |
| 980 | +- (id)initWithCppChange:(api::PipelineResultChange)change db:(std::shared_ptr<api::Firestore>)db { |
| 981 | + self = [super init]; |
| 982 | + if (self) { |
| 983 | + change_ = std::move(change); |
| 984 | + db_ = std::move(db); |
| 985 | + } |
| 986 | + |
| 987 | + return self; |
| 988 | +} |
| 989 | + |
| 990 | +@end |
| 991 | + |
931 | 992 | @implementation FIRPipelineBridge {
|
932 | 993 | NSArray<FIRStageBridge *> *_stages;
|
933 | 994 | FIRFirestore *firestore;
|
@@ -965,4 +1026,195 @@ - (void)executeWithCompletion:(void (^)(__FIRPipelineSnapshotBridge *_Nullable r
|
965 | 1026 |
|
966 | 1027 | @end
|
967 | 1028 |
|
| 1029 | +@interface __FIRRealtimePipelineSnapshotBridge () |
| 1030 | + |
| 1031 | +@property(nonatomic, strong, readwrite) NSArray<__FIRPipelineResultBridge *> *results; |
| 1032 | + |
| 1033 | +@property(nonatomic, strong, readwrite) NSArray<__FIRPipelineResultChangeBridge *> *changes; |
| 1034 | + |
| 1035 | +@end |
| 1036 | + |
| 1037 | +@implementation __FIRRealtimePipelineSnapshotBridge { |
| 1038 | + absl::optional<api::RealtimePipelineSnapshot> snapshot_; |
| 1039 | + NSMutableArray<__FIRPipelineResultBridge *> *results_; |
| 1040 | + NSMutableArray<__FIRPipelineResultChangeBridge *> *changes_; |
| 1041 | + FIRSnapshotMetadata *_metadata; |
| 1042 | +} |
| 1043 | + |
| 1044 | +- (id)initWithCppSnapshot:(api::RealtimePipelineSnapshot)snapshot { |
| 1045 | + self = [super init]; |
| 1046 | + if (self) { |
| 1047 | + snapshot_ = std::move(snapshot); |
| 1048 | + if (!snapshot_.has_value()) { |
| 1049 | + results_ = nil; |
| 1050 | + } else { |
| 1051 | + _metadata = |
| 1052 | + [[FIRSnapshotMetadata alloc] initWithMetadata:snapshot_.value().snapshot_metadata()]; |
| 1053 | + |
| 1054 | + NSMutableArray<__FIRPipelineResultBridge *> *results = [NSMutableArray array]; |
| 1055 | + for (auto &result : snapshot_.value().view_snapshot().documents()) { |
| 1056 | + [results addObject:[[__FIRPipelineResultBridge alloc] |
| 1057 | + initWithCppResult:api::PipelineResult(result) |
| 1058 | + db:snapshot_.value().firestore()]]; |
| 1059 | + } |
| 1060 | + results_ = results; |
| 1061 | + |
| 1062 | + NSMutableArray<__FIRPipelineResultChangeBridge *> *changes = [NSMutableArray array]; |
| 1063 | + for (auto &change : snapshot_.value().CalculateResultChanges(false)) { |
| 1064 | + [changes addObject:[[__FIRPipelineResultChangeBridge alloc] |
| 1065 | + initWithCppChange:change |
| 1066 | + db:snapshot_.value().firestore()]]; |
| 1067 | + } |
| 1068 | + changes_ = changes; |
| 1069 | + } |
| 1070 | + } |
| 1071 | + |
| 1072 | + return self; |
| 1073 | +} |
| 1074 | + |
| 1075 | +- (NSArray<__FIRPipelineResultBridge *> *)results { |
| 1076 | + return results_; |
| 1077 | +} |
| 1078 | + |
| 1079 | +- (NSArray<__FIRPipelineResultChangeBridge *> *)changes { |
| 1080 | + return changes_; |
| 1081 | +} |
| 1082 | + |
| 1083 | +- (FIRSnapshotMetadata *)metadata { |
| 1084 | + return _metadata; |
| 1085 | +} |
| 1086 | + |
| 1087 | +@end |
| 1088 | + |
| 1089 | +@implementation __FIRPipelineListenOptionsBridge |
| 1090 | + |
| 1091 | +- (instancetype)initWithServerTimestampBehavior:(NSString *)serverTimestampBehavior |
| 1092 | + includeMetadata:(BOOL)includeMetadata |
| 1093 | + source:(FIRListenSource)source { |
| 1094 | + // Call the designated initializer of the superclass (NSObject). |
| 1095 | + self = [super init]; |
| 1096 | + if (self) { |
| 1097 | + // Assign the passed-in values to the backing instance variables |
| 1098 | + // for the readonly properties. |
| 1099 | + // We use `copy` here for the string to ensure our object owns an immutable version. |
| 1100 | + _serverTimestampBehavior = [serverTimestampBehavior copy]; |
| 1101 | + _includeMetadata = includeMetadata; |
| 1102 | + _source = source; |
| 1103 | + } |
| 1104 | + return self; |
| 1105 | +} |
| 1106 | + |
| 1107 | +@end |
| 1108 | + |
| 1109 | +@implementation FIRRealtimePipelineBridge { |
| 1110 | + NSArray<FIRStageBridge *> *_stages; |
| 1111 | + FIRFirestore *firestore; |
| 1112 | + std::shared_ptr<api::RealtimePipeline> cpp_pipeline; |
| 1113 | +} |
| 1114 | + |
| 1115 | +- (id)initWithStages:(NSArray<FIRStageBridge *> *)stages db:(FIRFirestore *)db { |
| 1116 | + _stages = stages; |
| 1117 | + firestore = db; |
| 1118 | + return [super init]; |
| 1119 | +} |
| 1120 | + |
| 1121 | +core::ListenOptions ToListenOptions(__FIRPipelineListenOptionsBridge *_Nullable bridge) { |
| 1122 | + // If the bridge object is nil, return a default-constructed ListenOptions. |
| 1123 | + if (bridge == nil) { |
| 1124 | + return core::ListenOptions::DefaultOptions(); |
| 1125 | + } |
| 1126 | + |
| 1127 | + // 1. Translate include_metadata_changes |
| 1128 | + bool include_metadata = bridge.includeMetadata; |
| 1129 | + |
| 1130 | + // 2. Translate ListenSource |
| 1131 | + core::ListenSource source = core::ListenSource::Default; |
| 1132 | + switch (bridge.source) { |
| 1133 | + case FIRListenSourceDefault: |
| 1134 | + source = core::ListenSource::Default; |
| 1135 | + break; |
| 1136 | + case FIRListenSourceCache: |
| 1137 | + source = core::ListenSource::Cache; |
| 1138 | + break; |
| 1139 | + } |
| 1140 | + |
| 1141 | + // 3. Translate ServerTimestampBehavior |
| 1142 | + core::ListenOptions::ServerTimestampBehavior behavior = |
| 1143 | + core::ListenOptions::ServerTimestampBehavior::kNone; |
| 1144 | + if ([bridge.serverTimestampBehavior isEqual:@"estimate"]) { |
| 1145 | + behavior = core::ListenOptions::ServerTimestampBehavior::kEstimate; |
| 1146 | + } else if ([bridge.serverTimestampBehavior isEqual:@"previous"]) { |
| 1147 | + behavior = core::ListenOptions::ServerTimestampBehavior::kPrevious; |
| 1148 | + } else { |
| 1149 | + // "none" or any other value defaults to kNone. |
| 1150 | + behavior = core::ListenOptions::ServerTimestampBehavior::kNone; |
| 1151 | + } |
| 1152 | + |
| 1153 | + // 4. Construct the final C++ object using the canonical private constructor. |
| 1154 | + // Note: wait_for_sync_when_online is not part of the bridge, so we use 'false' |
| 1155 | + // to match the behavior of the existing static factories. |
| 1156 | + return core::ListenOptions( |
| 1157 | + /*include_query_metadata_changes=*/include_metadata, |
| 1158 | + /*include_document_metadata_changes=*/include_metadata, |
| 1159 | + /*wait_for_sync_when_online=*/false, source, behavior); |
| 1160 | +} |
| 1161 | + |
| 1162 | +- (id<FIRListenerRegistration>) |
| 1163 | + addSnapshotListenerWithOptions:(__FIRPipelineListenOptionsBridge *)options |
| 1164 | + listener: |
| 1165 | + (void (^)(__FIRRealtimePipelineSnapshotBridge *_Nullable snapshot, |
| 1166 | + NSError *_Nullable error))listener { |
| 1167 | + std::shared_ptr<api::Firestore> wrapped_firestore = firestore.wrapped; |
| 1168 | + |
| 1169 | + std::vector<std::shared_ptr<firebase::firestore::api::EvaluableStage>> cpp_stages; |
| 1170 | + for (FIRStageBridge *stage in _stages) { |
| 1171 | + auto evaluable_stage = std::dynamic_pointer_cast<api::EvaluableStage>( |
| 1172 | + [stage cppStageWithReader:firestore.dataReader]); |
| 1173 | + if (evaluable_stage) { |
| 1174 | + cpp_stages.push_back(evaluable_stage); |
| 1175 | + } else { |
| 1176 | + HARD_FAIL("Failed to convert cpp stage to EvaluableStage for RealtimePipeline"); |
| 1177 | + } |
| 1178 | + } |
| 1179 | + |
| 1180 | + cpp_pipeline = std::make_shared<RealtimePipeline>( |
| 1181 | + cpp_stages, std::make_unique<remote::Serializer>(wrapped_firestore->database_id())); |
| 1182 | + |
| 1183 | + // Convert from ViewSnapshots to RealtimePipelineSnapshots. |
| 1184 | + auto view_listener = EventListener<ViewSnapshot>::Create( |
| 1185 | + [listener, wrapped_firestore](StatusOr<ViewSnapshot> maybe_snapshot) { |
| 1186 | + if (!maybe_snapshot.status().ok()) { |
| 1187 | + listener(nil, MakeNSError(maybe_snapshot.status())); |
| 1188 | + return; |
| 1189 | + } |
| 1190 | + |
| 1191 | + ViewSnapshot snapshot = std::move(maybe_snapshot).ValueOrDie(); |
| 1192 | + SnapshotMetadata metadata(snapshot.has_pending_writes(), snapshot.from_cache()); |
| 1193 | + |
| 1194 | + listener( |
| 1195 | + [[__FIRRealtimePipelineSnapshotBridge alloc] |
| 1196 | + initWithCppSnapshot:RealtimePipelineSnapshot(wrapped_firestore, std::move(snapshot), |
| 1197 | + std::move(metadata))], |
| 1198 | + nil); |
| 1199 | + }); |
| 1200 | + |
| 1201 | + // Call the view_listener on the user Executor. |
| 1202 | + auto async_listener = core::AsyncEventListener<ViewSnapshot>::Create( |
| 1203 | + wrapped_firestore->client()->user_executor(), std::move(view_listener)); |
| 1204 | + |
| 1205 | + std::shared_ptr<core::QueryListener> query_listener = wrapped_firestore->client()->ListenToQuery( |
| 1206 | + *cpp_pipeline, ToListenOptions(options), async_listener); |
| 1207 | + |
| 1208 | + return [[FSTListenerRegistration alloc] |
| 1209 | + initWithRegistration:absl::make_unique<QueryListenerRegistration>(wrapped_firestore->client(), |
| 1210 | + std::move(async_listener), |
| 1211 | + std::move(query_listener))]; |
| 1212 | +} |
| 1213 | + |
| 1214 | +- (std::shared_ptr<api::RealtimePipeline>)cppPipelineWithReader:(FSTUserDataReader *)reader { |
| 1215 | + return cpp_pipeline; |
| 1216 | +} |
| 1217 | + |
| 1218 | +@end |
| 1219 | + |
968 | 1220 | NS_ASSUME_NONNULL_END
|
0 commit comments