[firestore][ios] Add document onSnapshot functionality
This commit is contained in:
parent
cda1c27b5c
commit
d40f464f1c
|
@ -16,6 +16,10 @@ static NSString *const DATABASE_CHILD_MODIFIED_EVENT = @"child_changed";
|
|||
static NSString *const DATABASE_CHILD_REMOVED_EVENT = @"child_removed";
|
||||
static NSString *const DATABASE_CHILD_MOVED_EVENT = @"child_moved";
|
||||
|
||||
// Firestore
|
||||
static NSString *const FIRESTORE_COLLECTION_SYNC_EVENT = @"firestore_collection_sync_event";
|
||||
static NSString *const FIRESTORE_DOCUMENT_SYNC_EVENT = @"firestore_document_sync_event";
|
||||
|
||||
// Storage
|
||||
static NSString *const STORAGE_EVENT = @"storage_event";
|
||||
static NSString *const STORAGE_ERROR = @"storage_error";
|
||||
|
|
|
@ -10,10 +10,13 @@
|
|||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
@interface RNFirebaseFirestore : RCTEventEmitter <RCTBridgeModule> {}
|
||||
@property NSMutableDictionary *collectionReferences;
|
||||
@property NSMutableDictionary *documentReferences;
|
||||
|
||||
+ (void)promiseRejectException:(RCTPromiseRejectBlock)reject error:(NSError *)error;
|
||||
|
||||
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appName;
|
||||
+ (NSDictionary *)getJSError:(NSError *)nativeError;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ RCT_EXPORT_MODULE();
|
|||
- (id)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
|
||||
_documentReferences = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -109,6 +109,24 @@ RCT_EXPORT_METHOD(documentGetAll:(NSString *) appName
|
|||
// Not supported on iOS out of the box
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(documentOffSnapshot:(NSString *) appName
|
||||
path:(NSString *) path
|
||||
listenerId:(nonnull NSNumber *) listenerId) {
|
||||
RNFirebaseFirestoreDocumentReference *ref = [self getCachedDocumentForAppPath:appName path:path];
|
||||
[ref offSnapshot:listenerId];
|
||||
|
||||
if (![ref hasListeners]) {
|
||||
[self clearCachedDocumentForAppPath:appName path:path];
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(documentOnSnapshot:(NSString *) appName
|
||||
path:(NSString *) path
|
||||
listenerId:(nonnull NSNumber *) listenerId) {
|
||||
RNFirebaseFirestoreDocumentReference *ref = [self getCachedDocumentForAppPath:appName path:path];
|
||||
[ref onSnapshot:listenerId];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(documentSet:(NSString *) appName
|
||||
path:(NSString *) path
|
||||
data:(NSDictionary *) data
|
||||
|
@ -130,10 +148,8 @@ RCT_EXPORT_METHOD(documentUpdate:(NSString *) appName
|
|||
* INTERNALS/UTILS
|
||||
*/
|
||||
+ (void)promiseRejectException:(RCTPromiseRejectBlock)reject error:(NSError *)error {
|
||||
// TODO
|
||||
// NSDictionary *jsError = [RNFirebaseDatabase getJSError:databaseError];
|
||||
// reject([jsError valueForKey:@"code"], [jsError valueForKey:@"message"], databaseError);
|
||||
reject(@"TODO", [error description], error);
|
||||
NSDictionary *jsError = [RNFirebaseFirestore getJSError:error];
|
||||
reject([jsError valueForKey:@"code"], [jsError valueForKey:@"message"], error);
|
||||
}
|
||||
|
||||
+ (FIRFirestore *)getFirestoreForApp:(NSString *)appName {
|
||||
|
@ -145,12 +161,60 @@ RCT_EXPORT_METHOD(documentUpdate:(NSString *) appName
|
|||
return [[RNFirebaseFirestoreCollectionReference alloc] initWithPathAndModifiers:appName path:path filters:filters orders:orders options:options];
|
||||
}
|
||||
|
||||
- (RNFirebaseFirestoreDocumentReference *)getCachedDocumentForAppPath:(NSString *)appName path:(NSString *)path {
|
||||
NSString *key = [NSString stringWithFormat:@"%@/%@", appName, path];
|
||||
RNFirebaseFirestoreDocumentReference *ref = _documentReferences[key];
|
||||
|
||||
if (ref == nil) {
|
||||
ref = [self getDocumentForAppPath:appName path:path];
|
||||
_documentReferences[key] = ref;
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
- (void)clearCachedDocumentForAppPath:(NSString *)appName path:(NSString *)path {
|
||||
NSString *key = [NSString stringWithFormat:@"%@/%@", appName, path];
|
||||
[_documentReferences removeObjectForKey:key];
|
||||
}
|
||||
|
||||
- (RNFirebaseFirestoreDocumentReference *)getDocumentForAppPath:(NSString *)appName path:(NSString *)path {
|
||||
return [[RNFirebaseFirestoreDocumentReference alloc] initWithPath:appName path:path];
|
||||
return [[RNFirebaseFirestoreDocumentReference alloc] initWithPath:self app:appName path:path];
|
||||
}
|
||||
|
||||
// TODO: Move to error util for use in other modules
|
||||
+ (NSString *)getMessageWithService:(NSString *)message service:(NSString *)service fullCode:(NSString *)fullCode {
|
||||
return [NSString stringWithFormat:@"%@: %@ (%@).", service, message, [fullCode lowercaseString]];
|
||||
}
|
||||
|
||||
+ (NSString *)getCodeWithService:(NSString *)service code:(NSString *)code {
|
||||
return [NSString stringWithFormat:@"%@/%@", [service lowercaseString], [code lowercaseString]];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)getJSError:(NSError *)nativeError {
|
||||
NSMutableDictionary *errorMap = [[NSMutableDictionary alloc] init];
|
||||
[errorMap setValue:@(nativeError.code) forKey:@"nativeErrorCode"];
|
||||
[errorMap setValue:[nativeError localizedDescription] forKey:@"nativeErrorMessage"];
|
||||
|
||||
NSString *code;
|
||||
NSString *message;
|
||||
NSString *service = @"Firestore";
|
||||
|
||||
// TODO: Proper error codes
|
||||
switch (nativeError.code) {
|
||||
default:
|
||||
code = [RNFirebaseFirestore getCodeWithService:service code:@"unknown"];
|
||||
message = [RNFirebaseFirestore getMessageWithService:@"An unknown error occurred." service:service fullCode:code];
|
||||
break;
|
||||
}
|
||||
|
||||
[errorMap setValue:code forKey:@"code"];
|
||||
[errorMap setValue:message forKey:@"message"];
|
||||
|
||||
return errorMap;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)supportedEvents {
|
||||
return @[DATABASE_SYNC_EVENT, DATABASE_TRANSACTION_EVENT];
|
||||
return @[FIRESTORE_COLLECTION_SYNC_EVENT, FIRESTORE_DOCUMENT_SYNC_EVENT];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -6,20 +6,27 @@
|
|||
#if __has_include(<Firestore/FIRFirestore.h>)
|
||||
|
||||
#import <Firestore/Firestore.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
#import "RNFirebaseEvents.h"
|
||||
#import "RNFirebaseFirestore.h"
|
||||
|
||||
@interface RNFirebaseFirestoreDocumentReference : NSObject
|
||||
@property RCTEventEmitter *emitter;
|
||||
@property NSString *app;
|
||||
@property NSString *path;
|
||||
@property FIRDocumentReference *ref;
|
||||
@property NSMutableDictionary *listeners;
|
||||
|
||||
- (id)initWithPath:(NSString *)app path:(NSString *)path;
|
||||
- (id)initWithPath:(RCTEventEmitter *)emitter app:(NSString *)app path:(NSString *)path;
|
||||
- (void)collections:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||
- (void)create:(NSDictionary *)data resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||
- (void)delete:(NSDictionary *)options resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||
- (void)offSnapshot:(NSNumber *)listenerId;
|
||||
- (void)onSnapshot:(NSNumber *)listenerId;
|
||||
- (void)set:(NSDictionary *)data options:(NSDictionary *)options resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||
- (void)update:(NSDictionary *)data resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||
- (BOOL)hasListeners;
|
||||
+ (NSDictionary *)snapshotToDictionary:(FIRDocumentSnapshot *)documentSnapshot;
|
||||
@end
|
||||
|
||||
|
|
|
@ -4,13 +4,16 @@
|
|||
|
||||
#if __has_include(<Firestore/FIRFirestore.h>)
|
||||
|
||||
- (id)initWithPath:(NSString *) app
|
||||
- (id)initWithPath:(RCTEventEmitter *)emitter
|
||||
app:(NSString *) app
|
||||
path:(NSString *) path {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_emitter = emitter;
|
||||
_app = app;
|
||||
_path = path;
|
||||
_ref = [[RNFirebaseFirestore getFirestoreForApp:_app] documentWithPath:_path];
|
||||
_listeners = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -46,6 +49,34 @@
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)offSnapshot:(NSNumber *) listenerId {
|
||||
id<FIRListenerRegistration> listener = _listeners[listenerId];
|
||||
if (listener) {
|
||||
[_listeners removeObjectForKey:listenerId];
|
||||
[listener remove];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onSnapshot:(NSNumber *) listenerId {
|
||||
if (_listeners[listenerId] == nil) {
|
||||
id listenerBlock = ^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
||||
if (error) {
|
||||
id<FIRListenerRegistration> listener = _listeners[listenerId];
|
||||
if (listener) {
|
||||
[_listeners removeObjectForKey:listenerId];
|
||||
[listener remove];
|
||||
}
|
||||
[self handleDocumentSnapshotError:listenerId error:error];
|
||||
} else {
|
||||
[self handleDocumentSnapshotEvent:listenerId documentSnapshot:snapshot];
|
||||
}
|
||||
};
|
||||
|
||||
id<FIRListenerRegistration> listener = [_ref addSnapshotListener:listenerBlock];
|
||||
_listeners[listenerId] = listener;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)set:(NSDictionary *) data
|
||||
options:(NSDictionary *) options
|
||||
resolver:(RCTPromiseResolveBlock) resolve
|
||||
|
@ -69,6 +100,10 @@
|
|||
}];
|
||||
}
|
||||
|
||||
- (BOOL)hasListeners {
|
||||
return [[_listeners allKeys] count] > 0;
|
||||
}
|
||||
|
||||
+ (void)handleWriteResponse:(NSError *) error
|
||||
resolver:(RCTPromiseResolveBlock) resolve
|
||||
rejecter:(RCTPromiseRejectBlock) reject {
|
||||
|
@ -95,6 +130,28 @@
|
|||
return snapshot;
|
||||
}
|
||||
|
||||
- (void)handleDocumentSnapshotError:(NSNumber *)listenerId
|
||||
error:(NSError *)error {
|
||||
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
|
||||
[event setValue:_app forKey:@"appName"];
|
||||
[event setValue:_path forKey:@"path"];
|
||||
[event setValue:listenerId forKey:@"listenerId"];
|
||||
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
|
||||
|
||||
[_emitter sendEventWithName:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
|
||||
}
|
||||
|
||||
- (void)handleDocumentSnapshotEvent:(NSNumber *)listenerId
|
||||
documentSnapshot:(FIRDocumentSnapshot *)documentSnapshot {
|
||||
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
|
||||
[event setValue:_app forKey:@"appName"];
|
||||
[event setValue:_path forKey:@"path"];
|
||||
[event setValue:listenerId forKey:@"listenerId"];
|
||||
[event setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentSnapshot] forKey:@"document"];
|
||||
|
||||
[_emitter sendEventWithName:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue