Skip to content

Commit 8543e7e

Browse files
authored
[Functions] Update HTTPSCallableOptions.swift (#15075)
1 parent 32c3c73 commit 8543e7e

File tree

4 files changed

+49
-93
lines changed

4 files changed

+49
-93
lines changed

FirebaseFunctions/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# Unreleased
2+
- [changed] **Breaking Change**: Mark `HTTPSCallable` and `HTTPSCallableOptions`
3+
as `final` classes for Swift clients. This was to achieve Swift 6 checked
4+
`Sendable` support.
5+
16
# 11.12.0
27
- [fixed] Fix regression from 11.6.0 where `HTTPSCallable` did not invoke
38
completion block on main thread (#14653).

FirebaseFunctions/Sources/HTTPSCallable.swift

Lines changed: 41 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,34 @@ open class HTTPSCallableResult: NSObject {
3333

3434
/// A `HTTPSCallable` is a reference to a particular Callable HTTPS trigger in Cloud Functions.
3535
@objc(FIRHTTPSCallable)
36-
open class HTTPSCallable: NSObject, @unchecked Sendable {
36+
public final class HTTPSCallable: NSObject, Sendable {
3737
// MARK: - Private Properties
3838

39-
/// Until this class can be marked *checked* `Sendable`, it's implementation
40-
/// is delegated to an auxiliary class that is checked Sendable.
41-
private let sendableCallable: SendableHTTPSCallable
39+
// The functions client to use for making calls.
40+
private let functions: Functions
41+
42+
private let url: URL
43+
44+
private let options: HTTPSCallableOptions?
45+
46+
private let _timeoutInterval: AtomicBox<TimeInterval> = .init(70)
4247

4348
// MARK: - Public Properties
4449

4550
/// The timeout to use when calling the function. Defaults to 70 seconds.
46-
@objc open var timeoutInterval: TimeInterval {
47-
get { sendableCallable.timeoutInterval }
48-
set { sendableCallable.timeoutInterval = newValue }
51+
@objc public var timeoutInterval: TimeInterval {
52+
get { _timeoutInterval.value() }
53+
set {
54+
_timeoutInterval.withLock { timeoutInterval in
55+
timeoutInterval = newValue
56+
}
57+
}
4958
}
5059

5160
init(functions: Functions, url: URL, options: HTTPSCallableOptions? = nil) {
52-
sendableCallable = SendableHTTPSCallable(functions: functions, url: url, options: options)
61+
self.functions = functions
62+
self.url = url
63+
self.options = options
5364
}
5465

5566
/// Executes this Callable HTTPS trigger asynchronously.
@@ -74,11 +85,11 @@ open class HTTPSCallable: NSObject, @unchecked Sendable {
7485
/// - data: Parameters to pass to the trigger.
7586
/// - completion: The block to call when the HTTPS request has completed.
7687
@available(swift 1000.0) // Objective-C only API
77-
@objc(callWithObject:completion:) open func call(_ data: Any? = nil,
78-
completion: @escaping @MainActor (HTTPSCallableResult?,
79-
Error?)
80-
-> Void) {
81-
sendableCallable.call(SendableWrapper(value: data as Any), completion: completion)
88+
@objc(callWithObject:completion:) public func call(_ data: Any? = nil,
89+
completion: @escaping @MainActor (HTTPSCallableResult?,
90+
Error?)
91+
-> Void) {
92+
call(SendableWrapper(value: data as Any), completion: completion)
8293
}
8394

8495
/// Executes this Callable HTTPS trigger asynchronously.
@@ -102,11 +113,19 @@ open class HTTPSCallable: NSObject, @unchecked Sendable {
102113
/// - Parameters:
103114
/// - data: Parameters to pass to the trigger.
104115
/// - completion: The block to call when the HTTPS request has completed.
105-
@nonobjc open func call(_ data: sending Any? = nil,
106-
completion: @escaping @MainActor (HTTPSCallableResult?,
107-
Error?)
108-
-> Void) {
109-
sendableCallable.call(data, completion: completion)
116+
@nonobjc public func call(_ data: sending Any? = nil,
117+
completion: @escaping @MainActor (HTTPSCallableResult?,
118+
Error?)
119+
-> Void) {
120+
let data = (data as? SendableWrapper)?.value ?? data
121+
Task {
122+
do {
123+
let result = try await call(data)
124+
await completion(result, nil)
125+
} catch {
126+
await completion(nil, error)
127+
}
128+
}
110129
}
111130

112131
/// Executes this Callable HTTPS trigger asynchronously. This API should only be used from
@@ -142,73 +161,13 @@ open class HTTPSCallable: NSObject, @unchecked Sendable {
142161
/// - Throws: An error if the Cloud Functions invocation failed.
143162
/// - Returns: The result of the call.
144163
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
145-
open func call(_ data: Any? = nil) async throws -> sending HTTPSCallableResult {
146-
try await sendableCallable.call(data)
164+
public func call(_ data: Any? = nil) async throws -> sending HTTPSCallableResult {
165+
try await functions
166+
.callFunction(at: url, withObject: data, options: options, timeout: timeoutInterval)
147167
}
148168

149169
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
150170
func stream(_ data: SendableWrapper? = nil) -> AsyncThrowingStream<JSONStreamResponse, Error> {
151-
sendableCallable.stream(data)
152-
}
153-
}
154-
155-
private extension HTTPSCallable {
156-
final class SendableHTTPSCallable: Sendable {
157-
// MARK: - Private Properties
158-
159-
// The functions client to use for making calls.
160-
private let functions: Functions
161-
162-
private let url: URL
163-
164-
private let options: HTTPSCallableOptions?
165-
166-
// MARK: - Public Properties
167-
168-
let _timeoutInterval = FIRAllocatedUnfairLock<TimeInterval>(initialState: 70)
169-
170-
/// The timeout to use when calling the function. Defaults to 70 seconds.
171-
var timeoutInterval: TimeInterval {
172-
get { _timeoutInterval.value() }
173-
set {
174-
_timeoutInterval.withLock { timeoutInterval in
175-
timeoutInterval = newValue
176-
}
177-
}
178-
}
179-
180-
init(functions: Functions, url: URL, options: HTTPSCallableOptions? = nil) {
181-
self.functions = functions
182-
self.url = url
183-
self.options = options
184-
}
185-
186-
func call(_ data: sending Any? = nil,
187-
completion: @escaping @MainActor (HTTPSCallableResult?, Error?) -> Void) {
188-
let data = (data as? SendableWrapper)?.value ?? data
189-
Task {
190-
do {
191-
let result = try await call(data)
192-
await completion(result, nil)
193-
} catch {
194-
await completion(nil, error)
195-
}
196-
}
197-
}
198-
199-
func __call(completion: @escaping @MainActor (HTTPSCallableResult?, Error?) -> Void) {
200-
call(nil, completion: completion)
201-
}
202-
203-
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
204-
func call(_ data: Any? = nil) async throws -> sending HTTPSCallableResult {
205-
try await functions
206-
.callFunction(at: url, withObject: data, options: options, timeout: timeoutInterval)
207-
}
208-
209-
@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
210-
func stream(_ data: SendableWrapper? = nil) -> AsyncThrowingStream<JSONStreamResponse, Error> {
211-
functions.stream(at: url, data: data, options: options, timeout: timeoutInterval)
212-
}
171+
functions.stream(at: url, data: data, options: options, timeout: timeoutInterval)
213172
}
214173
}

FirebaseFunctions/Sources/HTTPSCallableOptions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import Foundation
1616

1717
/// Configuration options for a ``HTTPSCallable`` instance.
18-
@objc(FIRHTTPSCallableOptions) public class HTTPSCallableOptions: NSObject, @unchecked Sendable {
18+
@objc(FIRHTTPSCallableOptions) public final class HTTPSCallableOptions: NSObject, Sendable {
1919
/// Whether or not to protect the callable function with a limited-use App Check token.
2020
@objc public let requireLimitedUseAppCheckTokens: Bool
2121

FirebaseFunctions/Tests/CombineUnit/HTTPSCallableTests.swift

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,6 @@ class MockFunctions: Functions, @unchecked Sendable {
5454
}
5555
}
5656

57-
public class HTTPSCallableResultFake: HTTPSCallableResult {
58-
let fakeData: String
59-
init(data: String) {
60-
fakeData = data
61-
super.init(data: data)
62-
}
63-
}
64-
6557
@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)
6658
class HTTPSCallableTests: XCTestCase {
6759
func testCallWithoutParametersSuccess() {
@@ -73,7 +65,7 @@ class HTTPSCallableTests: XCTestCase {
7365

7466
let functions = MockFunctions {
7567
httpsFunctionWasCalledExpectation.fulfill()
76-
return HTTPSCallableResultFake(data: expectedResult)
68+
return HTTPSCallableResult(data: expectedResult)
7769
}
7870

7971
let dummyFunction = functions.httpsCallable("dummyFunction")
@@ -115,7 +107,7 @@ class HTTPSCallableTests: XCTestCase {
115107
let expectedResult = "mockResult w/ parameters: \(inputParameter)"
116108
let functions = MockFunctions {
117109
httpsFunctionWasCalledExpectation.fulfill()
118-
return HTTPSCallableResultFake(data: expectedResult)
110+
return HTTPSCallableResult(data: expectedResult)
119111
}
120112
functions.verifyParameters = { url, data, timeout in
121113
XCTAssertEqual(

0 commit comments

Comments
 (0)