Skip to content

Commit

Permalink
Merge pull request #5 from hansemannn/fix/mapped-timestamp
Browse files Browse the repository at this point in the history
fix: map timestamp correctly, add "queryDocuments"
  • Loading branch information
hansemannn authored Jul 17, 2024
2 parents 36178e8 + 76f8b0a commit 778b2f1
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 33 deletions.
2 changes: 2 additions & 0 deletions example/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ function performCRUD() {

console.warn('Updated in Firestore successfully!');

// NOTE: You can also use "queryDocuments" to apply filters. See the source for details!

TiFirestore.getDocuments({
collection: 'users',
callback: event => {
Expand Down
2 changes: 1 addition & 1 deletion ios/Classes/FirebaseFirestoreFieldValueProxy.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ - (id)_initWithPageContext:(id<TiEvaluator>)context andFieldValue:(FIRFieldValue
if (self = [super _initWithPageContext:pageContext]) {
_fieldValue = fieldValue;
}

return self;
}

Expand Down
20 changes: 13 additions & 7 deletions ios/Classes/FirebaseFirestoreModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,37 @@
* Created by Hans Knöchel
*/

#import "TiModule.h"
#import "FirebaseFirestoreFieldValueProxy.h"
#import "TiModule.h"

@interface FirebaseFirestoreModule : TiModule

/**
Adds a new document to the provided Firestore collection.
- Parameter callback: The callback to be invoked if either the document was added or an error occurred.
- Parameter collection: The name of the collection.
- Parameter data: The data to save in the provided collection.
*/
- (void)addDocument:(id)params;

/**
Returns a list of documents saved in the provided Firestore collection. Different to `getDocuments`, you can also pass
filteres here.
*/
- (void)queryDocuments:(id)params;

/**
Returns a list of documents saved in the provided Firestore collection.
- Parameter callback: The callback to be invoked if either the documents were fetched or an error occurred.
- Parameter collection: The name of the collection.
*/
- (void)getDocuments:(id)params;

/**
Returns a single doc saved in the provided Firestore collection.
- Parameter callback: The callback to be invoked if either the documents were fetched or an error occurred.
- Parameter collection: The name of the collection.
- Parameter document: The name of the document.
Expand All @@ -37,7 +43,7 @@

/**
Updates an extisting document from the provided Firestore collection.
- Parameter callback: The callback to be invoked if either the document was updated or an error occurred.
- Parameter collection: The name of the collection.
- Parameter data: The data to update in the provided collection.
Expand All @@ -47,7 +53,7 @@

/**
Removes a new document from the provided Firestore collection.
- Parameter callback: The callback to be invoked if either the document was removed or an error occurred.
- Parameter collection: The name of the collection.
- Parameter document: The document to delete.
Expand All @@ -56,7 +62,7 @@

/**
Returns a special value that tells the server to increment the field's current value by the given value.
- Parameter value: The value to increment.
*/
- (FirebaseFirestoreFieldValueProxy *)increment:(id)value;
Expand Down
165 changes: 142 additions & 23 deletions ios/Classes/FirebaseFirestoreModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#import "FirebaseFirestoreModule.h"
#import "TiBase.h"
#import "TiFirestoreUtils.h"
#import "TiHost.h"
#import "TiUtils.h"

Expand Down Expand Up @@ -34,13 +35,119 @@ - (void)addDocument:(id)params
NSDictionary *data = params[@"data"]; // TODO: Parse "FIRFieldValue" proxy types

__block FIRDocumentReference *ref = [[FIRFirestore.firestore collectionWithPath:collection] addDocumentWithData:data
completion:^(NSError * _Nullable error) {
if (error != nil) {
[callback call:@[@{ @"success": @(NO), @"error": error.localizedDescription }] thisObject:self];
completion:^(NSError *_Nullable error) {
if (error != nil) {
[callback call:@[ @{@"success" : @(NO),
@"error" : error.localizedDescription} ]
thisObject:self];
return;
}

[callback call:@[ @{@"success" : @(YES),
@"documentID" : NULL_IF_NIL(ref.documentID),
@"documentPath" : NULL_IF_NIL(ref.path)} ]
thisObject:self];
}];
}

- (void)queryDocuments:(id)params
{
ENSURE_SINGLE_ARG(params, NSDictionary);
KrollCallback *callback = params[@"callback"];
NSString *collection = params[@"collection"];
NSString *document = params[@"document"];

FIRCollectionReference *ref = [FIRFirestore.firestore collectionWithPath:collection];
FIRDocumentReference *documentReference = [[FIRFirestore.firestore collectionWithPath:collection] documentWithPath:document];
FIRQuery *query = [FIRFirestore.firestore collectionWithPath:params[@"path"]];

NSDictionary *parameters = params[@"parameters"];
NSArray *whereConditions = params[@"where"];

for (id item in whereConditions) {
NSArray *condition = item;
NSString *fieldName = condition[0];
NSString *op = condition[1];
id value = condition[2];
if ([op isEqualToString:@"=="]) {
query = [query queryWhereField:fieldName isEqualTo:value];
} else if ([op isEqualToString:@"<"]) {
query = [query queryWhereField:fieldName isLessThan:value];
} else if ([op isEqualToString:@"<="]) {
query = [query queryWhereField:fieldName isLessThanOrEqualTo:value];
} else if ([op isEqualToString:@">"]) {
query = [query queryWhereField:fieldName isGreaterThan:value];
} else if ([op isEqualToString:@">="]) {
query = [query queryWhereField:fieldName isGreaterThanOrEqualTo:value];
} else if ([op isEqualToString:@"array-contains"]) {
query = [query queryWhereField:fieldName arrayContains:value];
} else {
NSLog(@"[ERROR] Unhandled operator \"%@\" on field \"%@\"", op, fieldName);
// Unsupported operator
}
}

id limit = parameters[@"limit"];
if (limit) {
NSNumber *length = limit;
query = [query queryLimitedTo:[length intValue]];
}

NSArray *orderBy = parameters[@"orderBy"];
if (orderBy) {
for (id item in orderBy) {
NSArray *orderByParameters = item;
NSString *fieldName = orderByParameters[0];
NSNumber *descending = orderByParameters[1];
query = [query queryOrderedByField:fieldName descending:[descending boolValue]];
}
}

id startAt = parameters[@"startAt"];
if (startAt) {
NSArray *startAtValues = startAt;
query = [query queryStartingAtValues:startAtValues];
}

id startAfter = parameters[@"startAfter"];
if (startAfter) {
NSArray *startAfterValues = startAfter;
query = [query queryStartingAfterValues:startAfterValues];
}

id endAt = parameters[@"endAt"];
if (endAt) {
NSArray *endAtValues = endAt;
query = [query queryEndingAtValues:endAtValues];
}

id endBefore = parameters[@"endBefore"];
if (endBefore) {
NSArray *endBeforeValues = endBefore;
query = [query queryEndingBeforeValues:endBeforeValues];
}

[query getDocumentsWithCompletion:^(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error) {
if (error != nil) {
[callback call:@[ @{
@"success" : @(NO),
@"error" : error.localizedDescription
} ]
thisObject:self];
return;
}

[callback call:@[@{ @"success": @(YES), @"documentID": NULL_IF_NIL(ref.documentID), @"documentPath": NULL_IF_NIL(ref.path) }] thisObject:self];
NSMutableArray<NSDictionary<NSString *, id> *> *documents = [NSMutableArray arrayWithCapacity:snapshot.documents.count];

// Map the documents to make sure it's a bridgeable type
[snapshot.documents enumerateObjectsUsingBlock:^(FIRQueryDocumentSnapshot *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
[documents addObject:[TiFirestoreUtils mappedFirestoreValue:obj.data]];
}];
[callback call:@[ @{
@"success" : @(YES),
@"documents" : documents
} ]
thisObject:self];
}];
}

Expand All @@ -51,19 +158,23 @@ - (void)getDocuments:(id)params
KrollCallback *callback = params[@"callback"];
NSString *collection = params[@"collection"];

[[FIRFirestore.firestore collectionWithPath:collection] getDocumentsWithCompletion:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
[[FIRFirestore.firestore collectionWithPath:collection] getDocumentsWithCompletion:^(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error) {
if (error != nil) {
[callback call:@[@{ @"success": @(NO), @"error": error.localizedDescription }] thisObject:self];
[callback call:@[ @{@"success" : @(NO),
@"error" : error.localizedDescription} ]
thisObject:self];
return;
}

NSMutableArray<NSDictionary<NSString *, id> *> *documents = [NSMutableArray arrayWithCapacity:snapshot.documents.count];

[snapshot.documents enumerateObjectsUsingBlock:^(FIRQueryDocumentSnapshot * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[documents addObject:[obj data]];
[snapshot.documents enumerateObjectsUsingBlock:^(FIRQueryDocumentSnapshot *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
[documents addObject:[TiFirestoreUtils mappedFirestoreValue:obj.data]];
}];

[callback call:@[@{ @"success": @(YES), @"documents": documents }] thisObject:self];
[callback call:@[ @{@"success" : @(YES),
@"documents" : documents} ]
thisObject:self];
}];
}

Expand All @@ -77,13 +188,17 @@ - (void)getSingleDocument:(id)params

FIRDocumentReference *documentReference = [[FIRFirestore.firestore collectionWithPath:collection] documentWithPath:document];

[documentReference getDocumentWithCompletion:^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
[documentReference getDocumentWithCompletion:^(FIRDocumentSnapshot *_Nullable snapshot, NSError *_Nullable error) {
if (error != nil) {
[callback call:@[@{ @"success": @(NO), @"error": error.localizedDescription }] thisObject:self];
[callback call:@[ @{@"success" : @(NO),
@"error" : error.localizedDescription} ]
thisObject:self];
return;
}

[callback call:@[@{ @"success": @(YES), @"document": [snapshot data] }] thisObject:self];
[callback call:@[ @{@"success" : @(YES),
@"document" : [snapshot data]} ]
thisObject:self];
}];
}

Expand All @@ -97,14 +212,16 @@ - (void)updateDocument:(id)params
NSString *document = params[@"document"];

[[[FIRFirestore.firestore collectionWithPath:collection] documentWithPath:document] updateData:data
completion:^(NSError * _Nullable error) {
if (error != nil) {
[callback call:@[@{ @"success": @(NO), @"error": error.localizedDescription }] thisObject:self];
return;
}
completion:^(NSError *_Nullable error) {
if (error != nil) {
[callback call:@[ @{@"success" : @(NO),
@"error" : error.localizedDescription} ]
thisObject:self];
return;
}

[callback call:@[@{ @"success": @(YES) }] thisObject:self];
}];
[callback call:@[ @{@"success" : @(YES)} ] thisObject:self];
}];
}

- (void)deleteDocument:(id)params
Expand All @@ -116,20 +233,22 @@ - (void)deleteDocument:(id)params
NSDictionary *data = params[@"data"];
NSString *document = params[@"document"];

[[[FIRFirestore.firestore collectionWithPath:collection] documentWithPath:document] deleteDocumentWithCompletion:^(NSError * _Nullable error) {
[[[FIRFirestore.firestore collectionWithPath:collection] documentWithPath:document] deleteDocumentWithCompletion:^(NSError *_Nullable error) {
if (error != nil) {
[callback call:@[@{ @"success": @(NO), @"error": error.localizedDescription }] thisObject:self];
[callback call:@[ @{@"success" : @(NO),
@"error" : error.localizedDescription} ]
thisObject:self];
return;
}

[callback call:@[@{ @"success": @(YES) }] thisObject:self];
[callback call:@[ @{@"success" : @(YES)} ] thisObject:self];
}];
}

- (FirebaseFirestoreFieldValueProxy *)increment:(id)value
{
ENSURE_SINGLE_ARG(value, NSNumber);

FIRFieldValue *fieldValue = [FIRFieldValue fieldValueForIntegerIncrement:[TiUtils intValue:value]];
return [[FirebaseFirestoreFieldValueProxy alloc] _initWithPageContext:pageContext andFieldValue:fieldValue];
}
Expand Down
18 changes: 18 additions & 0 deletions ios/Classes/TiFirestoreUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// TiFirestoreUtils.h
// titanium-firebase-firestore
//
// Created by Hans Knöchel on 06.12.22.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface TiFirestoreUtils : NSObject

+ (NSDictionary *)mappedFirestoreValue:(id)value;

@end

NS_ASSUME_NONNULL_END
34 changes: 34 additions & 0 deletions ios/Classes/TiFirestoreUtils.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// TiFirestoreUtils.m
// titanium-firebase-firestore
//
// Created by Hans Knöchel on 06.12.22.
//

#import "TiFirestoreUtils.h"
#import <FirebaseFirestore/FirebaseFirestore.h>

@implementation TiFirestoreUtils

+ (NSDictionary *)mappedFirestoreValue:(NSDictionary<NSString *, id> *)value
{
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];

[value enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL *_Nonnull stop) {
// Handle timestamps as a native type
if ([value isKindOfClass:[FIRTimestamp class]]) {
FIRTimestamp *timestamp = (FIRTimestamp *)value;
result[key] = @{
@"seconds" : @(timestamp.seconds),
@"nanoseconds" : @(timestamp.nanoseconds)
};
// Handle all other values directly
} else {
result[key] = value;
}
}];

return result;
}

@end
2 changes: 1 addition & 1 deletion ios/manifest
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# this is your module manifest and used by Titanium
# during compilation, packaging, distribution, etc.
#
version: 1.0.1
version: 1.0.2
apiversion: 2
architectures: arm64 x86_64
mac: false
Expand Down
Loading

0 comments on commit 778b2f1

Please sign in to comment.