aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore/Source/API/FIRTransaction.mm
blob: b5bdefa8a086ef8c6f5e495a191c5f5ed6b3d4a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
 * Copyright 2017 Google
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#import "Firestore/Source/API/FIRTransaction+Internal.h"

#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
#import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h"
#import "Firestore/Source/API/FIRFirestore+Internal.h"
#import "Firestore/Source/API/FSTUserDataConverter.h"
#import "Firestore/Source/Core/FSTTransaction.h"
#import "Firestore/Source/Model/FSTDocument.h"
#import "Firestore/Source/Util/FSTAssert.h"
#import "Firestore/Source/Util/FSTUsageValidation.h"

NS_ASSUME_NONNULL_BEGIN

#pragma mark - FIRTransaction

@interface FIRTransaction ()

- (instancetype)initWithTransaction:(FSTTransaction *)transaction
                          firestore:(FIRFirestore *)firestore NS_DESIGNATED_INITIALIZER;

@property(nonatomic, strong, readonly) FSTTransaction *internalTransaction;
@property(nonatomic, strong, readonly) FIRFirestore *firestore;
@end

@implementation FIRTransaction (Internal)

+ (instancetype)transactionWithFSTTransaction:(FSTTransaction *)transaction
                                    firestore:(FIRFirestore *)firestore {
  return [[FIRTransaction alloc] initWithTransaction:transaction firestore:firestore];
}

@end

@implementation FIRTransaction

- (instancetype)initWithTransaction:(FSTTransaction *)transaction
                          firestore:(FIRFirestore *)firestore {
  self = [super init];
  if (self) {
    _internalTransaction = transaction;
    _firestore = firestore;
  }
  return self;
}

- (FIRTransaction *)setData:(NSDictionary<NSString *, id> *)data
                forDocument:(FIRDocumentReference *)document {
  return [self setData:data forDocument:document merge:NO];
}

- (FIRTransaction *)setData:(NSDictionary<NSString *, id> *)data
                forDocument:(FIRDocumentReference *)document
                      merge:(BOOL)merge {
  [self validateReference:document];
  FSTParsedSetData *parsed = merge
                                 ? [self.firestore.dataConverter parsedMergeData:data fieldMask:nil]
                                 : [self.firestore.dataConverter parsedSetData:data];
  [self.internalTransaction setData:parsed forDocument:document.key];
  return self;
}

- (FIRTransaction *)setData:(NSDictionary<NSString *, id> *)data
                forDocument:(FIRDocumentReference *)document
                mergeFields:(NSArray<id> *)mergeFields {
  [self validateReference:document];
  FSTParsedSetData *parsed =
      [self.firestore.dataConverter parsedMergeData:data fieldMask:mergeFields];
  [self.internalTransaction setData:parsed forDocument:document.key];
  return self;
}

- (FIRTransaction *)updateData:(NSDictionary<id, id> *)fields
                   forDocument:(FIRDocumentReference *)document {
  [self validateReference:document];
  FSTParsedUpdateData *parsed = [self.firestore.dataConverter parsedUpdateData:fields];
  [self.internalTransaction updateData:parsed forDocument:document.key];
  return self;
}

- (FIRTransaction *)deleteDocument:(FIRDocumentReference *)document {
  [self validateReference:document];
  [self.internalTransaction deleteDocument:document.key];
  return self;
}

- (void)getDocument:(FIRDocumentReference *)document
         completion:(void (^)(FIRDocumentSnapshot *_Nullable document,
                              NSError *_Nullable error))completion {
  [self validateReference:document];
  [self.internalTransaction
      lookupDocumentsForKeys:{document.key}
                  completion:^(NSArray<FSTMaybeDocument *> *_Nullable documents,
                               NSError *_Nullable error) {
                    if (error) {
                      completion(nil, error);
                      return;
                    }
                    FSTAssert(documents.count == 1,
                              @"Mismatch in docs returned from document lookup.");
                    FSTMaybeDocument *internalDoc = documents.firstObject;
                    if ([internalDoc isKindOfClass:[FSTDeletedDocument class]]) {
                      completion(nil, nil);
                      return;
                    }
                    FIRDocumentSnapshot *doc =
                        [FIRDocumentSnapshot snapshotWithFirestore:self.firestore
                                                       documentKey:internalDoc.key
                                                          document:(FSTDocument *)internalDoc
                                                         fromCache:NO];
                    completion(doc, nil);
                  }];
}

- (FIRDocumentSnapshot *_Nullable)getDocument:(FIRDocumentReference *)document
                                        error:(NSError *__autoreleasing *)error {
  [self validateReference:document];
  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  __block FIRDocumentSnapshot *result;
  // We have to explicitly assign the innerError into a local to cause it to retain correctly.
  __block NSError *outerError = nil;
  [self getDocument:document
         completion:^(FIRDocumentSnapshot *_Nullable snapshot, NSError *_Nullable innerError) {
           result = snapshot;
           outerError = innerError;
           dispatch_semaphore_signal(semaphore);
         }];
  dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  if (error) {
    *error = outerError;
  }
  return result;
}

- (void)validateReference:(FIRDocumentReference *)reference {
  if (reference.firestore != self.firestore) {
    FSTThrowInvalidArgument(@"Provided document reference is from a different Firestore instance.");
  }
}

@end

NS_ASSUME_NONNULL_END