diff --git a/FirebaseFunctions/CHANGELOG.md b/FirebaseFunctions/CHANGELOG.md index f244f3f58e8..3c5253793be 100644 --- a/FirebaseFunctions/CHANGELOG.md +++ b/FirebaseFunctions/CHANGELOG.md @@ -1,3 +1,7 @@ +# Unreleased +- [fixed] Fix regression from 11.6.0 where `HTTPSCallable` did not invoke + completion block on main thread (#14653). + # 11.10.0 - [added] Streaming callable functions are now supported. diff --git a/FirebaseFunctions/Sources/HTTPSCallable.swift b/FirebaseFunctions/Sources/HTTPSCallable.swift index b423ac4195a..671a7b67dce 100644 --- a/FirebaseFunctions/Sources/HTTPSCallable.swift +++ b/FirebaseFunctions/Sources/HTTPSCallable.swift @@ -76,15 +76,15 @@ open class HTTPSCallable: NSObject { /// - data: Parameters to pass to the trigger. /// - completion: The block to call when the HTTPS request has completed. @objc(callWithObject:completion:) open func call(_ data: Any? = nil, - completion: @escaping (HTTPSCallableResult?, + completion: @escaping @MainActor (HTTPSCallableResult?, Error?) -> Void) { if #available(iOS 13, macCatalyst 13, macOS 10.15, tvOS 13, watchOS 7, *) { Task { do { let result = try await call(data) - completion(result, nil) + await completion(result, nil) } catch { - completion(nil, error) + await completion(nil, error) } } } else { @@ -98,9 +98,13 @@ open class HTTPSCallable: NSObject { ) { result in switch result { case let .success(callableResult): - completion(callableResult, nil) + DispatchQueue.main.async { + completion(callableResult, nil) + } case let .failure(error): - completion(nil, error) + DispatchQueue.main.async { + completion(nil, error) + } } } } diff --git a/FirebaseFunctions/Tests/Integration/IntegrationTests.swift b/FirebaseFunctions/Tests/Integration/IntegrationTests.swift index 878cec1c9a0..0fa9c21e862 100644 --- a/FirebaseFunctions/Tests/Integration/IntegrationTests.swift +++ b/FirebaseFunctions/Tests/Integration/IntegrationTests.swift @@ -867,6 +867,38 @@ class IntegrationTests: XCTestCase { XCTAssertEqual(response, expected) } } + + func testFunctionsReturnsOnMainThread() { + let expectation = expectation(description: #function) + functions.httpsCallable( + "scalarTest", + requestAs: Int16.self, + responseAs: Int.self + ).call(17) { result in + guard case .success = result else { + return XCTFail("Unexpected failure.") + } + XCTAssert(Thread.isMainThread) + expectation.fulfill() + } + waitForExpectations(timeout: 5) + } + + func testFunctionsThrowsOnMainThread() { + let expectation = expectation(description: #function) + functions.httpsCallable( + "httpErrorTest", + requestAs: [Int].self, + responseAs: Int.self + ).call([]) { result in + guard case .failure = result else { + return XCTFail("Unexpected failure.") + } + XCTAssert(Thread.isMainThread) + expectation.fulfill() + } + waitForExpectations(timeout: 5) + } } // MARK: - Streaming diff --git a/FirebaseFunctions/Tests/ObjCIntegration/FIRIntegrationTests.m b/FirebaseFunctions/Tests/ObjCIntegration/FIRIntegrationTests.m index 124f2eb9044..1d3f88981ee 100644 --- a/FirebaseFunctions/Tests/ObjCIntegration/FIRIntegrationTests.m +++ b/FirebaseFunctions/Tests/ObjCIntegration/FIRIntegrationTests.m @@ -290,4 +290,28 @@ - (void)testTimeout { [self waitForExpectations:@[ expectation ] timeout:10]; } +- (void)testFunctionsReturnsOnMainThread { + XCTestExpectation *expectation = [[XCTestExpectation alloc] init]; + FIRHTTPSCallable *function = [_functions HTTPSCallableWithName:@"scalarTest"]; + [function callWithObject:@17 + completion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) { + XCTAssert(NSThread.isMainThread); + XCTAssertNotNil(result); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:10]; +} + +- (void)testFunctionErrorsOnMainThread { + XCTestExpectation *expectation = [[XCTestExpectation alloc] init]; + FIRHTTPSCallable *function = [_functions HTTPSCallableWithName:@"httpErrorTest"]; + [function callWithObject:@{} + completion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) { + XCTAssert(NSThread.isMainThread); + XCTAssertNotNil(error); + [expectation fulfill]; + }]; + [self waitForExpectations:@[ expectation ] timeout:10]; +} + @end diff --git a/FirebaseFunctions/Tests/Unit/FunctionsTests.swift b/FirebaseFunctions/Tests/Unit/FunctionsTests.swift index 476fe116853..255b2785451 100644 --- a/FirebaseFunctions/Tests/Unit/FunctionsTests.swift +++ b/FirebaseFunctions/Tests/Unit/FunctionsTests.swift @@ -290,7 +290,7 @@ class FunctionsTests: XCTestCase { } XCTAssertEqual(error as NSError, networkError) - + XCTAssert(Thread.isMainThread) completionExpectation.fulfill() }