Skip to content

[Functions] Update HTTPSCallableOptions.swift #15075

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions FirebaseFunctions/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Unreleased
- [changed] **Breaking Change**: Mark `HTTPSCallable` and `HTTPSCallableOptions`
as `final` classes for Swift clients. This was to achieve Swift 6 checked
`Sendable` support.

# 11.12.0
- [fixed] Fix regression from 11.6.0 where `HTTPSCallable` did not invoke
completion block on main thread (#14653).
Expand Down
123 changes: 41 additions & 82 deletions FirebaseFunctions/Sources/HTTPSCallable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,34 @@

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

/// Until this class can be marked *checked* `Sendable`, it's implementation
/// is delegated to an auxiliary class that is checked Sendable.
private let sendableCallable: SendableHTTPSCallable
// The functions client to use for making calls.
private let functions: Functions

private let url: URL

private let options: HTTPSCallableOptions?

private let _timeoutInterval: AtomicBox<TimeInterval> = .init(70)

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / xcodebuild (iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / xcodebuild (iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / xcodebuild (iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / storage-combine-integration

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / storage-combine-integration

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / storage-combine-integration

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / storage-combine-integration

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, watchOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, watchOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, watchOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, watchOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, tvOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, tvOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, tvOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, visionOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, visionOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, visionOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, catalyst)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, catalyst)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, catalyst)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, macOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, macOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-unit / spm (macos-15, Xcode_16.4, macOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsIntegration) / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsIntegration) / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsIntegration) / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsIntegration) / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsIntegration) / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsIntegration) / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsObjCIntegration) / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsObjCIntegration) / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsObjCIntegration) / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsObjCIntegration) / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsObjCIntegration) / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FunctionsCombineUnit) / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FunctionsCombineUnit) / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FunctionsCombineUnit) / spm (macos-14, Xcode_16.2, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsObjCIntegration) / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsObjCIntegration) / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsObjCIntegration) / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FirebaseFunctionsObjCIntegration) / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FunctionsCombineUnit) / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FunctionsCombineUnit) / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FunctionsCombineUnit) / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

Check failure on line 46 in FirebaseFunctions/Sources/HTTPSCallable.swift

View workflow job for this annotation

GitHub Actions / spm-integration (FunctionsCombineUnit) / spm (macos-15, Xcode_16.4, iOS)

cannot find type 'AtomicBox' in scope

// MARK: - Public Properties

/// The timeout to use when calling the function. Defaults to 70 seconds.
@objc open var timeoutInterval: TimeInterval {
get { sendableCallable.timeoutInterval }
set { sendableCallable.timeoutInterval = newValue }
@objc public var timeoutInterval: TimeInterval {
get { _timeoutInterval.value() }
set {
_timeoutInterval.withLock { timeoutInterval in
timeoutInterval = newValue
}
}
}

init(functions: Functions, url: URL, options: HTTPSCallableOptions? = nil) {
sendableCallable = SendableHTTPSCallable(functions: functions, url: url, options: options)
self.functions = functions
self.url = url
self.options = options
}

/// Executes this Callable HTTPS trigger asynchronously.
Expand All @@ -74,11 +85,11 @@
/// - data: Parameters to pass to the trigger.
/// - completion: The block to call when the HTTPS request has completed.
@available(swift 1000.0) // Objective-C only API
@objc(callWithObject:completion:) open func call(_ data: Any? = nil,
completion: @escaping @MainActor (HTTPSCallableResult?,
Error?)
-> Void) {
sendableCallable.call(SendableWrapper(value: data as Any), completion: completion)
@objc(callWithObject:completion:) public func call(_ data: Any? = nil,
completion: @escaping @MainActor (HTTPSCallableResult?,
Error?)
-> Void) {
call(SendableWrapper(value: data as Any), completion: completion)
}

/// Executes this Callable HTTPS trigger asynchronously.
Expand All @@ -102,11 +113,19 @@
/// - Parameters:
/// - data: Parameters to pass to the trigger.
/// - completion: The block to call when the HTTPS request has completed.
@nonobjc open func call(_ data: sending Any? = nil,
completion: @escaping @MainActor (HTTPSCallableResult?,
Error?)
-> Void) {
sendableCallable.call(data, completion: completion)
@nonobjc public func call(_ data: sending Any? = nil,
completion: @escaping @MainActor (HTTPSCallableResult?,
Error?)
-> Void) {
let data = (data as? SendableWrapper)?.value ?? data
Task {
do {
let result = try await call(data)
await completion(result, nil)
} catch {
await completion(nil, error)
}
}
}

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

@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
func stream(_ data: SendableWrapper? = nil) -> AsyncThrowingStream<JSONStreamResponse, Error> {
sendableCallable.stream(data)
}
}

private extension HTTPSCallable {
final class SendableHTTPSCallable: Sendable {
// MARK: - Private Properties

// The functions client to use for making calls.
private let functions: Functions

private let url: URL

private let options: HTTPSCallableOptions?

// MARK: - Public Properties

let _timeoutInterval = FIRAllocatedUnfairLock<TimeInterval>(initialState: 70)

/// The timeout to use when calling the function. Defaults to 70 seconds.
var timeoutInterval: TimeInterval {
get { _timeoutInterval.value() }
set {
_timeoutInterval.withLock { timeoutInterval in
timeoutInterval = newValue
}
}
}

init(functions: Functions, url: URL, options: HTTPSCallableOptions? = nil) {
self.functions = functions
self.url = url
self.options = options
}

func call(_ data: sending Any? = nil,
completion: @escaping @MainActor (HTTPSCallableResult?, Error?) -> Void) {
let data = (data as? SendableWrapper)?.value ?? data
Task {
do {
let result = try await call(data)
await completion(result, nil)
} catch {
await completion(nil, error)
}
}
}

func __call(completion: @escaping @MainActor (HTTPSCallableResult?, Error?) -> Void) {
call(nil, completion: completion)
}

@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
func call(_ data: Any? = nil) async throws -> sending HTTPSCallableResult {
try await functions
.callFunction(at: url, withObject: data, options: options, timeout: timeoutInterval)
}

@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
func stream(_ data: SendableWrapper? = nil) -> AsyncThrowingStream<JSONStreamResponse, Error> {
functions.stream(at: url, data: data, options: options, timeout: timeoutInterval)
}
functions.stream(at: url, data: data, options: options, timeout: timeoutInterval)
}
}
2 changes: 1 addition & 1 deletion FirebaseFunctions/Sources/HTTPSCallableOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import Foundation

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

Expand Down
12 changes: 2 additions & 10 deletions FirebaseFunctions/Tests/CombineUnit/HTTPSCallableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,6 @@ class MockFunctions: Functions, @unchecked Sendable {
}
}

public class HTTPSCallableResultFake: HTTPSCallableResult {
let fakeData: String
init(data: String) {
fakeData = data
super.init(data: data)
}
}

@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, watchOS 6.0, *)
class HTTPSCallableTests: XCTestCase {
func testCallWithoutParametersSuccess() {
Expand All @@ -73,7 +65,7 @@ class HTTPSCallableTests: XCTestCase {

let functions = MockFunctions {
httpsFunctionWasCalledExpectation.fulfill()
return HTTPSCallableResultFake(data: expectedResult)
return HTTPSCallableResult(data: expectedResult)
}

let dummyFunction = functions.httpsCallable("dummyFunction")
Expand Down Expand Up @@ -115,7 +107,7 @@ class HTTPSCallableTests: XCTestCase {
let expectedResult = "mockResult w/ parameters: \(inputParameter)"
let functions = MockFunctions {
httpsFunctionWasCalledExpectation.fulfill()
return HTTPSCallableResultFake(data: expectedResult)
return HTTPSCallableResult(data: expectedResult)
}
functions.verifyParameters = { url, data, timeout in
XCTAssertEqual(
Expand Down
Loading