Skip to content

Commit ec611c0

Browse files
committed
give up and use manual wrapper
1 parent d903f6e commit ec611c0

File tree

3 files changed

+57
-1
lines changed

3 files changed

+57
-1
lines changed

Firestore/Source/Public/FirebaseFirestore/FIRFirestore.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ NS_SWIFT_NAME(Firestore)
144144
*/
145145
- (void)runTransactionWithBlock:(id _Nullable (^)(FIRTransaction *, NSError **))updateBlock
146146
completion:(void (^)(id _Nullable result, NSError *_Nullable error))completion
147-
__attribute__((swift_async_error(nonnull_error)));
147+
__attribute__((swift_async(none))); // Disable async import due to #9426.
148148

149149
/**
150150
* Creates a write batch, used for performing multiple writes as a single

Firestore/Swift/Source/AsyncAwait/Firestore+AsyncAwait.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,50 @@ import Foundation
5353
}
5454
}
5555
}
56+
57+
/// Executes the given updateBlock and then attempts to commit the changes applied within an atomic
58+
/// transaction.
59+
///
60+
/// The maximum number of writes allowed in a single transaction is 500, but note that each usage of
61+
/// `FieldValue.serverTimestamp()`, `FieldValue.arrayUnion()`, `FieldValue.arrayRemove()`, or
62+
/// `FieldValue.increment()` inside a transaction counts as an additional write.
63+
///
64+
/// In the updateBlock, a set of reads and writes can be performed atomically using the
65+
/// `FIRTransaction` object passed to the block. After the updateBlock is run, Firestore will attempt
66+
/// to apply the changes to the server. If any of the data read has been modified outside of this
67+
/// transaction since being read, then the transaction will be retried by executing the updateBlock
68+
/// again. If the transaction still fails after 5 retries, then the transaction will fail.
69+
///
70+
/// Since the updateBlock may be executed multiple times, it should avoiding doing anything that
71+
/// would cause side effects.
72+
///
73+
/// Any value maybe be returned from the updateBlock. If the transaction is successfully committed,
74+
/// then the completion block will be passed that value. The updateBlock also has an `NSError` out
75+
/// parameter. If this is set, then the transaction will not attempt to commit, and the given error
76+
/// will be passed to the completion block.
77+
///
78+
/// The `FIRTransaction` object passed to the updateBlock contains methods for accessing documents
79+
/// and collections. Unlike other firestore access, data accessed with the transaction will not
80+
/// reflect local changes that have not been committed. For this reason, it is required that all
81+
/// reads are performed before any writes. Transactions must be performed while online. Otherwise,
82+
/// reads will fail, the final commit will fail, and the completion block will return an error.
83+
///
84+
/// @param updateBlock The block to execute within the transaction context.
85+
/// @param completion The block to call with the result or error of the transaction. This
86+
/// block will run even if the client is offline, unless the process is killed.
87+
func runTransaction(
88+
_ updateBlock: @escaping (Transaction, NSErrorPointer) -> Any?) async throws -> Any? {
89+
// This needs to be wrapped in order to express a nullable return value upon success.
90+
// See https://github.com/firebase/firebase-ios-sdk/issues/9426 for more details.
91+
return try await withCheckedThrowingContinuation { continuation in
92+
self.runTransaction(updateBlock) { anyValue, error in
93+
if let err = error {
94+
continuation.resume(throwing: err)
95+
} else {
96+
continuation.resume(returning: anyValue)
97+
}
98+
}
99+
}
100+
}
56101
}
57102
#endif

Firestore/Swift/Tests/Integration/AsyncAwaitIntegrationTests.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,16 @@ let emptyBundle = """
6565
.loadBundle(InputStream(data: bundle.data(using: String.Encoding.utf8)!))
6666
XCTAssertEqual(LoadBundleTaskState.success, bundleProgress.state)
6767
}
68+
69+
func testRunTransactionDoesNotCrashOnNilSuccess() async throws {
70+
let document = collectionRef().document()
71+
try await document.setData(["": ""]) // ensure document exists
72+
let value = try await db.runTransaction { transact, error in
73+
transact.setData(["test": "test"], forDocument: document)
74+
return nil // should not crash
75+
}
76+
77+
XCTAssertNil(value, "value should be nil on success")
78+
}
6879
}
6980
#endif

0 commit comments

Comments
 (0)