Skip to content
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

Add FirebaseFunctions module #63

Merged
merged 18 commits into from
May 13, 2024
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ jobs:
- uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4 # v1.1.1
with:
repo: thebrowsercompany/firebase-cpp-sdk
version: tags/20240306.0
version: tags/20240511.0
file: firebase-windows-amd64.zip

- run: Expand-Archive -Path firebase-windows-amd64.zip -DestinationPath third_party/firebase-development
- run: Expand-Archive -Path firebase-windows-amd64.zip -DestinationPath third_party -and Rename-Item -Path third_party\firebase -NewName firebase-development
shell: powershell

- name: Build
Expand Down Expand Up @@ -66,10 +66,10 @@ jobs:
- uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4 # v1.1.1
with:
repo: thebrowsercompany/firebase-cpp-sdk
version: tags/20240306.0
version: tags/20240511.0
file: firebase-windows-amd64.zip

- run: Expand-Archive -Path firebase-windows-amd64.zip -DestinationPath third_party/firebase-development
- run: Expand-Archive -Path firebase-windows-amd64.zip -DestinationPath third_party -and Rename-Item -Path third_party\firebase -NewName firebase-development
shell: powershell

- name: Configure
Expand Down
35 changes: 34 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,22 @@ target_link_libraries(FirebaseFirestore PUBLIC
absl_cordz_functions
absl_cordz_handle
absl_cordz_info
absl_crc_cord_state
absl_crc_cpu_detect
absl_crc_internal
absl_crc32c
absl_flags_commandlineflag_internal
absl_flags_commandlineflag
absl_flags_config
absl_flags_internal
absl_flags_marshalling
absl_flags_private_handle_accessor
absl_flags_program_name
absl_flags_reflection
absl_graphcycles_internal
absl_hash
absl_int128
absl_kernel_timeout_internal
absl_low_level_hash
absl_malloc_internal
absl_random_internal_platform
Expand All @@ -149,6 +162,7 @@ target_link_libraries(FirebaseFirestore PUBLIC
absl_statusor
absl_str_format_internal
absl_strerror
absl_string_view
absl_strings
absl_strings_internal
absl_symbolize
Expand All @@ -175,9 +189,28 @@ target_link_libraries(FirebaseFirestore PUBLIC
re2
snappy
ssl
upb
upb_base_lib
upb_json_lib
upb_mem_lib
upb_message_lib
upb_textformat_lib
utf8_range_lib
utf8_validity
zlibstatic)

add_library(FirebaseFunctions SHARED
Sources/FirebaseFunctions/Functions+Swift.swift
Sources/FirebaseFunctions/FunctionsErrorCode.swift
Sources/FirebaseFunctions/HTTPSCallable+Swift.swift
Sources/FirebaseFunctions/HTTPSCallableResult+Swift.swift)
target_compile_options(FirebaseFunctions PRIVATE
-cxx-interoperability-mode=default)
target_link_libraries(FirebaseFunctions PUBLIC
firebase
FirebaseCore)
target_link_libraries(FirebaseFunctions PRIVATE
firebase_rest_lib)

if(SWIFT_FIREBASE_BUILD_EXAMPLES)
FetchContent_Declare(SwiftWin32
GIT_REPOSITORY https://github.com/compnerd/swift-win32
Expand Down
35 changes: 34 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ let SwiftFirebase =
.library(name: "FirebaseCore", targets: ["FirebaseCore"]),
.library(name: "FirebaseAuth", targets: ["FirebaseAuth"]),
.library(name: "FirebaseFirestore", targets: ["FirebaseFirestore"]),
.library(name: "FirebaseFunctions", targets: ["FirebaseFunctions"]),
.executable(name: "FireBaseUI", targets: ["FireBaseUI"]),
],
dependencies: [
Expand Down Expand Up @@ -117,9 +118,22 @@ let SwiftFirebase =
.linkedLibrary("absl_cordz_functions"),
.linkedLibrary("absl_cordz_handle"),
.linkedLibrary("absl_cordz_info"),
.linkedLibrary("absl_crc_cord_state"),
.linkedLibrary("absl_crc_cpu_detect"),
.linkedLibrary("absl_crc_internal"),
.linkedLibrary("absl_crc32c"),
.linkedLibrary("absl_flags_commandlineflag_internal"),
.linkedLibrary("absl_flags_commandlineflag"),
.linkedLibrary("absl_flags_config"),
.linkedLibrary("absl_flags_internal"),
.linkedLibrary("absl_flags_marshalling"),
.linkedLibrary("absl_flags_private_handle_accessor"),
.linkedLibrary("absl_flags_program_name"),
.linkedLibrary("absl_flags_reflection"),
.linkedLibrary("absl_graphcycles_internal"),
.linkedLibrary("absl_hash"),
.linkedLibrary("absl_int128"),
.linkedLibrary("absl_kernel_timeout_internal"),
.linkedLibrary("absl_low_level_hash"),
.linkedLibrary("absl_malloc_internal"),
.linkedLibrary("absl_random_internal_platform"),
Expand All @@ -138,6 +152,7 @@ let SwiftFirebase =
.linkedLibrary("absl_statusor"),
.linkedLibrary("absl_str_format_internal"),
.linkedLibrary("absl_strerror"),
.linkedLibrary("absl_string_view"),
.linkedLibrary("absl_strings"),
.linkedLibrary("absl_strings_internal"),
.linkedLibrary("absl_symbolize"),
Expand All @@ -158,7 +173,25 @@ let SwiftFirebase =
.linkedLibrary("protobuf-nanopb"),
.linkedLibrary("re2"),
.linkedLibrary("snappy"),
.linkedLibrary("upb"),
.linkedLibrary("upb_base_lib"),
.linkedLibrary("upb_json_lib"),
.linkedLibrary("upb_mem_lib"),
.linkedLibrary("upb_message_lib"),
.linkedLibrary("upb_textformat_lib"),
.linkedLibrary("utf8_range_lib"),
.linkedLibrary("utf8_validity"),
]),
.target(name: "FirebaseFunctions",
dependencies: ["firebase", "FirebaseCore"],
cxxSettings: [
.define("INTERNAL_EXPERIMENTAL"),
.define("_CRT_SECURE_NO_WARNINGS",
.when(platforms: [.windows])),
.headerSearchPath("../../third_party/firebase-development/usr/include"),
],
swiftSettings: [
.interoperabilityMode(.Cxx),
.unsafeFlags(["-Xcc", "-I\(include)"]),
]),
.executableTarget(name: "FireBaseUI",
dependencies: [
Expand Down
42 changes: 42 additions & 0 deletions Sources/FirebaseCore/Variant+Swift.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: BSD-3-Clause

@_exported
import firebase

enum VariantConversionError: Error {
case unsupportedType
}

@_spi(FirebaseInternal)
public func toVariant(_ object: Any?) throws -> firebase.Variant {
if object == nil {
return .init()
} else if let string = object as? String {
return .init(std.string(string))
} else if let dict = object as? Dictionary<String, Any> {
var result = firebase.Variant()
result.set_map(.init())
try dict.forEach { element in
let value = try toVariant(element.value)
swift_firebase.swift_cxx_shims.firebase.variant_map_insert(
&result, firebase.Variant(std.string(element.key)), value
)
}
return result
} else {
// TODO: Handle other data types.
darinf marked this conversation as resolved.
Show resolved Hide resolved
throw VariantConversionError.unsupportedType
}
}

@_spi(FirebaseInternal)
public func fromVariant(_ variant: firebase.Variant) throws -> Any? {
if variant.is_bool() {
return swift_firebase.swift_cxx_shims.firebase.variant_bool_value(variant)
} else if variant.is_null() {
return nil
} else {
// TODO: Handle other data types.
throw VariantConversionError.unsupportedType
}
}
82 changes: 82 additions & 0 deletions Sources/FirebaseFunctions/FirebaseFunctionsErrorCode.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: BSD-3-Clause

@_exported
import firebase
@_spi(FirebaseInternal)
import FirebaseCore

public struct FirebaseFunctionsErrorCode: Error {
public let rawValue: Int
public let localizedDescription: String

internal init(_ params: (code: Int32, message: String)) {
self.rawValue = Int(params.code)
localizedDescription = params.message
}

private init(_ error: firebase.functions.Error) {
self.init(rawValue: Int(error.rawValue))
}
}

extension FirebaseFunctionsErrorCode: RawRepresentable {
public typealias RawValue = Int

public init(rawValue: Int) {
self.rawValue = rawValue
localizedDescription = "\(rawValue)"
}
}

extension FirebaseFunctionsErrorCode {
init(_ error: firebase.functions.Error, errorMessage: String?) {
self.init((code: error.rawValue, message: errorMessage ?? "\(error.rawValue)"))
}

init?(_ error: firebase.functions.Error?, errorMessage: UnsafePointer<CChar>?) {
guard let actualError = error, actualError.rawValue != 0 else { return nil }
var errorMessageString: String?
if let errorMessage {
errorMessageString = .init(cString: errorMessage)
}
self.init(actualError, errorMessage: errorMessageString)
}
}

extension FirebaseFunctionsErrorCode {
public static var none: Self { .init(firebase.functions.kErrorNone) }
public static var cancelled: Self { .init(firebase.functions.kErrorCancelled) }
public static var unknown: Self { .init(firebase.functions.kErrorUnknown) }
public static var invalidArgument: Self { .init(firebase.functions.kErrorInvalidArgument) }
public static var deadlineExceeded: Self { .init(firebase.functions.kErrorDeadlineExceeded) }
public static var notFound: Self { .init(firebase.functions.kErrorNotFound) }
public static var alreadyExists: Self { .init(firebase.functions.kErrorAlreadyExists) }
public static var permissionDenied: Self { .init(firebase.functions.kErrorPermissionDenied) }
public static var unauthenticated: Self { .init(firebase.functions.kErrorUnauthenticated) }
public static var resourceExhausted: Self { .init(firebase.functions.kErrorResourceExhausted) }
public static var failedPrecondition: Self { .init(firebase.functions.kErrorFailedPrecondition) }
public static var aborted: Self { .init(firebase.functions.kErrorAborted) }
public static var outOfRange: Self { .init(firebase.functions.kErrorOutOfRange) }
public static var unimplemented: Self { .init(firebase.functions.kErrorUnimplemented) }
public static var `internal`: Self { .init(firebase.functions.kErrorInternal) }
public static var unavailable: Self { .init(firebase.functions.kErrorUnavailable) }
public static var dataLoss: Self { .init(firebase.functions.kErrorDataLoss) }
}

extension FirebaseFunctionsErrorCode: Equatable {}

extension FirebaseFunctionsErrorCode {
// The Obj C API provides this type as well, so provide it here for consistency.
public typealias Code = FirebaseFunctionsErrorCode

// This allows us to re-expose self as a code similarly
// to what the Firebase SDK does when it creates the
// underlying NSErrors on iOS/macOS.
public var code: Code {
return self
}

public init(_ code: Code) {
self.init(rawValue: code.rawValue)
}
}
28 changes: 28 additions & 0 deletions Sources/FirebaseFunctions/Functions+Swift.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: BSD-3-Clause

@_exported
import firebase
@_spi(FirebaseInternal)
import FirebaseCore

import CxxShim

public class Functions {
let impl: swift_firebase.swift_cxx_shims.firebase.functions.FunctionsRef

init(_ impl: swift_firebase.swift_cxx_shims.firebase.functions.FunctionsRef) {
self.impl = impl
}

public static func functions(app: FirebaseApp) -> Functions {
let instance = swift_firebase.swift_cxx_shims.firebase.functions.functions_get_instance(app)
guard swift_firebase.swift_cxx_shims.firebase.functions.functions_is_valid(instance) else {
fatalError("Invalid Functions Instance")
}
return .init(instance)
}

public func httpsCallable(_ name: String) -> HTTPSCallable {
.init(swift_firebase.swift_cxx_shims.firebase.functions.functions_get_https_callable(impl, name))
}
}
46 changes: 46 additions & 0 deletions Sources/FirebaseFunctions/HTTPSCallable+Swift.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: BSD-3-Clause

@_exported
import firebase
@_spi(FirebaseInternal)
import FirebaseCore

import CxxShim
import Foundation

public class HTTPSCallable {
let impl: firebase.functions.HttpsCallableReference

init(_ impl: firebase.functions.HttpsCallableReference) {
self.impl = impl
}

public func call(_ data: Any? = nil, completion: @escaping (HTTPSCallableResult?, Error?) -> Void) {
callImpl(data: data) { result, error in
DispatchQueue.main.async {
completion(result, error)
}
}
}

public func call(_ data: Any? = nil) async throws -> HTTPSCallableResult {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<HTTPSCallableResult, any Error>) in
callImpl(data: data) { result, error in
if let error {
continuation.resume(throwing: error)
} else{
continuation.resume(returning: result ?? .init())
}
}
}
}

private func callImpl(data: Any?, completion: @escaping (HTTPSCallableResult?, Error?) -> Void) {
let variant = try! toVariant(data)
let future = swift_firebase.swift_cxx_shims.firebase.functions.https_callable_call(impl, variant)
future.setCompletion({
let (result, error) = future.resultAndError { FirebaseFunctionsErrorCode($0) }
completion(result.map { .init($0) }, error)
})
}
}
21 changes: 21 additions & 0 deletions Sources/FirebaseFunctions/HTTPSCallableResult+Swift.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: BSD-3-Clause

@_exported
import firebase
@_spi(FirebaseInternal)
import FirebaseCore

import CxxShim
import Foundation

public class HTTPSCallableResult {
let data: Any

init(_ result: firebase.functions.HttpsCallableResult = .init()) {
let variant = swift_firebase.swift_cxx_shims.firebase.functions.https_callable_result_data(result)
let data = try! fromVariant(variant)

// For compatibility with the ObjC API, map nil to NSNull here.
self.data = data ?? NSNull()
}
}
14 changes: 14 additions & 0 deletions Sources/firebase/include/FirebaseCore.hh
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ class SWIFT_CONFORMS_TO_PROTOCOL(FirebaseCore.FutureProtocol)
// result. Swift is not able to handle a `ResultType` of `void`.
typedef Future<int> VoidFuture;

// VARIANT support

inline void
variant_map_insert(::firebase::Variant* variant,
const ::firebase::Variant& key,
const ::firebase::Variant& value) {
variant->map()[key] = value;
}

inline bool
variant_bool_value(const ::firebase::Variant& variant) {
return variant.bool_value(); // Someone was being too cute!
}

} // namespace swift_firebase::swift_cxx_shims::firebase

#endif
Loading
Loading