Skip to content

Commit 99b05b9

Browse files
darinfcompnerd
authored andcommitted
Add FirebaseFunctions module (#63)
Changes: - Updates workflow to a version of firebase-cpp-sdk that includes `functions` and `storage` headers and libs. - Adds incomplete support for mapping between Swift `Any` and C++ `firebase::Variant` types -- just what is needed by Arc for now. - Adds `FirebaseFunctionsErrorCode` following the pattern for `FirestoreErrorCode`, et. al. - Adds C++ type `FunctionsRef` as a copyable wrapper for the C++ `Functions` type, so that it can be exposed to Swift. - Adds `HTTPSCallable` and `HTTPSCallableResult` wrapper types. - Adds basic subset of `Functions` interface -- just what is needed by Arc for now.
1 parent f8cbe4f commit 99b05b9

File tree

11 files changed

+372
-7
lines changed

11 files changed

+372
-7
lines changed

.github/workflows/build.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ jobs:
3131
- uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4 # v1.1.1
3232
with:
3333
repo: thebrowsercompany/firebase-cpp-sdk
34-
version: tags/20240306.0
34+
version: tags/20240511.0
3535
file: firebase-windows-amd64.zip
3636

37-
- run: Expand-Archive -Path firebase-windows-amd64.zip -DestinationPath third_party/firebase-development
37+
- run: Expand-Archive -Path firebase-windows-amd64.zip -DestinationPath third_party; Rename-Item -Path third_party/firebase -NewName firebase-development
3838
shell: powershell
3939

4040
- name: Build
@@ -66,10 +66,10 @@ jobs:
6666
- uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4 # v1.1.1
6767
with:
6868
repo: thebrowsercompany/firebase-cpp-sdk
69-
version: tags/20240306.0
69+
version: tags/20240511.0
7070
file: firebase-windows-amd64.zip
7171

72-
- run: Expand-Archive -Path firebase-windows-amd64.zip -DestinationPath third_party/firebase-development
72+
- run: Expand-Archive -Path firebase-windows-amd64.zip -DestinationPath third_party; Rename-Item -Path third_party/firebase -NewName firebase-development
7373
shell: powershell
7474

7575
- name: Configure

CMakeLists.txt

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ add_library(FirebaseCore SHARED
5353
Sources/FirebaseCore/FirebaseConfiguration.swift
5454
Sources/FirebaseCore/FirebaseLogging+Swift.swift
5555
Sources/FirebaseCore/FirebaseOptions+Swift.swift
56-
Sources/FirebaseCore/FutureProtocol.swift)
56+
Sources/FirebaseCore/FutureProtocol.swift
57+
Sources/FirebaseCore/Variant+Swift.swift)
5758
target_compile_options(FirebaseCore PRIVATE
5859
-cxx-interoperability-mode=default)
5960
target_link_libraries(FirebaseCore PUBLIC
@@ -128,9 +129,22 @@ target_link_libraries(FirebaseFirestore PUBLIC
128129
absl_cordz_functions
129130
absl_cordz_handle
130131
absl_cordz_info
132+
absl_crc_cord_state
133+
absl_crc_cpu_detect
134+
absl_crc_internal
135+
absl_crc32c
136+
absl_flags_commandlineflag_internal
137+
absl_flags_commandlineflag
138+
absl_flags_config
139+
absl_flags_internal
140+
absl_flags_marshalling
141+
absl_flags_private_handle_accessor
142+
absl_flags_program_name
143+
absl_flags_reflection
131144
absl_graphcycles_internal
132145
absl_hash
133146
absl_int128
147+
absl_kernel_timeout_internal
134148
absl_low_level_hash
135149
absl_malloc_internal
136150
absl_random_internal_platform
@@ -149,6 +163,7 @@ target_link_libraries(FirebaseFirestore PUBLIC
149163
absl_statusor
150164
absl_str_format_internal
151165
absl_strerror
166+
absl_string_view
152167
absl_strings
153168
absl_strings_internal
154169
absl_symbolize
@@ -175,7 +190,32 @@ target_link_libraries(FirebaseFirestore PUBLIC
175190
re2
176191
snappy
177192
ssl
178-
upb
193+
upb_base_lib
194+
upb_json_lib
195+
upb_mem_lib
196+
upb_message_lib
197+
upb_textformat_lib
198+
utf8_range_lib
199+
utf8_validity
200+
zlibstatic)
201+
202+
add_library(FirebaseFunctions SHARED
203+
Sources/FirebaseFunctions/FirebaseFunctionsErrorCode.swift
204+
Sources/FirebaseFunctions/Functions+Swift.swift
205+
Sources/FirebaseFunctions/HTTPSCallable+Swift.swift
206+
Sources/FirebaseFunctions/HTTPSCallableResult+Swift.swift)
207+
target_compile_options(FirebaseFunctions PRIVATE
208+
-cxx-interoperability-mode=default)
209+
target_link_libraries(FirebaseFunctions PUBLIC
210+
firebase
211+
firebase_functions
212+
FirebaseCore)
213+
target_link_libraries(FirebaseFunctions PRIVATE
214+
crypto
215+
firebase_rest_lib
216+
flatbuffers
217+
libcurl
218+
ssl
179219
zlibstatic)
180220

181221
if(SWIFT_FIREBASE_BUILD_EXAMPLES)

Package.swift

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ let SwiftFirebase =
2727
.library(name: "FirebaseCore", targets: ["FirebaseCore"]),
2828
.library(name: "FirebaseAuth", targets: ["FirebaseAuth"]),
2929
.library(name: "FirebaseFirestore", targets: ["FirebaseFirestore"]),
30+
.library(name: "FirebaseFunctions", targets: ["FirebaseFunctions"]),
3031
.executable(name: "FireBaseUI", targets: ["FireBaseUI"]),
3132
],
3233
dependencies: [
@@ -117,9 +118,22 @@ let SwiftFirebase =
117118
.linkedLibrary("absl_cordz_functions"),
118119
.linkedLibrary("absl_cordz_handle"),
119120
.linkedLibrary("absl_cordz_info"),
121+
.linkedLibrary("absl_crc_cord_state"),
122+
.linkedLibrary("absl_crc_cpu_detect"),
123+
.linkedLibrary("absl_crc_internal"),
124+
.linkedLibrary("absl_crc32c"),
125+
.linkedLibrary("absl_flags_commandlineflag_internal"),
126+
.linkedLibrary("absl_flags_commandlineflag"),
127+
.linkedLibrary("absl_flags_config"),
128+
.linkedLibrary("absl_flags_internal"),
129+
.linkedLibrary("absl_flags_marshalling"),
130+
.linkedLibrary("absl_flags_private_handle_accessor"),
131+
.linkedLibrary("absl_flags_program_name"),
132+
.linkedLibrary("absl_flags_reflection"),
120133
.linkedLibrary("absl_graphcycles_internal"),
121134
.linkedLibrary("absl_hash"),
122135
.linkedLibrary("absl_int128"),
136+
.linkedLibrary("absl_kernel_timeout_internal"),
123137
.linkedLibrary("absl_low_level_hash"),
124138
.linkedLibrary("absl_malloc_internal"),
125139
.linkedLibrary("absl_random_internal_platform"),
@@ -138,6 +152,7 @@ let SwiftFirebase =
138152
.linkedLibrary("absl_statusor"),
139153
.linkedLibrary("absl_str_format_internal"),
140154
.linkedLibrary("absl_strerror"),
155+
.linkedLibrary("absl_string_view"),
141156
.linkedLibrary("absl_strings"),
142157
.linkedLibrary("absl_strings_internal"),
143158
.linkedLibrary("absl_symbolize"),
@@ -158,7 +173,25 @@ let SwiftFirebase =
158173
.linkedLibrary("protobuf-nanopb"),
159174
.linkedLibrary("re2"),
160175
.linkedLibrary("snappy"),
161-
.linkedLibrary("upb"),
176+
.linkedLibrary("upb_base_lib"),
177+
.linkedLibrary("upb_json_lib"),
178+
.linkedLibrary("upb_mem_lib"),
179+
.linkedLibrary("upb_message_lib"),
180+
.linkedLibrary("upb_textformat_lib"),
181+
.linkedLibrary("utf8_range_lib"),
182+
.linkedLibrary("utf8_validity"),
183+
]),
184+
.target(name: "FirebaseFunctions",
185+
dependencies: ["firebase", "FirebaseCore"],
186+
cxxSettings: [
187+
.define("INTERNAL_EXPERIMENTAL"),
188+
.define("_CRT_SECURE_NO_WARNINGS",
189+
.when(platforms: [.windows])),
190+
.headerSearchPath("../../third_party/firebase-development/usr/include"),
191+
],
192+
swiftSettings: [
193+
.interoperabilityMode(.Cxx),
194+
.unsafeFlags(["-Xcc", "-I\(include)"]),
162195
]),
163196
.executableTarget(name: "FireBaseUI",
164197
dependencies: [
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
3+
@_exported
4+
import firebase
5+
6+
enum VariantConversionError: Error {
7+
case unsupportedType
8+
}
9+
10+
@_spi(FirebaseInternal)
11+
public func toVariant(_ object: Any?) throws -> firebase.Variant {
12+
if object == nil {
13+
return .init()
14+
} else if let string = object as? String {
15+
return .init(std.string(string))
16+
} else if let dict = object as? Dictionary<String, Any> {
17+
var result = firebase.Variant()
18+
result.set_map(.init())
19+
try dict.forEach { element in
20+
let value = try toVariant(element.value)
21+
swift_firebase.swift_cxx_shims.firebase.variant_map_insert(
22+
&result, firebase.Variant(std.string(element.key)), value
23+
)
24+
}
25+
return result
26+
} else {
27+
// TODO(bcny/PRENG-63977): Handle other data types.
28+
throw VariantConversionError.unsupportedType
29+
}
30+
}
31+
32+
@_spi(FirebaseInternal)
33+
public func fromVariant(_ variant: firebase.Variant) throws -> Any? {
34+
if variant.is_bool() {
35+
return swift_firebase.swift_cxx_shims.firebase.variant_bool_value(variant)
36+
} else if variant.is_null() {
37+
return nil
38+
} else {
39+
// TODO(bcny/PRENG-63977): Handle other data types.
40+
throw VariantConversionError.unsupportedType
41+
}
42+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
3+
@_exported
4+
import firebase
5+
@_spi(FirebaseInternal)
6+
import FirebaseCore
7+
8+
public struct FirebaseFunctionsErrorCode: Error {
9+
public let rawValue: Int
10+
public let localizedDescription: String
11+
12+
internal init(_ params: (code: Int32, message: String)) {
13+
self.rawValue = Int(params.code)
14+
localizedDescription = params.message
15+
}
16+
17+
private init(_ error: firebase.functions.Error) {
18+
self.init(rawValue: Int(error.rawValue))
19+
}
20+
}
21+
22+
extension FirebaseFunctionsErrorCode: RawRepresentable {
23+
public typealias RawValue = Int
24+
25+
public init(rawValue: Int) {
26+
self.rawValue = rawValue
27+
localizedDescription = "\(rawValue)"
28+
}
29+
}
30+
31+
extension FirebaseFunctionsErrorCode {
32+
init(_ error: firebase.functions.Error, errorMessage: String?) {
33+
self.init((code: error.rawValue, message: errorMessage ?? "\(error.rawValue)"))
34+
}
35+
36+
init?(_ error: firebase.functions.Error?, errorMessage: UnsafePointer<CChar>?) {
37+
guard let actualError = error, actualError.rawValue != 0 else { return nil }
38+
var errorMessageString: String?
39+
if let errorMessage {
40+
errorMessageString = .init(cString: errorMessage)
41+
}
42+
self.init(actualError, errorMessage: errorMessageString)
43+
}
44+
}
45+
46+
extension FirebaseFunctionsErrorCode {
47+
public static var none: Self { .init(firebase.functions.kErrorNone) }
48+
public static var cancelled: Self { .init(firebase.functions.kErrorCancelled) }
49+
public static var unknown: Self { .init(firebase.functions.kErrorUnknown) }
50+
public static var invalidArgument: Self { .init(firebase.functions.kErrorInvalidArgument) }
51+
public static var deadlineExceeded: Self { .init(firebase.functions.kErrorDeadlineExceeded) }
52+
public static var notFound: Self { .init(firebase.functions.kErrorNotFound) }
53+
public static var alreadyExists: Self { .init(firebase.functions.kErrorAlreadyExists) }
54+
public static var permissionDenied: Self { .init(firebase.functions.kErrorPermissionDenied) }
55+
public static var unauthenticated: Self { .init(firebase.functions.kErrorUnauthenticated) }
56+
public static var resourceExhausted: Self { .init(firebase.functions.kErrorResourceExhausted) }
57+
public static var failedPrecondition: Self { .init(firebase.functions.kErrorFailedPrecondition) }
58+
public static var aborted: Self { .init(firebase.functions.kErrorAborted) }
59+
public static var outOfRange: Self { .init(firebase.functions.kErrorOutOfRange) }
60+
public static var unimplemented: Self { .init(firebase.functions.kErrorUnimplemented) }
61+
public static var `internal`: Self { .init(firebase.functions.kErrorInternal) }
62+
public static var unavailable: Self { .init(firebase.functions.kErrorUnavailable) }
63+
public static var dataLoss: Self { .init(firebase.functions.kErrorDataLoss) }
64+
}
65+
66+
extension FirebaseFunctionsErrorCode: Equatable {}
67+
68+
extension FirebaseFunctionsErrorCode {
69+
// The Obj C API provides this type as well, so provide it here for consistency.
70+
public typealias Code = FirebaseFunctionsErrorCode
71+
72+
// This allows us to re-expose self as a code similarly
73+
// to what the Firebase SDK does when it creates the
74+
// underlying NSErrors on iOS/macOS.
75+
public var code: Code {
76+
return self
77+
}
78+
79+
public init(_ code: Code) {
80+
self.init(rawValue: code.rawValue)
81+
}
82+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
3+
@_exported
4+
import firebase
5+
@_spi(FirebaseInternal)
6+
import FirebaseCore
7+
8+
import CxxShim
9+
10+
public class Functions {
11+
let impl: swift_firebase.swift_cxx_shims.firebase.functions.FunctionsRef
12+
13+
init(_ impl: swift_firebase.swift_cxx_shims.firebase.functions.FunctionsRef) {
14+
self.impl = impl
15+
}
16+
17+
public static func functions(app: FirebaseApp) -> Functions {
18+
let instance = swift_firebase.swift_cxx_shims.firebase.functions.functions_get_instance(app)
19+
guard swift_firebase.swift_cxx_shims.firebase.functions.functions_is_valid(instance) else {
20+
fatalError("Invalid Functions Instance")
21+
}
22+
return .init(instance)
23+
}
24+
25+
public func httpsCallable(_ name: String) -> HTTPSCallable {
26+
.init(swift_firebase.swift_cxx_shims.firebase.functions.functions_get_https_callable(impl, name))
27+
}
28+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
3+
@_exported
4+
import firebase
5+
@_spi(FirebaseInternal)
6+
import FirebaseCore
7+
8+
import CxxShim
9+
import Foundation
10+
11+
public class HTTPSCallable {
12+
let impl: firebase.functions.HttpsCallableReference
13+
14+
init(_ impl: firebase.functions.HttpsCallableReference) {
15+
self.impl = impl
16+
}
17+
18+
public func call(_ data: Any? = nil, completion: @escaping (HTTPSCallableResult?, Error?) -> Void) {
19+
callImpl(data: data) { result, error in
20+
DispatchQueue.main.async {
21+
completion(result, error)
22+
}
23+
}
24+
}
25+
26+
public func call(_ data: Any? = nil) async throws -> HTTPSCallableResult {
27+
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<HTTPSCallableResult, any Error>) in
28+
callImpl(data: data) { result, error in
29+
if let error {
30+
continuation.resume(throwing: error)
31+
} else{
32+
continuation.resume(returning: result ?? .init())
33+
}
34+
}
35+
}
36+
}
37+
38+
private func callImpl(data: Any?, completion: @escaping (HTTPSCallableResult?, Error?) -> Void) {
39+
let variant = try! toVariant(data)
40+
let future = swift_firebase.swift_cxx_shims.firebase.functions.https_callable_call(impl, variant)
41+
future.setCompletion({
42+
let (result, error) = future.resultAndError { FirebaseFunctionsErrorCode($0) }
43+
completion(result.map { .init($0) }, error)
44+
})
45+
}
46+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
3+
@_exported
4+
import firebase
5+
@_spi(FirebaseInternal)
6+
import FirebaseCore
7+
8+
import CxxShim
9+
import Foundation
10+
11+
public class HTTPSCallableResult {
12+
let data: Any
13+
14+
init(_ result: firebase.functions.HttpsCallableResult = .init()) {
15+
let variant = swift_firebase.swift_cxx_shims.firebase.functions.https_callable_result_data(result)
16+
let data = try! fromVariant(variant)
17+
18+
// For compatibility with the ObjC API, map nil to NSNull here.
19+
self.data = data ?? NSNull()
20+
}
21+
}

0 commit comments

Comments
 (0)