├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── CMakeLists.txt ├── Examples └── FireBaseUI │ ├── FireBaseUI.exe.manifest │ ├── FireBaseUI.swift │ ├── FireBaseUIViewController.swift │ ├── FirestoreTestingViewController.swift │ ├── Info.plist │ ├── Resources │ └── google-services.json │ ├── SceneDelegate.swift │ └── SwiftWin32+Extensions.swift ├── LICENSE ├── Package.swift ├── README.md ├── Sources ├── FirebaseAndroid │ ├── CMakeLists.txt │ ├── Native.java │ ├── abi.h │ ├── include │ │ ├── FirebaseAndroid.h │ │ └── module.modulemap │ ├── jni.c │ └── log.h ├── FirebaseAuth │ ├── AuthStateDidChangeListenerHandle.swift │ ├── FIRActionCodeOperation.swift │ ├── FIRAuthTokenResult.swift │ ├── FirebaseAuth+Swift.swift │ ├── FirebaseAuthError.swift │ ├── FirebaseAuthResult+Swift.swift │ ├── FirebaseEmailAuthProvider.swift │ └── FirebaseUser+Swift.swift ├── FirebaseCore │ ├── FirebaseApp+Swift.swift │ ├── FirebaseConfiguration.swift │ ├── FirebaseLogging+Swift.swift │ ├── FirebaseOptions+Swift.swift │ ├── FutureProtocol.swift │ └── Variant+Swift.swift ├── FirebaseFirestore │ ├── CollectionReference+Swift.swift │ ├── DocumentChange+Swift.swift │ ├── DocumentReference+Swift.swift │ ├── DocumentSnapshot+Swift.swift │ ├── FieldValue+Swift.swift │ ├── Firestore+Swift.swift │ ├── FirestoreDataConverter.swift │ ├── FirestoreErrorCode.swift │ ├── FirestoreSource+Swift.swift │ ├── ListenerRegistration.swift │ ├── Query+Swift.swift │ ├── QueryDocumentSnapshot.swift │ ├── QuerySnapshot+Swift.swift │ ├── Settings+Swift.swift │ ├── SnapshotMetadata+Swift.swift │ ├── Timestamp+Swift.swift │ ├── Transaction+Swift.swift │ ├── TransactionOptions+Swift.swift │ ├── WriteBatch+Swift.swift │ └── vendor │ │ ├── Codable │ │ ├── CodableErrors.swift │ │ ├── CodablePassThroughTypes.swift │ │ └── DocumentID.swift │ │ ├── FirebaseDataEncoder │ │ ├── FirebaseDataEncoder.swift │ │ └── FirebaseRemoteConfigValueDecoding.swift │ │ ├── LICENSE │ │ └── README.md ├── FirebaseFunctions │ ├── Functions+Swift.swift │ ├── FunctionsErrorCode.swift │ ├── HTTPSCallable+Swift.swift │ └── HTTPSCallableResult+Swift.swift ├── FirebaseStorage │ ├── Storage+Swift.swift │ ├── StorageErrorCode.swift │ ├── StorageMetadata+Swift.swift │ └── StorageReference+Swift.swift └── firebase │ └── include │ ├── FirebaseApp.hh │ ├── FirebaseAuth.hh │ ├── FirebaseCore.hh │ ├── FirebaseFirestore.hh │ ├── FirebaseFunctions.hh │ ├── FirebaseLogging.hh │ ├── FirebaseStorage.hh │ ├── TransactionWeakReference.hh │ └── module.modulemap └── patches └── 0001-Add-a-couple-of-workarounds-for-Swift-on-Windows.patch /.gitattributes: -------------------------------------------------------------------------------- 1 | .gitattributes eol=lf 2 | .gitignore eol=lf 3 | *.hh eol=lf 4 | *.json eol=lf 5 | *.plist eol=lf 6 | *.swift eol=lf 7 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | spm: 11 | runs-on: windows-latest 12 | 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | include: 17 | - branch: development 18 | tag: DEVELOPMENT-SNAPSHOT-2024-02-08-a 19 | options: -Xswiftc "-I${env:SDKROOT}\..\..\..\..\..\..\Toolchains\0.0.0+Asserts\usr\include" 20 | 21 | name: SPM (Windows) - Swift ${{ matrix.tag }} 22 | 23 | steps: 24 | - uses: compnerd/gha-setup-swift@main 25 | with: 26 | tag: ${{ matrix.tag }} 27 | branch: ${{ matrix.branch }} 28 | 29 | - uses: actions/checkout@v4 30 | 31 | - uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4 # v1.1.1 32 | with: 33 | repo: thebrowsercompany/firebase-cpp-sdk 34 | version: tags/20240909.0 35 | file: firebase-windows-amd64.zip 36 | 37 | - run: | 38 | Expand-Archive -Path firebase-windows-amd64.zip -DestinationPath third_party 39 | Rename-Item -Path third_party/firebase-windows-amd64 -NewName firebase-development 40 | shell: powershell 41 | 42 | - name: Build 43 | run: swift build -v ${{ matrix.options }} 44 | 45 | cmake: 46 | runs-on: windows-latest 47 | 48 | strategy: 49 | fail-fast: false 50 | matrix: 51 | include: 52 | - branch: development 53 | tag: DEVELOPMENT-SNAPSHOT-2024-02-08-a 54 | options: -I${env:SDKROOT}\..\..\..\..\..\..\Toolchains\0.0.0+Asserts\usr\include 55 | 56 | name: CMake (Windows) - Swift ${{ matrix.tag }} 57 | 58 | steps: 59 | - uses: compnerd/gha-setup-vsdevenv@main 60 | 61 | - uses: compnerd/gha-setup-swift@main 62 | with: 63 | tag: ${{ matrix.tag }} 64 | branch: ${{ matrix.branch }} 65 | 66 | - uses: actions/checkout@v4 67 | 68 | - uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4 # v1.1.1 69 | with: 70 | repo: thebrowsercompany/firebase-cpp-sdk 71 | version: tags/20240909.0 72 | file: firebase-windows-amd64.zip 73 | 74 | - run: | 75 | Expand-Archive -Path firebase-windows-amd64.zip -DestinationPath third_party 76 | Rename-Item -Path third_party/firebase-windows-amd64 -NewName firebase-development 77 | shell: powershell 78 | 79 | - name: Configure 80 | run: | 81 | cmake -B out ` 82 | -D CMAKE_BUILD_TYPE=Release ` 83 | -G Ninja ` 84 | -S ${{ github.workspace }} ` 85 | -D CMAKE_Swift_FLAGS="${{ matrix.options }}" ` 86 | -D SWIFT_FIREBASE_BUILD_EXAMPLES=NO 87 | 88 | - name: Build 89 | run: cmake --build out 90 | 91 | android: 92 | runs-on: windows-latest 93 | 94 | strategy: 95 | fail-fast: false 96 | matrix: 97 | include: 98 | - branch: development 99 | tag: DEVELOPMENT-SNAPSHOT-2024-08-02-a 100 | 101 | abi: arm64-v8a 102 | options: -sdk ${env:SDKROOT}..\..\..\..\Android.platform\Developer\SDKs\Android.sdk -sysroot ${env:ANDROID_NDK_ROOT}\toolchains\llvm\prebuilt\windows-x86_64\sysroot -I${env:SDKROOT}\..\..\..\..\..\..\Toolchains\0.0.0+Asserts\usr\include -I${env:SDKROOT}\usr\include -Xlinker -zdefs 103 | target: aarch64-unknown-linux-android28 104 | 105 | - branch: development 106 | tag: DEVELOPMENT-SNAPSHOT-2024-08-02-a 107 | 108 | abi: x86_64 109 | options: -sdk ${env:SDKROOT}..\..\..\..\Android.platform\Developer\SDKs\Android.sdk -sysroot ${env:ANDROID_NDK_ROOT}\toolchains\llvm\prebuilt\windows-x86_64\sysroot -I${env:SDKROOT}\..\..\..\..\..\..\Toolchains\0.0.0+Asserts\usr\include -I${env:SDKROOT}\usr\include -Xlinker -zdefs 110 | target: x86_64-unknown-linux-android28 111 | 112 | name: CMake (Android) - Swift ${{ matrix.tag }} 113 | 114 | steps: 115 | - uses: compnerd/gha-setup-vsdevenv@main 116 | 117 | - uses: compnerd/gha-setup-swift@main 118 | with: 119 | # tag: ${{ matrix.tag }} 120 | # branch: ${{ atrix.branch }} 121 | github-repo: thebrowsercompany/swift-build 122 | github-token: ${{ secrets.GITHUB_TOKEN }} 123 | release-asset-name: installer-amd64.exe 124 | release-tag-name: "20240909.3" 125 | 126 | - uses: actions/checkout@v4 127 | 128 | - uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4 # v1.1.1 129 | with: 130 | repo: thebrowsercompany/firebase-cpp-sdk 131 | version: tags/20240909.0 132 | file: firebase-android-${{ matrix.abi }}.zip 133 | 134 | - run: | 135 | Expand-Archive -Path firebase-android-${{ matrix.abi }}.zip -DestinationPath third_party 136 | Rename-Item -Path third_party/firebase-android-${{ matrix.abi}} -NewName firebase-development 137 | shell: powershell 138 | 139 | - uses: nttld/setup-ndk@afb4c9964b521afb97c864b7d40b11e6911bd410 # v1.5.0 140 | id: setup-ndk 141 | with: 142 | ndk-version: r26d 143 | 144 | - uses: actions/setup-java@v4 145 | with: 146 | java-version: 17 147 | distribution: temurin 148 | 149 | - uses: android-actions/setup-android@00854ea68c109d98c75d956347303bf7c45b0277 # v3.2.1 150 | with: 151 | packages: 'platforms;android-21' 152 | 153 | - name: Configure 154 | env: 155 | ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} 156 | run: | 157 | cmake -B out ` 158 | -D CMAKE_BUILD_TYPE=RelWithDebInfo ` 159 | -G Ninja ` 160 | -S ${{ github.workspace }} ` 161 | -D CMAKE_SYSTEM_NAME=Android ` 162 | -D CMAKE_ANDROID_ARCH_ABI=${{ matrix.abi }} ` 163 | -D CMAKE_ANDROID_API=21 ` 164 | -D CMAKE_Swift_COMPILER_TARGET=${{ matrix.target }} ` 165 | -D CMAKE_Swift_COMPILER_WORKS=YES ` 166 | -D CMAKE_Swift_FLAGS="${{ matrix.options }}" ` 167 | -D SWIFT_FIREBASE_BUILD_EXAMPLES=NO 168 | 169 | - name: Build 170 | run: cmake --build out 171 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw? 2 | .build/ 3 | build/ 4 | .vscode/ 5 | third_party/ 6 | Examples/FireBaseUI/Resources/google-services-desktop.json 7 | Package.resolved 8 | out/* 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: BSD-3-Clause 2 | 3 | cmake_minimum_required(VERSION 3.25) 4 | project(swift-firebase 5 | LANGUAGES Swift) 6 | if(ANDROID) 7 | enable_language(C) 8 | include(FindJava) 9 | include(FindJNI) 10 | include(UseJava) 11 | endif() 12 | 13 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 14 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 15 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 16 | set(CMAKE_Swift_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/swift) 17 | 18 | include(FetchContent) 19 | 20 | option(SWIFT_FIREBASE_BUILD_EXAMPLES "Build example UI" TRUE) 21 | 22 | # ${TOOLCHAIN_ROOT}/usr/include allows us to access swift C++ interop headers. 23 | cmake_path(GET CMAKE_Swift_COMPILER PARENT_PATH _SWIFT_INCLUDE_DIR) 24 | cmake_path(GET _SWIFT_INCLUDE_DIR PARENT_PATH _SWIFT_INCLUDE_DIR) 25 | include_directories(SYSTEM ${_SWIFT_INCLUDE_DIR}/include) 26 | 27 | add_library(firebase INTERFACE) 28 | target_compile_options(firebase INTERFACE 29 | "SHELL:$<$:-Xcc -DSR69711 -Xcc -DSR74578>" 30 | "SHELL:$<$:-Xcc -DINTERNAL_EXPERIMENTAL>") 31 | target_include_directories(firebase INTERFACE 32 | Sources/firebase/include 33 | third_party/firebase-development/usr/include) 34 | if(ANDROID) 35 | target_link_directories(firebase INTERFACE 36 | third_party/firebase-development/usr/libs/android/${CMAKE_ANDROID_ARCH_ABI}) 37 | 38 | # Add the compiler resource directory as a library search path explicitly as 39 | # we do not use `clang` from the NDK but do require `libunwind.a` which is not 40 | # in the sysroot (platform SDK) but rather placed into the resource directory 41 | # on the Android platform. 42 | if(CMAKE_ANDROID_ARCH STREQUAL arm64) 43 | target_link_directories(firebase INTERFACE 44 | $ENV{ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/windows-x86_64/lib/clang/17/lib/linux/aarch64) 45 | elseif(CMAKE_ANDROID_ARCH STREQUAL arm) 46 | target_link_directories(firebase INTERFACE 47 | $ENV{ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/windows-x86_64/lib/clang/17/lib/linux/arm) 48 | elseif(CMAKE_ANDROID_ARCH STREQUAL x86) 49 | target_link_directories(firebase INTERFACE 50 | $ENV{ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/windows-x86_64/lib/clang/17/lib/linux/i386) 51 | elseif(CMAKE_ANDROID_ARCH STREQUAL x86_64) 52 | target_link_directories(firebase INTERFACE 53 | $ENV{ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/windows-x86_64/lib/clang/17/lib/linux/x86_64) 54 | else() 55 | message(SEND_ERROR "unsupported architecture for Android") 56 | endif() 57 | elseif(WIN32) 58 | target_link_directories(firebase INTERFACE 59 | third_party/firebase-development/usr/libs/windows 60 | third_party/firebase-development/usr/libs/windows/deps/app 61 | third_party/firebase-development/usr/libs/windows/deps/app/external) 62 | else() 63 | message(FATAL_ERROR "unsupported firebase-cpp-sdk platform") 64 | endif() 65 | 66 | if(ANDROID) 67 | add_subdirectory(Sources/FirebaseAndroid) 68 | endif() 69 | 70 | add_library(FirebaseCore SHARED 71 | Sources/FirebaseCore/FirebaseApp+Swift.swift 72 | Sources/FirebaseCore/FirebaseConfiguration.swift 73 | Sources/FirebaseCore/FirebaseLogging+Swift.swift 74 | Sources/FirebaseCore/FirebaseOptions+Swift.swift 75 | Sources/FirebaseCore/FutureProtocol.swift 76 | Sources/FirebaseCore/Variant+Swift.swift) 77 | target_compile_options(FirebaseCore PRIVATE 78 | -cxx-interoperability-mode=default) 79 | target_link_libraries(FirebaseCore PUBLIC 80 | firebase) 81 | target_link_libraries(FirebaseCore PRIVATE 82 | firebase_app 83 | flatbuffers 84 | $<$:log> 85 | $<$:libcurl> 86 | $<$:zlibstatic>) 87 | if(ANDROID) 88 | target_link_libraries(FirebaseCore PUBLIC 89 | FirebaseAndroidJNI) 90 | endif() 91 | 92 | add_library(FirebaseAuth SHARED 93 | Sources/FirebaseAuth/AuthStateDidChangeListenerHandle.swift 94 | Sources/FirebaseAuth/FIRActionCodeOperation.swift 95 | Sources/FirebaseAuth/FIRAuthTokenResult.swift 96 | Sources/FirebaseAuth/FirebaseAuth+Swift.swift 97 | Sources/FirebaseAuth/FirebaseAuthError.swift 98 | Sources/FirebaseAuth/FirebaseAuthResult+Swift.swift 99 | Sources/FirebaseAuth/FirebaseEmailAuthProvider.swift 100 | Sources/FirebaseAuth/FirebaseUser+Swift.swift) 101 | target_compile_options(FirebaseAuth PRIVATE 102 | -cxx-interoperability-mode=default) 103 | target_link_libraries(FirebaseAuth PUBLIC 104 | firebase 105 | FirebaseCore) 106 | target_link_libraries(FirebaseAuth PRIVATE 107 | flatbuffers 108 | $<$:crypto> 109 | $<$:firebase_rest_lib> 110 | $<$:libcurl> 111 | $<$:ssl> 112 | $<$:zlibstatic>) 113 | 114 | add_library(FirebaseFirestore SHARED 115 | Sources/FirebaseFirestore/Vendor/Codable/CodableErrors.swift 116 | Sources/FirebaseFirestore/Vendor/Codable/CodablePassThroughTypes.swift 117 | Sources/FirebaseFirestore/Vendor/Codable/DocumentID.swift 118 | Sources/FirebaseFirestore/Vendor/FirebaseDataEncoder/FirebaseDataEncoder.swift 119 | Sources/FirebaseFirestore/Vendor/FirebaseDataEncoder/FirebaseRemoteConfigValueDecoding.swift 120 | Sources/FirebaseFirestore/CollectionReference+Swift.swift 121 | Sources/FirebaseFirestore/DocumentChange+Swift.swift 122 | Sources/FirebaseFirestore/DocumentReference+Swift.swift 123 | Sources/FirebaseFirestore/DocumentSnapshot+Swift.swift 124 | Sources/FirebaseFirestore/FieldValue+Swift.swift 125 | Sources/FirebaseFirestore/Firestore+Swift.swift 126 | Sources/FirebaseFirestore/FirestoreDataConverter.swift 127 | Sources/FirebaseFirestore/FirestoreErrorCode.swift 128 | Sources/FirebaseFirestore/FirestoreSource+Swift.swift 129 | Sources/FirebaseFirestore/ListenerRegistration.swift 130 | Sources/FirebaseFirestore/Query+Swift.swift 131 | Sources/FirebaseFirestore/QueryDocumentSnapshot.swift 132 | Sources/FirebaseFirestore/QuerySnapshot+Swift.swift 133 | Sources/FirebaseFirestore/Settings+Swift.swift 134 | Sources/FirebaseFirestore/SnapshotMetadata+Swift.swift 135 | Sources/FirebaseFirestore/Timestamp+Swift.swift 136 | Sources/FirebaseFirestore/Transaction+Swift.swift 137 | Sources/FirebaseFirestore/TransactionOptions+Swift.swift 138 | Sources/FirebaseFirestore/WriteBatch+Swift.swift) 139 | target_compile_options(FirebaseFirestore PRIVATE 140 | -cxx-interoperability-mode=default) 141 | target_link_libraries(FirebaseFirestore PUBLIC 142 | FirebaseCore 143 | $<$:crypto> 144 | $<$:libcurl> 145 | $<$:ssl> 146 | $<$:zlibstatic>) 147 | if(WIN32) 148 | target_link_libraries(FirebaseFirestore PUBLIC 149 | absl_bad_optional_access 150 | absl_bad_variant_access 151 | absl_base 152 | absl_city 153 | absl_cord 154 | absl_cord_internal 155 | absl_cordz_functions 156 | absl_cordz_handle 157 | absl_cordz_info 158 | absl_crc_cord_state 159 | absl_crc_cpu_detect 160 | absl_crc_internal 161 | absl_crc32c 162 | absl_flags_commandlineflag_internal 163 | absl_flags_commandlineflag 164 | absl_flags_config 165 | absl_flags_internal 166 | absl_flags_marshalling 167 | absl_flags_private_handle_accessor 168 | absl_flags_program_name 169 | absl_flags_reflection 170 | absl_graphcycles_internal 171 | absl_hash 172 | absl_int128 173 | absl_kernel_timeout_internal 174 | absl_low_level_hash 175 | absl_malloc_internal 176 | absl_random_internal_platform 177 | absl_random_internal_pool_urbg 178 | absl_random_internal_randen 179 | absl_random_internal_randen_hwaes 180 | absl_random_internal_randen_hwaes_impl 181 | absl_random_internal_randen_slow 182 | absl_random_internal_seed_material 183 | absl_random_seed_gen_exception 184 | absl_raw_hash_set 185 | absl_raw_logging_internal 186 | absl_spinlock_wait 187 | absl_stacktrace 188 | absl_status 189 | absl_statusor 190 | absl_str_format_internal 191 | absl_strerror 192 | absl_string_view 193 | absl_strings 194 | absl_strings_internal 195 | absl_symbolize 196 | absl_synchronization 197 | absl_throw_delegate 198 | absl_time 199 | absl_time_zone 200 | address_sorting 201 | cares 202 | firebase 203 | firebase_rest_lib 204 | firestore_core 205 | firestore_nanopb 206 | firestore_protos_nanopb 207 | firestore_util 208 | flatbuffers 209 | gpr 210 | grpc 211 | grpc++ 212 | leveldb 213 | protobuf-nanopb 214 | re2 215 | snappy 216 | upb_base_lib 217 | upb_json_lib 218 | upb_mem_lib 219 | upb_message_lib 220 | upb_textformat_lib 221 | utf8_range_lib 222 | utf8_validity) 223 | endif() 224 | 225 | add_library(FirebaseFunctions SHARED 226 | Sources/FirebaseFunctions/FunctionsErrorCode.swift 227 | Sources/FirebaseFunctions/Functions+Swift.swift 228 | Sources/FirebaseFunctions/HTTPSCallable+Swift.swift 229 | Sources/FirebaseFunctions/HTTPSCallableResult+Swift.swift) 230 | target_compile_options(FirebaseFunctions PRIVATE 231 | -cxx-interoperability-mode=default) 232 | target_link_libraries(FirebaseFunctions PUBLIC 233 | firebase 234 | firebase_functions 235 | FirebaseCore) 236 | target_link_libraries(FirebaseFunctions PRIVATE 237 | flatbuffers 238 | $<$:crypto> 239 | $<$:firebase_rest_lib> 240 | $<$:libcurl> 241 | $<$:ssl> 242 | $<$:zlibstatic>) 243 | 244 | add_library(FirebaseStorage SHARED 245 | Sources/FirebaseStorage/StorageErrorCode.swift 246 | Sources/FirebaseStorage/Storage+Swift.swift 247 | Sources/FirebaseStorage/StorageMetadata+Swift.swift 248 | Sources/FirebaseStorage/StorageReference+Swift.swift) 249 | target_compile_options(FirebaseStorage PRIVATE 250 | -cxx-interoperability-mode=default) 251 | target_link_libraries(FirebaseStorage PUBLIC 252 | firebase 253 | firebase_storage 254 | FirebaseCore) 255 | target_link_libraries(FirebaseStorage PRIVATE 256 | flatbuffers 257 | $<$:crypto> 258 | $<$:firebase_rest_lib> 259 | $<$:libcurl> 260 | $<$:ssl> 261 | $<$:zlibstatic>) 262 | 263 | if(SWIFT_FIREBASE_BUILD_EXAMPLES) 264 | FetchContent_Declare(SwiftWin32 265 | GIT_REPOSITORY https://github.com/compnerd/swift-win32 266 | GIT_TAG 07e91e67e86f173743329c6753d9e66ac4727830) # Pinned for reproducibility and before Package@swift-#.#.swift symlinks 267 | FetchContent_MakeAvailable(SwiftWin32) 268 | 269 | add_executable(FireBaseUI 270 | Examples/FireBaseUI/FireBaseUI.swift 271 | Examples/FireBaseUI/FireBaseUIViewController.swift 272 | Examples/FireBaseUI/FirestoreTestingViewController.swift 273 | Examples/FireBaseUI/SceneDelegate.swift 274 | Examples/FireBaseUI/SwiftWin32+Extensions.swift) 275 | target_compile_options(FireBaseUI PRIVATE 276 | -parse-as-library 277 | -cxx-interoperability-mode=default) 278 | target_link_libraries(FireBaseUI PRIVATE 279 | FirebaseCore 280 | FirebaseAuth 281 | FirebaseFirestore 282 | SwiftWin32) 283 | add_custom_command(TARGET FireBaseUI POST_BUILD 284 | COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/Examples/FireBaseUI/Resources $/Resources 285 | COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/Examples/FireBaseUI/Info.plist $/Info.plist 286 | COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/Examples/FireBaseUI/$.manifest $/$.manifest) 287 | endif() 288 | -------------------------------------------------------------------------------- /Examples/FireBaseUI/FireBaseUI.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | PerMonitorV2 18 | true 19 | 20 | 21 | 22 | FireBaseUI 23 | 24 | 25 | 26 | 27 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Examples/FireBaseUI/FireBaseUI.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | import SwiftWin32 4 | import Foundation 5 | 6 | import FirebaseCore 7 | import FirebaseAuth 8 | 9 | extension Foundation.Bundle { 10 | internal static var resources: URL { 11 | #if SWIFT_PACKAGE 12 | Bundle.module.bundleURL.appendingPathComponent("Resources") 13 | #else 14 | Bundle.main.bundleURL.appendingPathComponent("Resources") 15 | #endif 16 | } 17 | } 18 | 19 | @main 20 | final class FireBaseUI: ApplicationDelegate { 21 | func application(_ application: Application, 22 | didFinishLaunchingWithOptions: [Application.LaunchOptionsKey:Any]?) 23 | -> Bool { 24 | #if _runtime(_ObjC) 25 | firebase.App.SetDefaultConfigPath(Bundle.resources.fileSystemRepresentation) 26 | #else 27 | Bundle.resources.withUnsafeFileSystemRepresentation(firebase.App.SetDefaultConfigPath) 28 | #endif 29 | 30 | let path = Bundle.resources.appendingPathComponent("google-services.json") 31 | guard let options = FirebaseOptions(_contentsOfFile: path, format: .json) else { 32 | fatalError("Unable to create options from JSON file!") 33 | } 34 | 35 | FirebaseApp.configure(options: options) 36 | return true 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Examples/FireBaseUI/FireBaseUIViewController.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | import FirebaseCore 4 | import FirebaseFirestore 5 | import FirebaseAuth 6 | import SwiftWin32 7 | 8 | private final class FireBaseLogLevelPickerHandler { 9 | private let levels = ["Default", "Verbose", "Debug", "Info", "Warning", "Error", "Assert"] 10 | } 11 | 12 | extension FireBaseLogLevelPickerHandler: PickerViewDataSource { 13 | public func numberOfComponents(in pickerView: PickerView) -> Int { 14 | 1 15 | } 16 | 17 | public func pickerView(_ pickerView: PickerView, 18 | numberOfRowsInComponent component: Int) -> Int { 19 | self.levels.count 20 | } 21 | } 22 | 23 | extension FireBaseLogLevelPickerHandler: PickerViewDelegate { 24 | public func pickerView(_ pickerView: PickerView, titleForRow row: Int, 25 | forComponent component: Int) -> String? { 26 | self.levels[row] 27 | } 28 | 29 | public func pickerView(_ pickerView: PickerView, didSelectRow row: Int, 30 | inComponent component: Int) { 31 | guard row > 0 else { return } 32 | FirebaseConfiguration.shared.setLoggerLevel(FirebaseLoggerLevel(rawValue: CInt(row - 1))) 33 | } 34 | } 35 | 36 | // MARK: - FireBaseUIViewController 37 | 38 | internal final class FireBaseUIViewController: ViewController { 39 | fileprivate let firebaseLogHandler = FireBaseLogLevelPickerHandler() 40 | 41 | var cboLogLevel = PickerView(frame: Rect(x: 136, y: 8, width: 256, height: 24)) 42 | var txtEmail = TextField(frame: Rect(x: 136, y: 46, width: 512, height: 24)) 43 | var txtPassword = TextField(frame: Rect(x: 136, y: 78, width: 512, height: 24)) 44 | var btnSignIn = Button(frame: Rect(x: 8, y: 116, width: 120, height: 32), 45 | title: "Sign In") 46 | var btnToken = Button(frame: Rect(x: 8, y: 180, width: 120, height: 32), 47 | title: "Get Token") 48 | var chkRefresh = Switch(frame: Rect(x: 8, y: 212, width: 648, height: 32), 49 | title: "Force Token Refresh") 50 | var txtToken: TextView = TextView(frame: Rect(x: 8, y: 244, width: 640, height: 48)) 51 | 52 | var btnCreate = Button(frame: Rect(x: 8, y: 324, width: 120, height: 32), 53 | title: "Create User") 54 | var btnVerify = Button(frame: Rect(x: 132, y: 324, width: 120, height: 32), 55 | title: "Verify Email") 56 | var btnReset = Button(frame: Rect(x: 256, y: 324, width: 156, height: 32), 57 | title: "Reset Password") 58 | 59 | let firestoreTestingWindow: Window = { 60 | let vc = FirestoreTestingViewController() 61 | let window = Window(frame: .init(x: 20, y: 20, width: 450, height: 500)) 62 | window.rootViewController = vc 63 | return window 64 | }() 65 | 66 | lazy var firestoreTestingButton: Button = { 67 | return Button(frame: .init(x: btnReset.frame.maxX, y: btnReset.frame.minY, 68 | width: 200, height: 32), 69 | title: "Firestore Testing") 70 | }() 71 | 72 | lazy var userDetailsLabel = { 73 | Label(frame: .init(x: btnCreate.frame.minX, y: btnReset.frame.maxY, width: view.frame.width - 16, height: 400)) 74 | }() 75 | 76 | var authStateListenerHandle: AuthStateDidChangeListenerHandle? 77 | 78 | override func viewDidLoad() { 79 | super.viewDidLoad() 80 | self.title = "FireBase UI" 81 | configureView() 82 | 83 | authStateListenerHandle = Auth.auth().addStateDidChangeListener { auth, user in 84 | print( 85 | """ 86 | Auth State Changed! 87 | Auth: \(String(describing: auth)) 88 | User: \(String(describing: user)) 89 | """) 90 | } 91 | 92 | if let user = Auth.auth().currentUser { 93 | txtEmail.text = user.email 94 | try? Auth.auth().signOut() 95 | } 96 | } 97 | 98 | private func configureView() { 99 | let lblLogLevel = Label(frame: Rect(x: 8, y: 8, width: 128, height: 24), 100 | title: "Log Level:") 101 | self.view?.addSubview(lblLogLevel) 102 | 103 | cboLogLevel.dataSource = firebaseLogHandler 104 | cboLogLevel.delegate = firebaseLogHandler 105 | self.view?.addSubview(cboLogLevel) 106 | cboLogLevel.reloadAllComponents() 107 | cboLogLevel.selectRow(0, inComponent: 0, animated: false) 108 | 109 | let lblEmail = Label(frame: Rect(x: 8, y: 46, width: 128, height: 20), 110 | title: "Email Address:") 111 | self.view?.addSubview(lblEmail) 112 | self.view?.addSubview(txtEmail) 113 | 114 | let lblPassword = Label(frame: Rect(x: 8, y: 78, width: 128, height: 20), 115 | title: "Password:") 116 | self.view?.addSubview(lblPassword) 117 | 118 | txtPassword.isSecureTextEntry = true 119 | self.view?.addSubview(txtPassword) 120 | 121 | btnSignIn.addTarget(self, action: FireBaseUIViewController.signIn, 122 | for: .primaryActionTriggered) 123 | self.view?.addSubview(btnSignIn) 124 | 125 | btnToken.addTarget(self, action: FireBaseUIViewController.getToken, 126 | for: .primaryActionTriggered) 127 | self.view?.addSubview(btnToken) 128 | 129 | self.view?.addSubview(chkRefresh) 130 | 131 | txtToken.editable = false 132 | txtToken.font = .systemFont(ofSize: 9) 133 | self.view?.addSubview(txtToken) 134 | 135 | btnCreate.addTarget(self, action: FireBaseUIViewController.createUser, 136 | for: .primaryActionTriggered) 137 | self.view?.addSubview(btnCreate) 138 | 139 | btnVerify.addTarget(self, action: FireBaseUIViewController.verifyEmail, 140 | for: .primaryActionTriggered) 141 | self.view?.addSubview(btnVerify) 142 | 143 | btnReset.addTarget(self, action: FireBaseUIViewController.resetPassword, 144 | for: .primaryActionTriggered) 145 | self.view?.addSubview(btnReset) 146 | 147 | firestoreTestingButton.addTarget(self, action: FireBaseUIViewController.showSubscriptionTestingWindow, 148 | for: .primaryActionTriggered) 149 | 150 | userDetailsLabel.text = "No User Data Fetched" 151 | 152 | view?.addSubview(firestoreTestingButton) 153 | view?.addSubview(userDetailsLabel) 154 | } 155 | 156 | private func signIn() { 157 | guard let email = txtEmail.text, let password = txtPassword.text else { 158 | print("email and password are required") 159 | return 160 | } 161 | 162 | Task { 163 | do { 164 | _ = try await Auth.auth().signIn(withEmail: email, password: password) 165 | } catch { 166 | print("Error signing in: \(error.localizedDescription)") 167 | } 168 | } 169 | } 170 | 171 | private func createUser() { 172 | guard let email = txtEmail.text, let password = txtPassword.text else { 173 | print("email and password are required") 174 | return 175 | } 176 | 177 | Task { 178 | do { 179 | _ = try await Auth.auth().createUser(withEmail: email, password: password) 180 | } catch { 181 | print("Error signing in: \(error.localizedDescription)") 182 | } 183 | } 184 | } 185 | 186 | private func getToken() { 187 | Task { 188 | guard var user = Auth.auth().currentUser else { 189 | print("user not logged in") 190 | return 191 | } 192 | 193 | if chkRefresh.isOn { 194 | do { 195 | let result = try await user.getIDTokenResult(forcingRefresh: true) 196 | txtToken.text = result.token 197 | } catch { 198 | print("Error refreshing token: \(error.localizedDescription)") 199 | } 200 | } else { 201 | do { 202 | txtToken.text = try await user.getIDToken() 203 | } catch { 204 | print("Error refreshing token: \(error.localizedDescription)") 205 | } 206 | } 207 | } 208 | } 209 | 210 | private func verifyEmail() { 211 | Task { 212 | guard var user = Auth.auth().currentUser else { 213 | print("user not logged in") 214 | return 215 | } 216 | 217 | try await user.sendEmailVerification() 218 | } 219 | } 220 | 221 | private func resetPassword() { 222 | guard let email = txtEmail.text else { 223 | print("email is required") 224 | return 225 | } 226 | 227 | Task { 228 | do { 229 | _ = try await Auth.auth().sendPasswordReset(withEmail: email) 230 | fetchUserDocument() 231 | } catch { 232 | print("Error sending password reset: \(error.localizedDescription)") 233 | } 234 | } 235 | } 236 | 237 | private func fetchUserDocument() { 238 | guard let user = Auth.auth().currentUser else { 239 | print("No current user, can't fetch document...") 240 | return 241 | } 242 | 243 | Task { 244 | do { 245 | let firestore = Firestore.firestore() 246 | let document = firestore 247 | .collection("users") 248 | .document(user.uid) 249 | let snapshot = try await document.getDocument() 250 | await MainActor.run { [weak self] in 251 | guard let self else { return } 252 | userDetailsLabel.text = snapshot.debugDescription 253 | } 254 | } catch { 255 | print("Error fetching snapshot: \(error)") 256 | } 257 | } 258 | } 259 | 260 | private func showSubscriptionTestingWindow() { 261 | guard let _ = Auth.auth().currentUser else { 262 | print("No current user, can't start Firestore testing...") 263 | return 264 | } 265 | firestoreTestingWindow.makeKeyAndVisible() 266 | } 267 | 268 | deinit { 269 | if let handle = authStateListenerHandle { 270 | Auth.auth().removeStateDidChangeListener(handle) 271 | } 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /Examples/FireBaseUI/FirestoreTestingViewController.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | import FirebaseFirestore 4 | import SwiftWin32 5 | 6 | internal final class FirestoreTestingViewController: ViewController { 7 | private let textField: TextField = .init(frame: .zero) 8 | private let startButton: Button = .init(frame: .zero, title: "Start") 9 | private let stopButton: Button = .init(frame: .zero, title: "Stop") 10 | private let fetchButton: Button = .init(frame: .zero, title: "Fetch") 11 | private let dataLabel: Label = .init(frame: .zero) 12 | 13 | private var subscription: ListenerRegistration? { 14 | didSet { 15 | fetchButton.isUserInteractionEnabled = subscription == nil 16 | startButton.isUserInteractionEnabled = subscription == nil 17 | stopButton.isUserInteractionEnabled = subscription != nil 18 | } 19 | } 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | view?.addSubview(textField) 24 | view?.addSubview(startButton) 25 | view?.addSubview(stopButton) 26 | view?.addSubview(fetchButton) 27 | view?.addSubview(dataLabel) 28 | 29 | dataLabel.text = "No Data..." 30 | 31 | title = "Firestore Testing" 32 | 33 | fetchButton.addTarget(self, 34 | action: FirestoreTestingViewController.fetchFromInput, 35 | for: .primaryActionTriggered) 36 | 37 | startButton.addTarget(self, 38 | action: FirestoreTestingViewController.startSubscriptionFromInput, 39 | for: .primaryActionTriggered) 40 | 41 | stopButton.addTarget(self, 42 | action: FirestoreTestingViewController.stopSubscription, 43 | for: .primaryActionTriggered) 44 | 45 | layoutView() 46 | } 47 | 48 | private func layoutView() { 49 | let padding = 8.0 50 | textField.frame = .init(x: padding, y: padding, width: view!.bounds.width, height: 30) 51 | 52 | let usableWidth = view!.bounds.width 53 | let buttonWidth = usableWidth / 3 54 | let buttonHeight = 30.0 55 | let buttonY = textField.frame.maxY + padding 56 | 57 | fetchButton.frame = .init(x: padding, y: buttonY, width: buttonWidth, height: buttonHeight) 58 | startButton.frame = .init(x: fetchButton.frame.maxX, y: buttonY, width: buttonWidth, height: buttonHeight) 59 | stopButton.frame = .init(x: startButton.frame.maxX, y: buttonY, width: buttonWidth, height: buttonHeight) 60 | 61 | let dataLabelY = stopButton.frame.maxY + padding 62 | dataLabel.frame = .init(x: padding, y: dataLabelY, width: usableWidth, height: view!.bounds.maxY - stopButton.frame.maxY) 63 | } 64 | 65 | func fetch(path: String) { 66 | Task { 67 | await buttonsEnabled(false) 68 | let document = Firestore.firestore().document(path) 69 | do { 70 | let snapshot = try await document.getDocument() 71 | await displayData(data: snapshot.debugDescription) 72 | } catch { 73 | await displayError(error: error) 74 | } 75 | await buttonsEnabled(true) 76 | } 77 | } 78 | 79 | func startSubscription(to path: String) { 80 | if subscription != nil { 81 | stopSubscription() 82 | } 83 | 84 | subscription = Firestore.firestore().document(path).addSnapshotListener { [weak self] snapshot, error in 85 | Task { [weak self] in 86 | let data = snapshot?.data(with: .none) 87 | await self?.displayData(data: String(describing: dump(data))) 88 | } 89 | } 90 | } 91 | 92 | func stopSubscription() { 93 | guard let subscription else { return } 94 | 95 | subscription.remove() 96 | self.subscription = nil 97 | } 98 | 99 | private func fetchFromInput() { 100 | guard let path = textField.text else { return } 101 | fetch(path: path) 102 | } 103 | 104 | private func startSubscriptionFromInput() { 105 | guard let path = textField.text else { return } 106 | startSubscription(to: path) 107 | } 108 | 109 | @MainActor 110 | private func buttonsEnabled(_ enabled: Bool) { 111 | fetchButton.isUserInteractionEnabled = enabled 112 | startButton.isUserInteractionEnabled = enabled 113 | stopButton.isUserInteractionEnabled = enabled 114 | textField.isUserInteractionEnabled = enabled 115 | } 116 | 117 | @MainActor 118 | private func displayError(error: Error) { 119 | dataLabel.text = "\(error)" 120 | } 121 | 122 | @MainActor 123 | private func displayData(data: String) { 124 | dataLabel.text = data 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Examples/FireBaseUI/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | org.compnerd.SwiftFirebase.FireBaseUI 7 | ApplicationSceneManifest 8 | 9 | ApplicationSupportsMultipleScenes 10 | 11 | SceneConfigurations 12 | 13 | UIWindowSceneSessionRoleApplication 14 | 15 | 16 | SceneConfigurationName 17 | Default Configuration 18 | SceneDelegateClassName 19 | FireBaseUI.SceneDelegate 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Examples/FireBaseUI/Resources/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_id": "test-do-not-use", 4 | "firebase_url": "https://test-do-not-use.firebaseio.com", 5 | "storage_bucket": "test-do-not-use.firebaseio.com" 6 | }, 7 | "client": [ 8 | { 9 | "api_key": [ 10 | { "current_key": "000000000000000000000000000000000000000" } 11 | ], 12 | "client_info": { 13 | "mobilesdk_app_id": "1:999999999999:ios:0000000000000000", 14 | "android_client_info": { 15 | "package_name": "com.firebaseio.test-do-not-use" 16 | } 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /Examples/FireBaseUI/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | import SwiftWin32 4 | 5 | final class SceneDelegate: WindowSceneDelegate { 6 | var window: Window? 7 | 8 | func scene(_ scene: Scene, willConnectTo session: SceneSession, 9 | options: Scene.ConnectionOptions) { 10 | guard let windowScene = scene as? WindowScene else { return } 11 | 12 | self.window = Window(windowScene: windowScene) 13 | self.window?.rootViewController = FireBaseUIViewController() 14 | self.window?.makeKeyAndVisible() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Examples/FireBaseUI/SwiftWin32+Extensions.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | import SwiftWin32 4 | 5 | extension Button { 6 | internal convenience init(frame: Rect, title: String) { 7 | self.init(frame: frame) 8 | self.setTitle(title, forState: .normal) 9 | } 10 | } 11 | 12 | extension Label { 13 | internal convenience init(frame: Rect, title: String) { 14 | self.init(frame: frame) 15 | self.text = title 16 | } 17 | } 18 | 19 | extension Switch { 20 | internal convenience init(frame: Rect, title: String) { 21 | self.init(frame: frame) 22 | self.title = title 23 | } 24 | } 25 | 26 | extension Rect { 27 | static var zero: Rect = .init(x: 0, y: 0, width: 0, height: 0) 28 | 29 | var maxY: Double { 30 | origin.y + size.height 31 | } 32 | 33 | var minY: Double { 34 | origin.y 35 | } 36 | 37 | var minX: Double { 38 | origin.x 39 | } 40 | 41 | var maxX: Double { 42 | origin.x + size.width 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, Saleem Abdulrasool 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftFirebase 2 | 3 | Swift bindings for [firebase-cpp-sdk](https://github.com/firebase/firebase-cpp-sdk), loosely modelled after the iOS APIs. It serves as both exploration of the C++ Interop as well as a means of using Firebase on Windows from Swift. 4 | 5 | ## Requirements 6 | 7 | The Swift interface to Firebase is built upon the [Firebase C++ SDK](https://github.com/firebase/firebase-cpp-sdk). As a result, the package requires that the C++ SDK is available and distributed to the clients. The package expects that the firebase SDK is present in the root of the source tree under `third_party/firebase-development/usr`. 8 | 9 | As of 2023-08-10, the Firebase SDK requires some changes to support the Swift/C++ interop. The changes for this are available [here](patches/0001-Add-a-couple-of-workarounds-for-Swift-on-Windows.patch). This has sent upstream as [firebase/firebase-cpp-sdk#1414](https://github.com/firebase/firebase-cpp-sdk/pull/1414). 10 | 11 | ## Setup 12 | 13 | ### Prerequisites 14 | 15 | 0. Make sure you have a Swift toolchain that supports C++ interop. 16 | 1. Download the lastest build of the Firebase C++ SDK from https://github.com/thebrowsercompany/firebase-cpp-sdk/tags, click on the tag and then download the pre-build artifacts. These will be called `firebase-windows-amd64.zip` or `firebase-windows-arm64.zip` depending on which architecture you'd like to build for. 17 | 3. Run `md third_party\firebase-development` to create the directory where we will extract the Firebase C++ SDK release that was just downloaded 18 | 4. Extract the Firebase C++ SDK release into the `third_party\firebase-development` directory that was just created. 19 | 5. Modify the `Examples\FireBaseUI\Resources\google-services.json` file to include the correct values from Firebase. 20 | 21 | > [!TIP] 22 | > It can be useful to mark the `google-services.json` file as assumed unchanged so you don't accidentially check it in with real credentials. To do that, you can run the following: `git update-index --assume-unchanged .\Examples\FireBaseUI\Resources\google-services.json` 23 | 24 | ### Building 25 | 26 | #### SwiftPM 27 | 28 | Assuming a build of firebase is available in the top level under `third_party\firebase-development\usr`, the package should build as a standard SPM package using: 29 | ```powershell 30 | swift build 31 | ``` 32 | 33 | A demo application is included as a sample and requires some additional setup due to the auxiliary files needing to be deployed. 34 | ```powershell 35 | swift build --product FireBaseUI 36 | copy Examples\FireBaseUI\Info.plist .build\debug\ 37 | copy Examples\FireBaseUI\FireBaseUI.exe.manifest .build\debug\ 38 | swift run 39 | ``` 40 | 41 | #### CMake 42 | 43 | Assuming a build of firebase is available in the top level under `third_party\firebase-development\usr`, the package should build as a standard CMake package using: 44 | ```powershell 45 | cmake -B out -G Ninja -S . 46 | ``` 47 | 48 | You should be able to run the demo application subsequently by just launching it as: 49 | ```powershell 50 | .\out\bin\FireBaseUI.exe 51 | ``` 52 | -------------------------------------------------------------------------------- /Sources/FirebaseAndroid/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: BSD-3-Clause 2 | 3 | add_library(FirebaseAndroidJNI SHARED 4 | jni.c) 5 | target_include_directories(FirebaseAndroidJNI PUBLIC 6 | include) 7 | target_link_libraries(FirebaseAndroidJNI PRIVATE 8 | log 9 | JNI::JNI) 10 | 11 | add_jar(SwiftFirebase 12 | Native.java 13 | INCLUDE_JARS 14 | $ENV{ANDROID_SDK_ROOT}/platforms/android-${CMAKE_ANDROID_API}/android.jar) 15 | -------------------------------------------------------------------------------- /Sources/FirebaseAndroid/Native.java: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | package company.thebrowser; 4 | 5 | import android.app.Activity; 6 | 7 | public class Native 8 | { 9 | public native boolean RegisterActivity(android.app.Activity activity); 10 | } 11 | -------------------------------------------------------------------------------- /Sources/FirebaseAndroid/abi.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: BSD-3-Clause */ 2 | 3 | #ifndef SwiftFirebase_FirebaseAndroidShim_abi_h 4 | #define SwiftFirebase_FirebaseAndroidShim_abi_h 5 | 6 | #if defined(FirebaseAndroidJNI_EXPORTS) 7 | #define FIREBASE_ANDROID_ABI __attribute__((__visibility__("default"))) 8 | #else 9 | #define FIREBASE_ANDROID_ABI __attribute__((__visibility__("default"))) 10 | #endif 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /Sources/FirebaseAndroid/include/FirebaseAndroid.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: BSD-3-Clause */ 2 | 3 | #ifndef SwiftFirebase_FirebaseAndroidShim_FirebaseAndroid_h 4 | #define SwiftFirebase_FirebaseAndroidShim_FirebaseAndroid_h 5 | 6 | #include 7 | 8 | #if defined(__cplusplus) 9 | extern "C" { 10 | #endif 11 | 12 | jobject SwiftFirebase_GetActivity(void); 13 | JNIEnv *SwiftFirebase_GetJavaEnvironment(void); 14 | JavaVM *SwiftFirebase_GetJVM(void); 15 | 16 | #if defined(__cplusplus) 17 | } 18 | #endif 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /Sources/FirebaseAndroid/include/module.modulemap: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: BSD-3-Clause */ 2 | 3 | module FirebaseAndroid { 4 | header "FirebaseAndroid.h" 5 | export * 6 | } 7 | -------------------------------------------------------------------------------- /Sources/FirebaseAndroid/jni.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: BSD-3-Clause */ 2 | 3 | #include "FirebaseAndroid.h" 4 | #include "abi.h" 5 | #include "log.h" 6 | 7 | #include 8 | 9 | static JavaVM *g_VM; 10 | static JNIEnv *g_Env; 11 | static jobject *g_Activity; 12 | 13 | #define N_ELEMENTS(array) (sizeof((array)) / sizeof(*(array))) 14 | 15 | static const char kClassPath[] = "company/thebrowser/Native"; 16 | 17 | static jboolean 18 | SwiftFirebase_RegisterActivity(JNIEnv *env, jobject *this, jobject *activity) 19 | { 20 | assert(g_Activity == NULL && "re-registeration of activity"); 21 | if (g_Activity) return JNI_FALSE; 22 | 23 | g_Activity = activity; 24 | return JNI_TRUE; 25 | } 26 | 27 | static JNINativeMethod kMethods[] = { 28 | { "RegisterActivity", "()Z", SwiftFirebase_RegisterActivity }, 29 | }; 30 | 31 | static void 32 | RegisterNativeMethods(JNIEnv *env) 33 | { 34 | jclass class; 35 | jint result; 36 | 37 | class = (*env)->FindClass(env, kClassPath); 38 | if (class == NULL) { 39 | LOG_ERROR("unable to find class '%s'", kClassPath); 40 | return; 41 | } 42 | LOG_DEBUG("located class path '%s': %p", kClassPath, class); 43 | 44 | result = (*env)->RegisterNatives(env, class, kMethods, N_ELEMENTS(kMethods)); 45 | if (result < 0) { 46 | LOG_ERROR("JVM.RegisterNatives(%s): %u", kClassPath, result); 47 | return; 48 | } 49 | LOG_DEBUG("registered %lu methods", N_ELEMENTS(kMethods)); 50 | } 51 | 52 | FIREBASE_ANDROID_ABI 53 | jint JNI_OnLoad(JavaVM *vm, void *reserved) 54 | { 55 | g_VM = vm; 56 | if ((*g_VM)->GetEnv(g_VM, (void **)&g_Env, JNI_VERSION_1_6) != JNI_OK) 57 | return -1; 58 | RegisterNativeMethods(g_Env); 59 | return JNI_VERSION_1_6; 60 | } 61 | 62 | FIREBASE_ANDROID_ABI 63 | jobject SwiftFirebase_GetActivity(void) 64 | { 65 | assert(g_Activity && "`GetActivity` invoked before `RegisterActivity`"); 66 | return *g_Activity; 67 | } 68 | 69 | FIREBASE_ANDROID_ABI 70 | JNIEnv *SwiftFirebase_GetJavaEnvironment(void) 71 | { 72 | return g_Env; 73 | } 74 | 75 | FIREBASE_ANDROID_ABI 76 | JavaVM *SwiftFirebase_GetJVM(void) 77 | { 78 | return g_VM; 79 | } 80 | -------------------------------------------------------------------------------- /Sources/FirebaseAndroid/log.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: BSD-3-Clause */ 2 | 3 | #ifndef SwiftFirebase_FirebaseAndroidShim_logging_h 4 | #define SwiftFirebase_FirebaseAndroidShim_logging_h 5 | 6 | #include 7 | 8 | #define FIREBASE_ANDROID_LOG(level, tag, ...) __android_log_print(level, tag, __VA_ARGS__) 9 | #define FIREBASE_ANDROID_TAG "company.thebrowser.firebase" 10 | 11 | #define LOG_DEBUG(...) FIREBASE_ANDROID_LOG(ANDROID_LOG_DEBUG, FIREBASE_ANDROID_TAG, __VA_ARGS__) 12 | #define LOG_VERBOSE(...) FIREBASE_ANDROID_LOG(ANDROID_LOG_VERBOSE, FIREBASE_ANDROID_TAG, __VA_ARGS__) 13 | #define LOG_INFO(...) FIREBASE_ANDROID_LOG(ANDROID_LOG_INFO, FIREBASE_ANDROID_TAG, __VA_ARGS__) 14 | #define LOG_WARN(...) FIREBASE_ANDROID_LOG(ANDROID_LOG_WARN, FIREBASE_ANDROID_TAG, __VA_ARGS__) 15 | #define LOG_ERROR(...) FIREBASE_ANDROID_LOG(ANDROID_LOG_ERROR, FIREBASE_ANDROID_TAG, __VA_ARGS__) 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /Sources/FirebaseAuth/AuthStateDidChangeListenerHandle.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | import CxxShim 7 | 8 | public typealias AuthStateDidChangeListenerHandle = AnyObject 9 | 10 | internal class _AuthStateDidChangeListenerHandle { 11 | /// The boxed version of the callback that was passed in from the Swift caller that we will retain in this object. 12 | private let callback: Unmanaged 13 | 14 | /// An internal reference to the actual Firebase listener that we must hold onto. 15 | internal var listener: UnsafeMutablePointer 16 | 17 | internal init(_ body: @escaping (Auth, User?) -> Void) { 18 | self.callback = Unmanaged.passRetained(body as AnyObject) 19 | self.listener = swift_firebase.swift_cxx_shims.firebase.auth.AuthStateListener.Create({ auth, user, callback in 20 | guard let auth else { return } 21 | if let callback, let body = Unmanaged.fromOpaque(callback).takeUnretainedValue() as? ((Auth, User?) -> Void) { 22 | body(.init(auth), user.pointee.is_valid() ? .init(user.pointee) : nil) 23 | } 24 | }, UnsafeMutableRawPointer(self.callback.toOpaque())) 25 | } 26 | 27 | deinit { 28 | swift_firebase.swift_cxx_shims.firebase.auth.AuthStateListener.Destroy(UnsafeMutableRawPointer(self.listener)) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/FirebaseAuth/FIRActionCodeOperation.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | public enum ActionCodeOperation: Int { 4 | case unknown 5 | case passwordReset 6 | case verifyEmail 7 | case recoverEmail 8 | case emailLink 9 | case verifyAndChangeEmail 10 | case revertSecondFactorAddition 11 | } 12 | -------------------------------------------------------------------------------- /Sources/FirebaseAuth/FIRAuthTokenResult.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | import Foundation 7 | 8 | public struct AuthTokenResult { 9 | public let token: String 10 | 11 | public let expirationDate: Date 12 | 13 | public let authDate: Date 14 | 15 | public let issuedAtDate: Date 16 | 17 | public let signInProvider: String 18 | 19 | public let signInSecondFactor: String 20 | 21 | public let claims: [String:Any] 22 | 23 | internal init(_ token: String) throws { 24 | let components = token.components(separatedBy: ".") 25 | // The JWT should have three components 26 | guard components.count == 3 else { 27 | throw NSError(domain: "com.google.firebase.auth", 28 | code: AuthErrorCode.malformedJWT.rawValue, 29 | userInfo: [NSLocalizedDescriptionKey:"Failed to decode token"]) 30 | } 31 | 32 | var payload = components[1].replacing("_", with: "/") 33 | .replacing("-", with: "+") 34 | // Pad to 4 character alignment for base64 decoding 35 | if payload.count % 4 != 0 { 36 | payload.append(String(repeating: "=", count: 4 - payload.count % 4)) 37 | } 38 | 39 | guard let data = Data(base64Encoded: payload, 40 | options: .ignoreUnknownCharacters) else { 41 | throw NSError(domain: "com.google.firebase.auth", 42 | code: AuthErrorCode.malformedJWT.rawValue, 43 | userInfo: [NSLocalizedDescriptionKey:"Failed to decode token payload"]) 44 | } 45 | 46 | let options: JSONSerialization.ReadingOptions = 47 | [.mutableContainers, .allowFragments] 48 | guard let contents = 49 | try? JSONSerialization.jsonObject(with: data, options: options) 50 | as? [String:Any] else { 51 | throw NSError(domain: "com.google.firebase.auth", 52 | code: AuthErrorCode.malformedJWT.rawValue, 53 | userInfo: [NSLocalizedDescriptionKey:"Failed to deserialise token payload"]) 54 | } 55 | 56 | // These are dates since 00:00:00 January 1 1970, as described by the 57 | // Terminology section in the JWT spec. 58 | // https://tools.ietf.org/html/rfc7519 59 | guard let authDate = contents["auth_time"] as? TimeInterval, 60 | let expirationDate = contents["exp"] as? TimeInterval, 61 | let issueDate = contents["iat"] as? TimeInterval else { 62 | throw NSError(domain: "com.google.firebase.auth", 63 | code: AuthErrorCode.malformedJWT.rawValue, 64 | userInfo: [NSLocalizedDescriptionKey:"Missing fields in token payload"]) 65 | } 66 | 67 | self.token = token 68 | self.expirationDate = Date(timeIntervalSince1970: expirationDate) 69 | self.authDate = Date(timeIntervalSince1970: authDate) 70 | self.issuedAtDate = Date(timeIntervalSince1970: issueDate) 71 | self.signInProvider = contents["sign_in_provider"] as? String ?? "" 72 | self.signInSecondFactor = contents["sign_in_second_factor"] as? String ?? "" 73 | self.claims = contents 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Sources/FirebaseAuth/FirebaseAuth+Swift.swift: -------------------------------------------------------------------------------- 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 | @available(*, unavailable) 12 | public enum AuthAPNSTokenType: Int { 13 | case FIRAuthAPNSTokenTypeUnknown 14 | case FIRAuthAPNSTokenTypeSandbox 15 | case FIRAuthAPNSTokenTypeProd 16 | } 17 | 18 | public final class Auth { 19 | let impl: UnsafeMutablePointer 20 | 21 | init(_ impl: UnsafeMutablePointer) { 22 | self.impl = impl 23 | } 24 | 25 | public var app: FirebaseApp? { 26 | impl.pointee.__appUnsafe() 27 | } 28 | 29 | public var currentUser: User? { 30 | let user = impl.pointee.current_user() 31 | guard user.is_valid() else { return nil } 32 | return .init(user) 33 | } 34 | 35 | public var languageCode: String? { 36 | get { 37 | let code = String(impl.pointee.language_code()) 38 | guard !code.isEmpty else { return nil } 39 | return String(code) 40 | } 41 | set { impl.pointee.set_language_code(newValue) } 42 | } 43 | 44 | // @available(*, unavailable) 45 | // public var settings: AuthSettings? { get set } 46 | 47 | // @available(*, unavailable) 48 | // public var userAccessGroup: String? { get } 49 | 50 | // @available(*, unavailable) 51 | // public var shareAuthStateAcrossDevices: Bool { get set } 52 | 53 | // @available(*, unavailable) 54 | // public var tennantID: String? { get set } 55 | 56 | // @available(*, unavailable) 57 | // public var apnsToken: Data? { get set } 58 | 59 | public static func auth() -> Auth { 60 | // TODO(compnerd) convert this to an exception 61 | guard let application = firebase.App.GetInstance() else { 62 | fatalError("no default application") 63 | } 64 | return auth(app: application) 65 | } 66 | 67 | public static func auth(app: FirebaseApp) -> Auth { 68 | .init(firebase.auth.Auth.GetAuth(app, nil)) 69 | } 70 | 71 | public func updateCurrentUser(_ user: User, completion: ((Error?) -> Void)?) { 72 | fatalError("\(#function) not yet implemented") 73 | } 74 | 75 | public func updateCurrentUser(_ user: User) async throws { 76 | fatalError("\(#function) not yet implemented") 77 | } 78 | 79 | public func fetchSignInMethods(forEmail email: String, completion: (([String]?, Error?) -> Void)?) { 80 | fetchSignInMethodsImpl(forEmail: email) { providers, error in 81 | if let completion { 82 | DispatchQueue.main.async { 83 | completion(providers, error) 84 | } 85 | } 86 | } 87 | } 88 | 89 | public func fetchSignInMethods(forEmail email: String) async throws 90 | -> [String] { 91 | try await withCheckedThrowingContinuation { continuation in 92 | fetchSignInMethodsImpl(forEmail: email) { providers, error in 93 | if let error { 94 | continuation.resume(throwing: error) 95 | } else { 96 | continuation.resume(returning: providers ?? []) 97 | } 98 | } 99 | } 100 | } 101 | 102 | private func fetchSignInMethodsImpl(forEmail email: String, completion: @escaping ([String]?, Error?) -> Void) { 103 | let future = swift_firebase.swift_cxx_shims.firebase.auth.auth_fetch_providers_for_email(impl, email) 104 | future.setCompletion({ 105 | let (result, error) = future.resultAndError { AuthErrorCode($0) } 106 | var providers: [String]? 107 | if let result { 108 | providers = result.providers.map(String.init) 109 | } else { 110 | providers = nil 111 | } 112 | completion(providers, error) 113 | }) 114 | } 115 | 116 | public func signIn(withEmail email: String, password: String, completion: ((AuthDataResult?, Error?) -> Void)?) { 117 | signInImpl(withEmail: email, password: password) { data, error in 118 | if let completion { 119 | DispatchQueue.main.async { 120 | completion(data, error) 121 | } 122 | } 123 | } 124 | } 125 | 126 | public func signIn(withEmail email: String, password: String) async throws 127 | -> AuthDataResult { 128 | try await withCheckedThrowingContinuation { continuation in 129 | signInImpl(withEmail: email, password: password) { data, error in 130 | if let error { 131 | continuation.resume(throwing: error) 132 | } else { 133 | continuation.resume(returning: data ?? .init()) 134 | } 135 | } 136 | } 137 | } 138 | 139 | private func signInImpl(withEmail email: String, password: String, completion: @escaping (AuthDataResult?, Error?) -> Void) { 140 | let future = swift_firebase.swift_cxx_shims.firebase.auth.auth_sign_in_with_email_and_password(impl, email, password) 141 | future.setCompletion({ 142 | let (result, error) = future.resultAndError { AuthErrorCode($0) } 143 | var data: AuthDataResult? 144 | if let result { 145 | data = .init(result) 146 | } else { 147 | data = nil 148 | } 149 | completion(data, error) 150 | }) 151 | } 152 | 153 | public func signIn(withEmail email: String, link: String) async throws 154 | -> AuthDataResult { 155 | fatalError("\(#function) not yet implemented") 156 | } 157 | 158 | // signInWithProvider:UIDelegate:completion: 159 | 160 | // public func signIn(with credential: AuthCredential) async throws 161 | // -> AuthDataResult { 162 | // fatalError("\(#function) not yet implemented") 163 | // } 164 | 165 | public func signInAnonymously() async throws -> AuthDataResult { 166 | fatalError("\(#function) not yet implemented") 167 | } 168 | 169 | public func signIn(withCustomToken token: String) async throws 170 | -> AuthDataResult { 171 | fatalError("\(#function) not yet implemented") 172 | } 173 | 174 | public func createUser(withEmail email: String, password: String, completion: ((AuthDataResult?, Error?) -> Void)?) { 175 | createUserImpl(withEmail: email, password: password) { data, error in 176 | if let completion { 177 | DispatchQueue.main.async { 178 | completion(data, error) 179 | } 180 | } 181 | } 182 | } 183 | 184 | public func createUser(withEmail email: String, password: String) async throws 185 | -> AuthDataResult { 186 | try await withCheckedThrowingContinuation { continuation in 187 | createUserImpl(withEmail: email, password: password) { data, error in 188 | if let error { 189 | continuation.resume(throwing: error) 190 | } else { 191 | continuation.resume(returning: data ?? .init()) 192 | } 193 | } 194 | } 195 | } 196 | 197 | private func createUserImpl(withEmail email: String, password: String, completion: @escaping (AuthDataResult?, Error?) -> Void) { 198 | let future = swift_firebase.swift_cxx_shims.firebase.auth.auth_create_user_with_email_and_password(impl, email, password) 199 | future.setCompletion({ 200 | let (result, error) = future.resultAndError { AuthErrorCode($0) } 201 | var data: AuthDataResult? 202 | if let result { 203 | data = .init(result) 204 | } else { 205 | data = nil 206 | } 207 | completion(data, error) 208 | }) 209 | } 210 | 211 | public func confirmPasswordReset(withCode code: String, 212 | newPassword: String) async throws { 213 | fatalError("\(#function) not yet implemented") 214 | } 215 | 216 | // public func checkActionCode(_ code: String) async throws -> ActionCodeInfo { 217 | // fatalError("\(#function) not yet implemented") 218 | // } 219 | 220 | public func verifyPasswordResetCode(_ code: String) async throws -> String { 221 | fatalError("\(#function) not yet implemented") 222 | } 223 | 224 | public func applyActionCode(_ code: String) async throws { 225 | fatalError("\(#function) not yet implemented") 226 | } 227 | 228 | public func sendPasswordReset(withEmail email: String, completion: ((Error?) -> Void)?) { 229 | sendPasswordResetImpl(withEmail: email) { error in 230 | if let completion { 231 | DispatchQueue.main.async { 232 | completion(error) 233 | } 234 | } 235 | } 236 | } 237 | 238 | public func sendPasswordReset(withEmail email: String) async throws { 239 | try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in 240 | sendPasswordResetImpl(withEmail: email) { error in 241 | if let error { 242 | continuation.resume(throwing: error) 243 | } else { 244 | continuation.resume() 245 | } 246 | } 247 | } 248 | } 249 | 250 | private func sendPasswordResetImpl(withEmail email: String, completion: @escaping (Error?) -> Void) { 251 | let future = swift_firebase.swift_cxx_shims.firebase.auth.auth_send_password_reset_email(impl, email) 252 | future.setCompletion({ 253 | let (_, error) = future.resultAndError { AuthErrorCode($0) } 254 | completion(error) 255 | }) 256 | } 257 | 258 | // public func sendPasswordReset(withEmail email: String, 259 | // actionCodeSettings: ActionCodeSettings) async throws { 260 | // fatalError("\(#function) not yet implemented") 261 | // } 262 | 263 | // public func sendSignInLink(toEmail email: String, 264 | // actionCodeSettings: ActionCodeSettings) async throws { 265 | // fatalError("\(#function) not yet implemented") 266 | // } 267 | 268 | @discardableResult public func signOut() throws -> Bool { 269 | impl.pointee.SignOut() 270 | return true 271 | } 272 | 273 | public func isSignIn(withEmailLink link: String) -> Bool { 274 | fatalError("\(#function) not yet implemented") 275 | } 276 | 277 | public func addStateDidChangeListener(_ listener: @escaping (Auth, User?) -> Void) 278 | -> AuthStateDidChangeListenerHandle { 279 | let handle = _AuthStateDidChangeListenerHandle(listener) 280 | impl.pointee.AddAuthStateListener(handle.listener) 281 | return handle 282 | } 283 | 284 | public func removeStateDidChangeListener(_ listenerHandle: AuthStateDidChangeListenerHandle) { 285 | guard let handle = listenerHandle as? _AuthStateDidChangeListenerHandle else { return } 286 | impl.pointee.RemoveAuthStateListener(handle.listener) 287 | } 288 | 289 | // public func addIDTokenDidChangeListener(_ listener: @escaping (Auth, User?) -> Void) 290 | // -> IDTokenDidChangeListenerHandle { 291 | // fatalError("\(#function) not yet implemented") 292 | // } 293 | 294 | // public func removeIDTokenDidChangeListener(_ listenerHandle: IDTokenDidChangeListenerHandle) { 295 | // fatalError("\(#function) not yet implemented") 296 | // } 297 | 298 | public func useAppLangauge() { 299 | fatalError("\(#function) not yet implemented") 300 | } 301 | 302 | public func useEmulator(withHost host: String, port: Int) { 303 | fatalError("\(#function) not yet implemented") 304 | } 305 | 306 | public func canHandle(_ url: URL) -> Bool { 307 | fatalError("\(#function) not yet implemented") 308 | } 309 | 310 | @available(*, unavailable) 311 | public func setAPNSToken(_ token: Data, type: AuthAPNSTokenType) { 312 | } 313 | 314 | public func canHandleNotification(_ userInfo: [AnyHashable:Any]) -> Bool { 315 | fatalError("\(#function) not yet implemented") 316 | } 317 | 318 | public func revokeToken(withAuthorizationCode authorizationCode: String) async throws { 319 | fatalError("\(#function) not yet implemented") 320 | } 321 | 322 | public func useUserAccessGroup(_ accessGroup: String?) throws { 323 | fatalError("\(#function) not yet implemented") 324 | } 325 | 326 | public func getStoredUser(forAccessGroup accessGroup: String?) throws -> User? { 327 | fatalError("\(#function) not yet implemented") 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /Sources/FirebaseAuth/FirebaseAuthError.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | @_spi(FirebaseInternal) 6 | import FirebaseCore 7 | 8 | public struct AuthErrorCode: 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.auth.AuthError) { 18 | self.init(rawValue: Int(error.rawValue)) 19 | } 20 | } 21 | 22 | extension AuthErrorCode: RawRepresentable { 23 | public typealias RawValue = Int 24 | 25 | public init(rawValue: Int) { 26 | self.rawValue = rawValue 27 | localizedDescription = "\(rawValue)" 28 | } 29 | } 30 | 31 | extension AuthErrorCode { 32 | public static var none: Self { .init(firebase.auth.kAuthErrorNone) } 33 | public static var unimplemented: Self { .init(firebase.auth.kAuthErrorUnimplemented) } 34 | public static var invalidCustomToken: Self { .init(firebase.auth.kAuthErrorInvalidCustomToken) } 35 | public static var customTokenMismatch: Self { .init(firebase.auth.kAuthErrorCustomTokenMismatch) } 36 | public static var invalidCredential: Self { .init(firebase.auth.kAuthErrorInvalidCredential) } 37 | public static var userDisabled: Self { .init(firebase.auth.kAuthErrorUserDisabled) } 38 | public static var accountExistsWithDifferentCredential: Self { .init(firebase.auth.kAuthErrorAccountExistsWithDifferentCredentials) } 39 | public static var operationNotAllowed: Self { .init(firebase.auth.kAuthErrorOperationNotAllowed) } 40 | public static var emailAlreadyInUse: Self { .init(firebase.auth.kAuthErrorEmailAlreadyInUse) } 41 | public static var requiresRecentLogin: Self { .init(firebase.auth.kAuthErrorRequiresRecentLogin) } 42 | public static var credentialAlreadyInUse: Self { .init(firebase.auth.kAuthErrorCredentialAlreadyInUse) } 43 | public static var invalidEmail: Self { .init(firebase.auth.kAuthErrorInvalidEmail) } 44 | public static var wrongPassword: Self { .init(firebase.auth.kAuthErrorWrongPassword) } 45 | public static var tooManyRequests: Self { .init(firebase.auth.kAuthErrorTooManyRequests) } 46 | public static var userNotFound: Self { .init(firebase.auth.kAuthErrorUserNotFound) } 47 | public static var providerAlreadyLinked: Self { .init(firebase.auth.kAuthErrorProviderAlreadyLinked) } 48 | public static var noSuchProvider: Self { .init(firebase.auth.kAuthErrorNoSuchProvider) } 49 | public static var invalidUserToken: Self { .init(firebase.auth.kAuthErrorInvalidUserToken) } 50 | public static var userTokenExpired: Self { .init(firebase.auth.kAuthErrorUserTokenExpired) } 51 | public static var networkError: Self { .init(firebase.auth.kAuthErrorNetworkRequestFailed) } 52 | public static var invalidAPIKey: Self { .init(firebase.auth.kAuthErrorInvalidApiKey) } 53 | public static var appNotAuthorized: Self { .init(firebase.auth.kAuthErrorAppNotAuthorized) } 54 | public static var userMismatch: Self { .init(firebase.auth.kAuthErrorUserMismatch) } 55 | public static var weakPassword: Self { .init(firebase.auth.kAuthErrorWeakPassword) } 56 | public static var noSignedInUser: Self { .init(firebase.auth.kAuthErrorNoSignedInUser) } 57 | public static var apiNotAvailable: Self { .init(firebase.auth.kAuthErrorApiNotAvailable) } 58 | public static var expiredActionCode: Self { .init(firebase.auth.kAuthErrorExpiredActionCode) } 59 | public static var invalidActionCode: Self { .init(firebase.auth.kAuthErrorInvalidActionCode) } 60 | public static var invalidMessagePayload: Self { .init(firebase.auth.kAuthErrorInvalidMessagePayload) } 61 | public static var invalidPhoneNumber: Self { .init(firebase.auth.kAuthErrorInvalidPhoneNumber) } 62 | public static var missingPhoneNumber: Self { .init(firebase.auth.kAuthErrorMissingPhoneNumber) } 63 | public static var invalidRecipientEmail: Self { .init(firebase.auth.kAuthErrorInvalidRecipientEmail) } 64 | public static var invalidSender: Self { .init(firebase.auth.kAuthErrorInvalidSender) } 65 | public static var invalidVerificationCode: Self { .init(firebase.auth.kAuthErrorInvalidVerificationCode) } 66 | public static var invalidVerificationID: Self { .init(firebase.auth.kAuthErrorInvalidVerificationId) } 67 | public static var missingVerificationCode: Self { .init(firebase.auth.kAuthErrorMissingVerificationCode) } 68 | public static var missingVerificationID: Self { .init(firebase.auth.kAuthErrorMissingVerificationId) } 69 | public static var missingEmail: Self { .init(firebase.auth.kAuthErrorMissingEmail) } 70 | public static var missingPassword: Self { .init(firebase.auth.kAuthErrorMissingPassword) } 71 | public static var quotaExceeded: Self { .init(firebase.auth.kAuthErrorQuotaExceeded) } 72 | public static var retryPhoneAuth: Self { .init(firebase.auth.kAuthErrorRetryPhoneAuth) } 73 | public static var sessionExpired: Self { .init(firebase.auth.kAuthErrorSessionExpired) } 74 | public static var appNotVerified: Self { .init(firebase.auth.kAuthErrorAppNotVerified) } 75 | public static var appVerificationUserInteractionFailure: Self { .init(firebase.auth.kAuthErrorAppVerificationFailed) } 76 | public static var captchaCheckFailed: Self { .init(firebase.auth.kAuthErrorCaptchaCheckFailed) } 77 | public static var invalidAppCredential: Self { .init(firebase.auth.kAuthErrorInvalidAppCredential) } 78 | public static var missingAppCredential: Self { .init(firebase.auth.kAuthErrorMissingAppCredential) } 79 | public static var invalidClientID: Self { .init(firebase.auth.kAuthErrorInvalidClientId) } 80 | public static var invalidContinueURI: Self { .init(firebase.auth.kAuthErrorInvalidContinueUri) } 81 | public static var missingContinueURI: Self { .init(firebase.auth.kAuthErrorMissingContinueUri) } 82 | public static var keychainError: Self { .init(firebase.auth.kAuthErrorKeychainError) } 83 | public static var missingAppToken: Self { .init(firebase.auth.kAuthErrorMissingAppToken) } 84 | public static var missingIosBundleID: Self { .init(firebase.auth.kAuthErrorMissingIosBundleId) } 85 | public static var notificationNotForwarded: Self { .init(firebase.auth.kAuthErrorNotificationNotForwarded) } 86 | public static var unauthorizedDomain: Self { .init(firebase.auth.kAuthErrorUnauthorizedDomain) } 87 | public static var webContextAlreadyPresented: Self { .init(firebase.auth.kAuthErrorWebContextAlreadyPresented) } 88 | public static var webContextCancelled: Self { .init(firebase.auth.kAuthErrorWebContextCancelled) } 89 | public static var dynamicLinkNotActivated: Self { .init(firebase.auth.kAuthErrorDynamicLinkNotActivated) } 90 | public static var cancelled: Self { .init(firebase.auth.kAuthErrorCancelled) } 91 | public static var invalidProviderID: Self { .init(firebase.auth.kAuthErrorInvalidProviderId) } 92 | public static var webInternalError: Self { .init(firebase.auth.kAuthErrorWebInternalError) } 93 | // There's a typo in the Firebase error, carrying it over here. 94 | public static var webStorateUnsupported: Self { .init(firebase.auth.kAuthErrorWebStorateUnsupported) } 95 | public static var tenantIDMismatch: Self { .init(firebase.auth.kAuthErrorTenantIdMismatch) } 96 | public static var unsupportedTenantOperation: Self { .init(firebase.auth.kAuthErrorUnsupportedTenantOperation) } 97 | public static var invalidDynamicLinkDomain: Self { .init(firebase.auth.kAuthErrorInvalidLinkDomain) } 98 | public static var rejectedCredential: Self { .init(firebase.auth.kAuthErrorRejectedCredential) } 99 | public static var phoneNumberNotFound: Self { .init(firebase.auth.kAuthErrorPhoneNumberNotFound) } 100 | public static var invalidTenantID: Self { .init(firebase.auth.kAuthErrorInvalidTenantId) } 101 | public static var missingClientIdentifier: Self { .init(firebase.auth.kAuthErrorMissingClientIdentifier) } 102 | public static var missingMultiFactorSession: Self { .init(firebase.auth.kAuthErrorMissingMultiFactorSession) } 103 | public static var missingMultiFactorInfo: Self { .init(firebase.auth.kAuthErrorMissingMultiFactorInfo) } 104 | public static var invalidMultiFactorSession: Self { .init(firebase.auth.kAuthErrorInvalidMultiFactorSession) } 105 | public static var multiFactorInfoNotFound: Self { .init(firebase.auth.kAuthErrorMultiFactorInfoNotFound) } 106 | public static var adminRestrictedOperation: Self { .init(firebase.auth.kAuthErrorAdminRestrictedOperation) } 107 | public static var unverifiedEmail: Self { .init(firebase.auth.kAuthErrorUnverifiedEmail) } 108 | public static var secondFactorAlreadyEnrolled: Self { .init(firebase.auth.kAuthErrorSecondFactorAlreadyEnrolled) } 109 | public static var maximumSecondFactorCountExceeded: Self { .init(firebase.auth.kAuthErrorMaximumSecondFactorCountExceeded) } 110 | public static var unsupportedFirstFactor: Self { .init(firebase.auth.kAuthErrorUnsupportedFirstFactor) } 111 | public static var emailChangeNeedsVerification: Self { .init(firebase.auth.kAuthErrorEmailChangeNeedsVerification) } 112 | #if INTERNAL_EXPERIMENTAL 113 | public static var invalidEventHandler: Self { .init(firebase.auth.kAuthErrorInvalidEventHandler) } 114 | public static var federatedProviderAlreadyInUse: Self { .init(firebase.auth.kAuthErrorFederatedProviderAlreadyInUse) } 115 | public static var invalidAuthenticatedUserData: Self { .init(firebase.auth.kAuthErrorInvalidAuthenticatedUserData) } 116 | public static var federatedSignInUserInteractionFailure: Self { .init(firebase.auth.kAuthErrorFederatedSignInUserInteractionFailure) } 117 | public static var missingOrInvalidNonce: Self { .init(firebase.auth.kAuthErrorMissingOrInvalidNonce) } 118 | public static var userCancelled: Self { .init(firebase.auth.kAuthErrorUserCancelled) } 119 | public static var unsupportedPassthroughOperation: Self { .init(firebase.auth.kAuthErrorUnsupportedPassthroughOperation) } 120 | public static var tokenRefreshUnavailable: Self { .init(firebase.auth.kAuthErrorTokenRefreshUnavailable) } 121 | #endif 122 | 123 | // Errors that are not represented in the C++ SDK, but are 124 | // present in the reference API. 125 | public static var missingAndroidPackageName: Self { .init(rawValue: 17037) } 126 | public static var webNetworkRequestFailed: Self { .init(rawValue: 17061) } 127 | public static var webSignInUserInteractionFailure: Self { .init(rawValue: 17063) } 128 | public static var localPlayerNotAuthenticated: Self { .init(rawValue: 17066) } 129 | public static var nullUser: Self { .init(rawValue: 17067) } 130 | public static var gameKitNotLinked: Self { .init(rawValue: 17076) } 131 | public static var secondFactorRequired: Self { .init(rawValue: 17078) } 132 | public static var blockingCloudFunctionError: Self { .init(rawValue: 17105) } 133 | public static var internalError: Self { .init(rawValue: 17999) } 134 | public static var malformedJWT: Self { .init(rawValue: 18000) } 135 | } 136 | 137 | extension AuthErrorCode: Equatable {} 138 | 139 | extension AuthErrorCode { 140 | // The Obj C API provides this type as well, so provide it here for consistency. 141 | public typealias Code = AuthErrorCode 142 | 143 | // This allows us to re-expose self as a code similarly 144 | // to what the Firebase SDK does when it creates the 145 | // underlying NSErrors on iOS/macOS. 146 | public var code: Code { 147 | return self 148 | } 149 | 150 | public init(_ code: Code) { 151 | self.init(rawValue: code.rawValue) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Sources/FirebaseAuth/FirebaseAuthResult+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | //public typealias AuthDataResult = UnsafeMutablePointer 7 | 8 | public final class AuthDataResult { 9 | let impl: firebase.auth.AuthResult 10 | 11 | init(_ impl: firebase.auth.AuthResult = .init()) { 12 | self.impl = impl 13 | } 14 | 15 | public var user: User { 16 | .init(impl.user) 17 | } 18 | 19 | // public var additionalUserInfo: AdditionalUserInfo? { 20 | // fatalError("\(#function) not yet implemented") 21 | // } 22 | 23 | // public var credential: AuthCredential? { 24 | // fatalError("\(#function) not yet implemented") 25 | // } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/FirebaseAuth/FirebaseEmailAuthProvider.swift: -------------------------------------------------------------------------------- 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 typealias EmailAuthProvider = firebase.auth.EmailAuthProvider 12 | public typealias Credential = firebase.auth.Credential 13 | 14 | extension EmailAuthProvider { 15 | public static func credential(withEmail email: String, password: String) -> Credential { 16 | GetCredential(email, password) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sources/FirebaseAuth/FirebaseUser+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | import CxxStdlib 4 | import Foundation 5 | 6 | @_exported 7 | import firebase 8 | @_spi(FirebaseInternal) 9 | import FirebaseCore 10 | 11 | import CxxShim 12 | 13 | public typealias AuthResult = firebase.auth.AuthResult 14 | 15 | public protocol UserInfo { 16 | var providerID: String { get } 17 | var uid: String { get } 18 | var displayName: String? { get } 19 | var photoURL: URL? { get } 20 | var email: String? { get } 21 | var phoneNumber: String? { get } 22 | } 23 | 24 | // TODO(WPP-1581): Improve the API to match the ObjC one better. 25 | public final class User { 26 | let impl: firebase.auth.User 27 | 28 | init(_ impl: firebase.auth.User) { 29 | self.impl = impl 30 | } 31 | 32 | public var isAnonymous: Bool { 33 | impl.is_anonymous() 34 | } 35 | 36 | public var isEmailVerified: Bool { 37 | impl.is_email_verified() 38 | } 39 | 40 | public var refreshToken: String? { 41 | fatalError("\(#function) not yet implemented") 42 | } 43 | 44 | // public var providerData: [UserInfo] { 45 | // fatalError("\(#function) not yet implemented") 46 | // } 47 | 48 | // public var metadata: UserMetadata { 49 | // fatalError("\(#function) not yet implemented") 50 | // } 51 | 52 | public var tenantID: String? { 53 | fatalError("\(#function) not yet implemented") 54 | } 55 | 56 | // public var multiFactor: MultiFactor { 57 | // fatalError("\(#function) not yet implemented") 58 | // } 59 | 60 | public func updateEmail(to email: String) async throws { 61 | fatalError("\(#function) not yet implemented") 62 | } 63 | 64 | public func updatePassword(to password: String) async throws { 65 | fatalError("\(#function) not yet implemented") 66 | } 67 | 68 | // public func updatePhoneNumber(_ credential: PhoneAuthCredential) async throws { 69 | // fatalError("\(#function) not yet implemented") 70 | // } 71 | 72 | // public func createProfileChangeRequest() -> UserProfileChangeRequest { 73 | // fatalError("\(#function) not yet implemented") 74 | // } 75 | 76 | public func reload(completion: ((Error?) -> Void)?) { 77 | reloadImpl() { error in 78 | if let completion { 79 | DispatchQueue.main.async { 80 | completion(error) 81 | } 82 | } 83 | } 84 | } 85 | 86 | public func reload() async throws { 87 | try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in 88 | reloadImpl() { error in 89 | if let error { 90 | continuation.resume(throwing: error) 91 | } else { 92 | continuation.resume() 93 | } 94 | } 95 | } 96 | } 97 | 98 | private func reloadImpl(completion: @escaping (Error?) -> Void) { 99 | let future = swift_firebase.swift_cxx_shims.firebase.auth.user_reload(impl) 100 | future.setCompletion({ 101 | let (_, error) = future.resultAndError { AuthErrorCode($0) } 102 | completion(error) 103 | }) 104 | } 105 | 106 | public func reauthenticate(with credential: Credential, completion: ((AuthResult?, Error?) -> Void)?) { 107 | reauthenticateImpl(with: credential) { result, error in 108 | if let completion { 109 | DispatchQueue.main.async { 110 | completion(result, error) 111 | } 112 | } 113 | } 114 | } 115 | 116 | public func reauthenticate(with credential: Credential) async throws { 117 | try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in 118 | reauthenticateImpl(with: credential) { result, error in 119 | if let error { 120 | continuation.resume(throwing: error) 121 | } else { 122 | continuation.resume() 123 | } 124 | } 125 | } 126 | } 127 | 128 | public func reauthenticateImpl(with credential: Credential, completion: @escaping (AuthResult?, Error?) -> Void) { 129 | let future = swift_firebase.swift_cxx_shims.firebase.auth.user_reauthenticate_and_retrieve_data(impl, credential) 130 | future.setCompletion({ 131 | let (result, error) = future.resultAndError { AuthErrorCode($0) } 132 | completion(result, error) 133 | }) 134 | } 135 | 136 | // -reauthenticateWithProvider:UIDelegate:completion: 137 | 138 | public func getIDTokenResult() async throws -> AuthTokenResult { 139 | return try await getIDTokenResult(forcingRefresh: false) 140 | } 141 | 142 | public func getIDTokenResult(forcingRefresh forceRefresh: Bool) async throws 143 | -> AuthTokenResult { 144 | return try await AuthTokenResult(idTokenForcingRefresh(forceRefresh)) 145 | } 146 | 147 | public func getIDToken(completion: ((String?, Error?) -> Void)?) { 148 | idTokenForcingRefresh(false, completion: completion) 149 | } 150 | 151 | public func getIDToken() async throws -> String { 152 | return try await idTokenForcingRefresh(false) 153 | } 154 | 155 | public func idTokenForcingRefresh(_ forceRefresh: Bool, completion: ((String?, Error?) -> Void)?) { 156 | idTokenForcingRefreshImpl(forceRefresh) { result, error in 157 | if let completion { 158 | DispatchQueue.main.async { 159 | completion(result, error) 160 | } 161 | } 162 | } 163 | } 164 | 165 | public func idTokenForcingRefresh(_ forceRefresh: Bool) async throws 166 | -> String { 167 | try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in 168 | idTokenForcingRefreshImpl(forceRefresh) { result, error in 169 | if let error { 170 | continuation.resume(throwing: error) 171 | } else { 172 | continuation.resume(returning: result ?? .init()) 173 | } 174 | } 175 | } 176 | } 177 | 178 | private func idTokenForcingRefreshImpl(_ forceRefresh: Bool, completion: @escaping (String?, Error?) -> Void) { 179 | let future = swift_firebase.swift_cxx_shims.firebase.auth.user_get_token(impl, forceRefresh) 180 | future.setCompletion({ 181 | let (result, error) = future.resultAndError { AuthErrorCode($0) } 182 | let stringResult: String? 183 | if let result { 184 | stringResult = String(result) 185 | } else { 186 | stringResult = nil 187 | } 188 | completion(stringResult, error) 189 | }) 190 | } 191 | 192 | // public func link(with credential: AuthCredential) async throws 193 | // -> AuthDataResult { 194 | // fatalError("\(#function) not yet implemented") 195 | // } 196 | 197 | // -linkWithProvider:UIDelegate:completion 198 | 199 | public func unlink(from provider: String) async throws -> User { 200 | fatalError("\(#function) not yet implemented") 201 | } 202 | 203 | public func sendEmailVerification(completion: ((Error?) -> Void)?) { 204 | sendEmailVerificationImpl() { error in 205 | if let completion { 206 | DispatchQueue.main.async { 207 | completion(error) 208 | } 209 | } 210 | } 211 | } 212 | 213 | public func sendEmailVerification() async throws { 214 | try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in 215 | sendEmailVerificationImpl() { error in 216 | if let error { 217 | continuation.resume(throwing: error) 218 | } else { 219 | continuation.resume() 220 | } 221 | } 222 | } 223 | } 224 | 225 | public func sendEmailVerificationImpl(completion: @escaping (Error?) -> Void) { 226 | let future = swift_firebase.swift_cxx_shims.firebase.auth.user_send_email_verification(impl) 227 | future.setCompletion({ 228 | let (_, error) = future.resultAndError { AuthErrorCode($0) } 229 | completion(error) 230 | }) 231 | } 232 | 233 | // public func sendEmailVerification(with actionCodeSettings: ActionCodeSettings) async throws { 234 | // fatalError("\(#function) not yet implemented") 235 | // } 236 | 237 | public func delete(completion: ((Error?) -> Void)?) { 238 | deleteImpl() { error in 239 | if let completion { 240 | DispatchQueue.main.async { 241 | completion(error) 242 | } 243 | } 244 | } 245 | } 246 | 247 | public func delete() async throws { 248 | try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in 249 | deleteImpl() { error in 250 | if let error { 251 | continuation.resume(throwing: error) 252 | } else { 253 | continuation.resume() 254 | } 255 | } 256 | } 257 | } 258 | 259 | private func deleteImpl(completion: @escaping (Error?) -> Void) { 260 | let future = swift_firebase.swift_cxx_shims.firebase.auth.user_delete(impl) 261 | future.setCompletion({ 262 | let (_, error) = future.resultAndError { AuthErrorCode($0) } 263 | completion(error) 264 | }) 265 | } 266 | 267 | public func sendEmailVerification(beforeUpdatingEmail email: String) async throws { 268 | fatalError("\(#function) not yet implemented") 269 | } 270 | 271 | // public func sendEmailVerification(beforeUpdatingEmail email: String, 272 | // actionCodeSettings: ActionCodeSettings) async throws { 273 | // fatalError("\(#function) not yet implemented") 274 | // } 275 | } 276 | 277 | extension User: UserInfo { 278 | public var providerID: String { 279 | String(swift_firebase.swift_cxx_shims.firebase.auth.user_provider_id(impl)) 280 | } 281 | 282 | public var uid: String { 283 | String(swift_firebase.swift_cxx_shims.firebase.auth.user_uid(impl)) 284 | } 285 | 286 | public var displayName: String? { 287 | let name = String(swift_firebase.swift_cxx_shims.firebase.auth.user_display_name(impl)) 288 | return name.isEmpty ? nil : name 289 | } 290 | 291 | public var photoURL: URL? { 292 | let url = String(swift_firebase.swift_cxx_shims.firebase.auth.user_photo_url(impl)) 293 | return url.isEmpty ? nil : URL(string: url) 294 | } 295 | 296 | public var email: String? { 297 | let email = String(swift_firebase.swift_cxx_shims.firebase.auth.user_email(impl)) 298 | return email.isEmpty ? nil : email 299 | } 300 | 301 | public var phoneNumber: String? { 302 | let number = String(swift_firebase.swift_cxx_shims.firebase.auth.user_phone_number(impl)) 303 | return number.isEmpty ? nil : number 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /Sources/FirebaseCore/FirebaseApp+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | #if os(Android) 7 | private import FirebaseAndroid 8 | #endif 9 | 10 | public typealias FirebaseApp = UnsafeMutablePointer 11 | 12 | extension FirebaseApp { 13 | public static func configure() { 14 | #if os(Android) 15 | _ = firebase.App.Create(SwiftFirebase_GetJavaEnvironment(), 16 | SwiftFirebase_GetActivity()) 17 | #else 18 | _ = firebase.App.Create() 19 | #endif 20 | } 21 | 22 | public static func configure(options: FirebaseOptions) { 23 | #if os(Android) 24 | _ = firebase.App.Create(options.pointee, SwiftFirebase_GetJavaEnvironment(), 25 | SwiftFirebase_GetActivity()) 26 | #else 27 | _ = firebase.App.Create(options.pointee) 28 | #endif 29 | } 30 | 31 | public static func configure(name: String, options: FirebaseOptions) { 32 | #if os(Android) 33 | _ = firebase.App.Create(options.pointee, name, 34 | SwiftFirebase_GetJavaEnvironment(), 35 | SwiftFirebase_GetActivity()) 36 | #else 37 | _ = firebase.App.Create(options.pointee, name) 38 | #endif 39 | } 40 | 41 | public static func app() -> FirebaseApp? { 42 | firebase.App.GetInstance() 43 | } 44 | 45 | public static func app(name: String) -> FirebaseApp? { 46 | firebase.App.GetInstance(name) 47 | } 48 | 49 | public static var allApps: [String:FirebaseApp]? { 50 | let applications = firebase.App.GetApps() 51 | guard !applications.isEmpty else { return nil } 52 | return .init(uniqueKeysWithValues: applications.compactMap { 53 | guard let application = $0 else { return nil } 54 | return (application.name, application) 55 | }) 56 | } 57 | 58 | public func delete() async -> Bool { 59 | fatalError("\(#function) not yet implemented") 60 | } 61 | 62 | public var name: String { 63 | String(cString: self.pointee.__nameUnsafe()!) 64 | } 65 | 66 | public var options: UnsafePointer { 67 | // TODO(compnerd) ensure that the `FirebaseOptions` API is applied to this. 68 | self.pointee.__optionsUnsafe() 69 | } 70 | 71 | public var isDataCollectionDefaultEnabled: Bool { 72 | get { self.pointee.IsDataCollectionDefaultEnabled() } 73 | set { self.pointee.SetDataCollectionDefaultEnabled(newValue) } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Sources/FirebaseCore/FirebaseConfiguration.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | public class FirebaseConfiguration { 7 | public static let shared: FirebaseConfiguration = FirebaseConfiguration() 8 | 9 | public func setLoggerLevel(_ loggerLevel: FirebaseLoggerLevel) { 10 | firebase.SetLogLevel(loggerLevel) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Sources/FirebaseCore/FirebaseLogging+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | public typealias FirebaseLoggerLevel = firebase.LogLevel 7 | 8 | extension firebase.LogLevel { 9 | public static var verbose: Self { firebase.kLogLevelVerbose } 10 | public static var debug: Self { firebase.kLogLevelDebug } 11 | public static var info: Self { firebase.kLogLevelInfo } 12 | public static var warning: Self { firebase.kLogLevelWarning } 13 | public static var error: Self { firebase.kLogLevelError } 14 | public static var assert: Self { firebase.kLogLevelAssert } 15 | } 16 | 17 | extension firebase.LogLevel { 18 | public static var min: Self { .error } 19 | public static var max: Self { .debug } 20 | } 21 | 22 | extension firebase.LogLevel: CustomStringConvertible { 23 | public var description: String { 24 | switch self { 25 | case .verbose: return "Verbose" 26 | case .debug: return "Debug" 27 | case .info: return "Info" 28 | case .warning: return "Warning" 29 | case .error: return "Error" 30 | case .assert: return "Assert" 31 | default: fatalError("unknown value for firebase.LogLevel") 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/FirebaseCore/FirebaseOptions+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | #if os(Android) 7 | private import FirebaseAndroid 8 | #endif 9 | 10 | import Foundation 11 | 12 | public typealias FirebaseOptions = UnsafeMutablePointer 13 | 14 | extension firebase.AppOptions: CustomDebugStringConvertible { 15 | public var debugDescription: String { 16 | """ 17 | AppOptions { 18 | package_name_: "\(String(cString: self.__package_nameUnsafe()))" 19 | api_key_: "\(String(cString: self.__api_keyUnsafe()))" 20 | app_id_: "\(String(cString: self.__app_idUnsafe()))" 21 | client_id_: "\(String(cString: self.__client_idUnsafe()))" 22 | database_url_: "\(String(cString: self.__database_urlUnsafe()))" 23 | ga_tracking_id_: "\(String(cString: self.__ga_tracking_idUnsafe()))" 24 | fcm_sender_id_: "\(String(cString: self.__messaging_sender_idUnsafe()))" 25 | storage_bucket_: "\(String(cString: self.__storage_bucketUnsafe()))" 26 | project_id_: "\(String(cString: self.__project_idUnsafe()))" 27 | } 28 | """ 29 | } 30 | } 31 | 32 | extension FirebaseOptions { 33 | public static func defaultOptions() -> FirebaseOptions { 34 | #if os(Android) 35 | let options = 36 | firebase.AppOptions.LoadDefault(nil, 37 | SwiftFirebase_GetJavaEnvironment(), 38 | SwiftFirebase_GetActivity()) 39 | #else 40 | let options = firebase.AppOptions.LoadDefault(nil) 41 | #endif 42 | 43 | guard let options else { 44 | fatalError("unable to deserialise firebase options") 45 | } 46 | return options 47 | } 48 | 49 | public init?(contentsOfFile plistPath: String) { 50 | fatalError("\(#function) NYI") 51 | return nil 52 | } 53 | 54 | public init(googleAppID: String, gcmSenderID GCMSenderID: String) { 55 | self = .allocate(capacity: 1) 56 | self.initialize(to: firebase.AppOptions()) 57 | self.googleAppID = googleAppID 58 | self.gcmSenderID = GCMSenderID 59 | } 60 | 61 | public var apiKey: String? { 62 | get { 63 | guard let value = self.pointee.__api_keyUnsafe() else { return nil } 64 | return String(cString: value) 65 | } 66 | set { self.pointee.set_api_key(newValue) } 67 | } 68 | 69 | public var bundleID: String { 70 | get { 71 | guard let value = self.pointee.__package_nameUnsafe() else { 72 | return Bundle.main.bundleIdentifier! 73 | } 74 | return String(cString: value) 75 | } 76 | set { self.pointee.set_package_name(newValue) } 77 | } 78 | 79 | public var clientID: String? { 80 | get { 81 | guard let value = self.pointee.__client_idUnsafe() else { return nil } 82 | return String(cString: value) 83 | } 84 | set { self.pointee.set_client_id(newValue) } 85 | } 86 | 87 | public var trackingID: String? { 88 | get { 89 | guard let value = self.pointee.__ga_tracking_idUnsafe() else { 90 | return nil 91 | } 92 | return String(cString: value) 93 | } 94 | set { self.pointee.set_ga_tracking_id(newValue) } 95 | } 96 | 97 | public var gcmSenderID: String { 98 | get { String(cString: self.pointee.__messaging_sender_idUnsafe()!) } 99 | set { self.pointee.set_messaging_sender_id(newValue) } 100 | } 101 | 102 | public var projectID: String? { 103 | get { 104 | guard let value = self.pointee.__project_idUnsafe() else { return nil } 105 | return String(cString: value) 106 | } 107 | set { self.pointee.set_project_id(newValue) } 108 | } 109 | 110 | @available(*, unavailable) 111 | public var androidClientID: String? { nil } 112 | 113 | public var googleAppID: String? { 114 | get { 115 | guard let value = self.pointee.__app_idUnsafe() else { return nil } 116 | return String(cString: value) 117 | } 118 | set { self.pointee.set_app_id(newValue) } 119 | } 120 | 121 | public var databaseURL: String? { 122 | get { 123 | guard let value = self.pointee.__database_urlUnsafe() else { return nil } 124 | return String(cString: value) 125 | } 126 | set { self.pointee.set_database_url(newValue) } 127 | } 128 | 129 | @available(*, unavailable) 130 | public var deepLinkURLScheme: String? { nil } 131 | 132 | public var storageBucket: String? { 133 | get { 134 | guard let value = self.pointee.__storage_bucketUnsafe() else { 135 | return nil 136 | } 137 | return String(cString: value) 138 | } 139 | set { self.pointee.set_storage_bucket(newValue) } 140 | } 141 | 142 | @available(*, unavailable) 143 | public var appGroupId: String? { nil } 144 | } 145 | 146 | extension FirebaseOptions { 147 | /// The format of a specified config file 148 | public enum ConfigFormat { 149 | /// Specifies that the file contents should be treated as a json string. 150 | case json 151 | } 152 | 153 | /// An added initializer which will allow loading of a config from a specified file. 154 | /// - Parameters: 155 | /// - path: The path where the config file can be found. 156 | /// - format: A format which describes how the content of the file should be treated. 157 | public init?(_contentsOfFile path: URL, format: ConfigFormat) { 158 | guard let data = try? Data(contentsOf: path, options: .alwaysMapped) else { return nil } 159 | switch format { 160 | case .json: 161 | let config = String(data: data, encoding: .utf8) 162 | guard let options = firebase.AppOptions.LoadFromJsonConfig(config, nil) else { 163 | return nil 164 | } 165 | self = options 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /Sources/FirebaseCore/FutureProtocol.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | import Foundation 7 | 8 | @_spi(FirebaseInternal) 9 | public typealias FutureCompletionType = 10 | swift_firebase.swift_cxx_shims.firebase.FutureCompletionType 11 | 12 | // This protocol enables extracting common code for Future handling. Because 13 | // C++ interop is limited for templated class types, we need to invent a 14 | // protocol to reflect the features of a Future that should be generically 15 | // available. This works by having a C++ annotation (see swift_cxx_shims' 16 | // Future) that specifies this protocol conformance. 17 | @_spi(FirebaseInternal) 18 | public protocol FutureProtocol { 19 | associatedtype ResultType 20 | func error() -> Int32 21 | func __error_messageUnsafe() -> UnsafePointer? 22 | func __resultUnsafe() -> UnsafePointer? 23 | func OnCompletion( 24 | _ completion: FutureCompletionType, 25 | _ user_data: UnsafeMutableRawPointer? 26 | ) 27 | } 28 | 29 | @_spi(FirebaseInternal) 30 | public extension FutureProtocol { 31 | // Callsites retain their own reference to the Future, but they still need 32 | // a way to know when the Future completes. This provides that mechanism. 33 | // While the underlying Firebase `OnCompletion` method can provide a reference 34 | // back to the Future, we don't need to expose that here. 35 | func setCompletion(_ completion: @escaping () -> Void) { 36 | OnCompletion({ ptr in 37 | Unmanaged.fromOpaque(ptr!).takeRetainedValue().completion() 38 | }, Unmanaged.passRetained(CompletionReference(completion)).toOpaque()) 39 | } 40 | 41 | var result: ResultType? { 42 | __resultUnsafe()?.pointee 43 | } 44 | 45 | var errorMessage: String? { 46 | guard let errorMessageUnsafe = __error_messageUnsafe() else { return nil } 47 | return String(cString: errorMessageUnsafe) 48 | } 49 | 50 | func resultAndError(_ constructError: ((Int32, String)) -> ErrorType) -> (ResultType?, ErrorType?) { 51 | let error = self.error() 52 | guard error == 0 else { 53 | return (nil, constructError((code: error, message: errorMessage!))) 54 | } 55 | return (result, nil) 56 | } 57 | } 58 | 59 | // The Unmanaged type only works with classes, so we need a wrapper for the 60 | // completion callback. 61 | private class CompletionReference { 62 | let completion: () -> Void 63 | init(_ completion: @escaping () -> Void) { 64 | self.completion = completion 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/FirebaseCore/Variant+Swift.swift: -------------------------------------------------------------------------------- 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 { 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 | } 43 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/CollectionReference+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | import CxxShim 7 | import Foundation 8 | 9 | public typealias CollectionReference = firebase.firestore.CollectionReference 10 | 11 | extension CollectionReference { 12 | public func document(_ path: String) -> DocumentReference { 13 | swift_firebase.swift_cxx_shims.firebase.firestore.collection_document(self, std.string(path)) 14 | } 15 | 16 | public var path: String { 17 | String(swift_firebase.swift_cxx_shims.firebase.firestore.collection_path(self)) 18 | } 19 | } 20 | 21 | extension CollectionReference: QueryProtocol { 22 | public var _asQuery: Query { 23 | swift_firebase.swift_cxx_shims.firebase.firestore.collection_as_query(self) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/DocumentChange+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | import CxxShim 7 | 8 | public typealias DocumentChange = firebase.firestore.DocumentChange 9 | 10 | public enum DocumentChangeType: Int { 11 | case added 12 | case modified 13 | case removed 14 | } 15 | 16 | extension DocumentChange { 17 | public var type: DocumentChangeType { 18 | .fromType(swift_firebase.swift_cxx_shims.firebase.firestore.document_change_type(self)) 19 | } 20 | 21 | public var document: QueryDocumentSnapshot { 22 | .init(snapshot: swift_firebase.swift_cxx_shims.firebase.firestore.document_change_document(self)) 23 | } 24 | 25 | public var oldIndex: UInt { 26 | UInt(swift_firebase.swift_cxx_shims.firebase.firestore.document_change_old_index(self)) 27 | } 28 | 29 | public var newIndex: UInt { 30 | UInt(swift_firebase.swift_cxx_shims.firebase.firestore.document_change_new_index(self)) 31 | } 32 | } 33 | 34 | private extension DocumentChangeType { 35 | static func fromType(_ type: firebase.firestore.DocumentChange.`Type`) -> DocumentChangeType { 36 | .init(rawValue: Int(type.rawValue))! 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/DocumentReference+Swift.swift: -------------------------------------------------------------------------------- 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 typealias DocumentReference = firebase.firestore.DocumentReference 12 | 13 | extension DocumentReference { 14 | public var documentID: String { 15 | String(swift_firebase.swift_cxx_shims.firebase.firestore.document_id(self)) 16 | } 17 | 18 | public var firestore: Firestore { 19 | .init(swift_firebase.swift_cxx_shims.firebase.firestore.document_firestore(self)!) 20 | } 21 | 22 | public func collection(_ path: String) -> CollectionReference { 23 | swift_firebase.swift_cxx_shims.firebase.firestore.document_collection(self, std.string(path)) 24 | } 25 | 26 | public var path: String { 27 | String(swift_firebase.swift_cxx_shims.firebase.firestore.document_path(self)) 28 | } 29 | 30 | // This variant is provided for compatibility with the ObjC API. 31 | public func getDocument(source: FirestoreSource = .default, completion: @escaping (DocumentSnapshot?, Error?) -> Void) { 32 | let future = swift_firebase.swift_cxx_shims.firebase.firestore.document_get(self, source) 33 | future.setCompletion({ 34 | let (snapshot, error) = future.resultAndError { FirestoreErrorCode($0) } 35 | DispatchQueue.main.async { 36 | completion(snapshot, error) 37 | } 38 | }) 39 | } 40 | 41 | public func getDocument(source: FirestoreSource = .default) async throws -> DocumentSnapshot { 42 | try await withCheckedThrowingContinuation { continuation in 43 | let future = swift_firebase.swift_cxx_shims.firebase.firestore.document_get(self, source) 44 | future.setCompletion({ 45 | let (snapshot, error) = future.resultAndError { FirestoreErrorCode($0) } 46 | if let error { 47 | continuation.resume(throwing: error) 48 | } else { 49 | continuation.resume(returning: snapshot ?? .init()) 50 | } 51 | }) 52 | } 53 | } 54 | 55 | public func addSnapshotListener(_ listener: @escaping (DocumentSnapshot?, Error?) -> Void) -> ListenerRegistration { 56 | addSnapshotListener(includeMetadataChanges: false, listener: listener) 57 | } 58 | 59 | public func addSnapshotListener(includeMetadataChanges: Bool, listener: @escaping (DocumentSnapshot?, Error?) -> Void) -> ListenerRegistration { 60 | typealias ListenerCallback = (DocumentSnapshot?, Error?) -> Void 61 | let boxed = Unmanaged.passRetained(listener as AnyObject) 62 | let instance = swift_firebase.swift_cxx_shims.firebase.firestore.document_add_snapshot_listener( 63 | includeMetadataChanges, 64 | self, 65 | { snapshot, errorCode, errorMessage, pvListener in 66 | let callback = Unmanaged.fromOpaque(pvListener!).takeUnretainedValue() as! ListenerCallback 67 | 68 | let error = FirestoreErrorCode(errorCode, errorMessage: errorMessage) 69 | // We only return a snapshot if the error code isn't 0 (aka the 'ok' error code) 70 | let returned = error == nil ? snapshot?.pointee : nil 71 | 72 | // Make sure we dispatch our callback back into the main thread to keep consistent 73 | // with the reference API which will call back on the 'user_executor' which typically 74 | // ends up being the main queue. 75 | // Relevant code: 76 | // - https://github.com/firebase/firebase-ios-sdk/blob/main/Firestore/Source/API/FIRFirestore.mm#L210-L218 77 | // - https://github.com/firebase/firebase-ios-sdk/blob/main/Firestore/core/src/api/document_reference.cc#L236-L237 78 | DispatchQueue.main.async { 79 | callback(returned, error) 80 | } 81 | }, 82 | boxed.toOpaque() 83 | ) 84 | return ListenerRegistration(boxed, instance) 85 | } 86 | 87 | public func setData(_ data: [String: Any], merge: Bool = false, completion: ((Error?) -> Void)?) { 88 | setDataImpl(data, merge: merge) { error in 89 | if let completion { 90 | DispatchQueue.main.async { 91 | completion(error) 92 | } 93 | } 94 | } 95 | } 96 | 97 | private func setDataImpl(_ data: [String: Any], merge: Bool, completion: @escaping (Error?) -> Void) { 98 | let converted = FirestoreDataConverter.firestoreValue(document: data) 99 | let options = merge ? firebase.firestore.SetOptions.Merge() : firebase.firestore.SetOptions() 100 | let future = swift_firebase.swift_cxx_shims.firebase.firestore.document_set_data(self, converted, options) 101 | future.setCompletion({ 102 | let (_, error) = future.resultAndError { FirestoreErrorCode($0) } 103 | completion(error) 104 | }) 105 | } 106 | } 107 | 108 | extension DocumentReference { 109 | public func setData(_ data: [String: Any], merge: Bool = false) async throws { 110 | try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in 111 | setDataImpl(data, merge: merge) { error in 112 | if let error { 113 | continuation.resume(throwing: error) 114 | } else { 115 | continuation.resume() 116 | } 117 | } 118 | } 119 | } 120 | } 121 | 122 | extension DocumentReference: CustomDebugStringConvertible { 123 | public var debugDescription: String { 124 | String(self.ToString()) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/DocumentSnapshot+Swift.swift: -------------------------------------------------------------------------------- 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 | private typealias MapFieldValue = firebase.firestore.MapFieldValue 12 | 13 | // Expose these types as public to better match the types available from the Cocoa SDK. 14 | public typealias DocumentSnapshot = firebase.firestore.DocumentSnapshot 15 | public typealias ServerTimestampBehavior = firebase.firestore.DocumentSnapshot.ServerTimestampBehavior 16 | public typealias GeoPoint = firebase.firestore.GeoPoint 17 | 18 | extension DocumentSnapshot { 19 | public var reference: DocumentReference { 20 | swift_firebase.swift_cxx_shims.firebase.firestore.document_snapshot_reference(self) 21 | } 22 | 23 | public var exists: Bool { 24 | swift_firebase.swift_cxx_shims.firebase.firestore.document_snapshot_exists(self) 25 | } 26 | 27 | public var documentID: String { 28 | String(swift_firebase.swift_cxx_shims.firebase.firestore.document_snapshot_id(self).pointee) 29 | } 30 | 31 | public func data(with behavior: ServerTimestampBehavior = .default) -> [String: Any]? { 32 | guard exists else { return nil } 33 | let data = swift_firebase.swift_cxx_shims.firebase.firestore.document_snapshot_get_data_workaround(self, behavior) 34 | return FirestoreDataConverter.value(workaround: data) 35 | } 36 | } 37 | 38 | extension DocumentSnapshot { 39 | public func data(as type: T.Type, 40 | with serverTimestampBehavior: ServerTimestampBehavior = .none, 41 | decoder: Firestore.Decoder = .init()) throws -> T { 42 | let value: Any = data(with: serverTimestampBehavior) ?? NSNull() 43 | let result = try decoder.decode(T.self, from: value, in: reference) 44 | return result 45 | } 46 | } 47 | 48 | extension DocumentSnapshot: CustomDebugStringConvertible { 49 | public var debugDescription: String { 50 | String(self.ToString()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/FieldValue+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | public typealias FieldValue = firebase.firestore.FieldValue 7 | 8 | extension FieldValue { 9 | public static func serverTimestamp() -> Self { 10 | ServerTimestamp() 11 | } 12 | } 13 | 14 | extension FieldValue: Equatable {} 15 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/Firestore+Swift.swift: -------------------------------------------------------------------------------- 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 | // On Apple platforms, this is defined by Foundation using AutoreleasingUnsafeMutablePointer. 12 | // That type is specific to the ObjC runtime, so we don't have access to it. Use this instead. 13 | public typealias NSErrorPointer = UnsafeMutablePointer? 14 | 15 | public class Firestore { 16 | var impl: UnsafeMutablePointer 17 | 18 | init(_ impl: UnsafeMutablePointer) { 19 | self.impl = impl 20 | } 21 | 22 | public static func firestore() -> Firestore { 23 | guard let application = firebase.App.GetInstance() else { 24 | fatalError("no default application") 25 | } 26 | 27 | return firestore(app: application) 28 | } 29 | 30 | public static func firestore(app: FirebaseApp) -> Firestore { 31 | guard let instance = firebase.firestore.Firestore.GetInstance(app, nil) else { 32 | fatalError("Invalid Firestore Instance") 33 | } 34 | 35 | return .init(instance) 36 | } 37 | 38 | public var settings: FirestoreSettings { 39 | get { 40 | .init(swift_firebase.swift_cxx_shims.firebase.firestore.firestore_settings(impl)) 41 | } 42 | set { 43 | swift_firebase.swift_cxx_shims.firebase.firestore.firestore_set_settings(impl, newValue.impl) 44 | } 45 | } 46 | 47 | public func clearPersistence(completion: ((Error?) -> Void)?) { 48 | let future = swift_firebase.swift_cxx_shims.firebase.firestore.firestore_clear_persistence(impl) 49 | future.setCompletion({ 50 | if let completion { 51 | DispatchQueue.main.async { 52 | completion(nil) 53 | } 54 | } 55 | }) 56 | } 57 | 58 | public func clearPersistence() async throws { 59 | await withCheckedContinuation { continuation in 60 | let future = swift_firebase.swift_cxx_shims.firebase.firestore.firestore_clear_persistence(impl) 61 | future.setCompletion({ 62 | continuation.resume() 63 | }) 64 | } 65 | } 66 | 67 | public func terminate(completion: ((Error?) -> Void)?) { 68 | let future = swift_firebase.swift_cxx_shims.firebase.firestore.firestore_terminate(impl) 69 | future.setCompletion({ 70 | if let completion { 71 | DispatchQueue.main.async { 72 | completion(nil) 73 | } 74 | } 75 | }) 76 | } 77 | 78 | public func terminate() async throws { 79 | await withCheckedContinuation { continuation in 80 | let future = swift_firebase.swift_cxx_shims.firebase.firestore.firestore_terminate(impl) 81 | future.setCompletion({ 82 | continuation.resume() 83 | }) 84 | } 85 | } 86 | 87 | public func document(_ documentPath: String) -> DocumentReference { 88 | swift_firebase.swift_cxx_shims.firebase.firestore.firestore_document(impl, std.string(documentPath)) 89 | } 90 | 91 | public func collection(_ collectionPath: String) -> CollectionReference { 92 | swift_firebase.swift_cxx_shims.firebase.firestore.firestore_collection(impl, std.string(collectionPath)) 93 | } 94 | 95 | public func runTransaction(_ updateBlock: @escaping (Transaction, NSErrorPointer) -> Any, completion: @escaping (Any?, Error?) -> Void) { 96 | runTransaction(with: nil, block: updateBlock, completion: completion) 97 | } 98 | 99 | public func runTransaction(_ updateBlock: @escaping (Transaction, NSErrorPointer) -> Any?, completion: @escaping (Any?, Error?) -> Void) { 100 | runTransaction(with: nil, block: updateBlock, completion: completion) 101 | } 102 | 103 | public func runTransaction( 104 | with options: TransactionOptions?, 105 | block updateBlock: @escaping (Transaction, NSErrorPointer) -> Any?, 106 | completion: @escaping (Any?, Error?) -> Void 107 | ) { 108 | let context = TransactionContext(updateBlock: updateBlock) 109 | let boxed = Unmanaged.passRetained(context as AnyObject) 110 | let future = swift_firebase.swift_cxx_shims.firebase.firestore.firestore_run_transaction( 111 | impl, options ?? .init(), { transaction, pErrorMessage, pvUpdateBlock in 112 | let context = Unmanaged.fromOpaque(pvUpdateBlock!).takeUnretainedValue() as! TransactionContext 113 | 114 | // Instead of trying to relay the generated `NSError` through firebase's `Error` type 115 | // and error message string, just store the `NSError` on `context` and access it later. 116 | // We then only need to tell firebase if the update block succeeded or failed and can 117 | // just not bother setting `pErrorMessage`. 118 | 119 | // It is expected to run `updateBlock` on whatever thread this happens to be. This is 120 | // consistent with the behavior of the ObjC API as well. 121 | 122 | // Since we could run `updateBlock` multiple times, we need to take care to reset any 123 | // residue from previous runs. That means clearing out this error field. 124 | context.error = nil 125 | 126 | context.result = context.updateBlock(transaction!.pointee, &context.error) 127 | 128 | return context.error == nil ? firebase.firestore.kErrorNone : firebase.firestore.kErrorCancelled 129 | }, 130 | boxed.toOpaque() 131 | ) 132 | future.setCompletion({ 133 | completion(context.result, context.error) 134 | boxed.release() 135 | }) 136 | } 137 | 138 | public func batch() -> WriteBatch { 139 | swift_firebase.swift_cxx_shims.firebase.firestore.firestore_batch(impl) 140 | } 141 | 142 | private class TransactionContext { 143 | typealias UpdateBlock = (Transaction, NSErrorPointer) -> Any? 144 | 145 | let updateBlock: UpdateBlock 146 | var result: Any? 147 | var error: Error? 148 | 149 | init(updateBlock: @escaping UpdateBlock) { 150 | self.updateBlock = updateBlock 151 | } 152 | } 153 | } 154 | 155 | // An extension that adds the encoder and decoder functions required 156 | // to serialize and deserialize documents in Firebase. These are mostly 157 | // copy of the Swift implementation that's available within firebase-ios-sdk 158 | // 159 | // The `FirebaseDataDecoder` is a heavily forked version of the Swift JSONEncoder/Decoder 160 | // this extension creates classes that can be configured in a similar fashion to the standard 161 | // encoder/decoders but with Firebase specific options like for data encoding, extended 162 | // date format options, etc. 163 | // 164 | // We are re-exposing these to maximize compatibility with existing Firestore APIs that might 165 | // depend on these being the same access level and shape. 166 | extension Firestore { 167 | public class Encoder { 168 | public var dateEncodingStrategy: FirebaseDataEncoder.DateEncodingStrategy = .timestamp 169 | public var dataEncodingStrategy: FirebaseDataEncoder.DataEncodingStrategy = .blob 170 | public var nonConformingFloatEncodingStrategy: FirebaseDataEncoder.NonConformingFloatEncodingStrategy = .throw 171 | public var keyEncodingStrategy: FirebaseDataEncoder.KeyEncodingStrategy = .useDefaultKeys 172 | public var userInfo: [CodingUserInfoKey: Any] = [:] 173 | 174 | public func encode(_ value: T) throws -> [String: Any] { 175 | let encoder = FirebaseDataEncoder() 176 | // Configure the encoder to the set values 177 | encoder.dateEncodingStrategy = dateEncodingStrategy 178 | encoder.dataEncodingStrategy = dataEncodingStrategy 179 | encoder.nonConformingFloatEncodingStrategy = nonConformingFloatEncodingStrategy 180 | encoder.keyEncodingStrategy = keyEncodingStrategy 181 | encoder.passthroughTypeResolver = FirestorePassthroughTypes.self 182 | encoder.userInfo = userInfo 183 | 184 | // Decode the document correctly, or throw an error describing 185 | // what sort of bad thing has happened. 186 | let encoded = try encoder.encode(value) 187 | guard let dictionaryValue = encoded as? [String: Any] else { 188 | throw EncodingError 189 | .invalidValue(value, 190 | EncodingError.Context(codingPath: [], 191 | debugDescription: "Top-level \(T.self) is not allowed.")) 192 | } 193 | return dictionaryValue 194 | } 195 | 196 | public init() {} 197 | } 198 | 199 | public class Decoder { 200 | public var dateDecodingStrategy: FirebaseDataDecoder.DateDecodingStrategy = .timestamp 201 | public var dataDecodingStrategy: FirebaseDataDecoder.DataDecodingStrategy = .blob 202 | public var nonConformingFloatDecodingStrategy: FirebaseDataDecoder.NonConformingFloatDecodingStrategy = .throw 203 | public var keyDecodingStrategy: FirebaseDataDecoder.KeyDecodingStrategy = .useDefaultKeys 204 | public var userInfo: [CodingUserInfoKey: Any] = [:] 205 | 206 | public func decode(_ t: T.Type, from data: Any) throws -> T { 207 | let decoder = FirebaseDataDecoder() 208 | decoder.dateDecodingStrategy = dateDecodingStrategy 209 | decoder.dataDecodingStrategy = dataDecodingStrategy 210 | decoder.nonConformingFloatDecodingStrategy = nonConformingFloatDecodingStrategy 211 | decoder.keyDecodingStrategy = keyDecodingStrategy 212 | decoder.passthroughTypeResolver = FirestorePassthroughTypes.self 213 | decoder.userInfo = userInfo 214 | return try decoder.decode(t, from: data) 215 | } 216 | 217 | public func decode(_ t: T.Type, from data: Any, in reference: DocumentReference?) throws -> T { 218 | if let reference = reference { 219 | userInfo[CodingUserInfoKey.documentRefUserInfoKey] = reference 220 | } 221 | return try decode(T.self, from: data) 222 | } 223 | 224 | public init() {} 225 | } 226 | } 227 | 228 | extension FirebaseDataEncoder.DateEncodingStrategy { 229 | public static var timestamp: FirebaseDataEncoder.DateEncodingStrategy { 230 | return .custom { date, encoder in 231 | var container = encoder.singleValueContainer() 232 | try container.encode(Timestamp(date: date)) 233 | } 234 | } 235 | } 236 | 237 | extension FirebaseDataDecoder.DateDecodingStrategy { 238 | public static var timestamp: FirebaseDataDecoder.DateDecodingStrategy { 239 | return .custom { decoder in 240 | let container = try decoder.singleValueContainer() 241 | let value = try container.decode(Timestamp.self) 242 | return value.dateValue() 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/FirestoreDataConverter.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | import Foundation 3 | 4 | import Cxx 5 | 6 | internal struct FirestoreDataConverter { 7 | static func value(workaround: swift_firebase.swift_cxx_shims.firebase.firestore.MapFieldValue_Workaround) -> [String: Any] { 8 | guard workaround.keys.size() == workaround.values.size() else { fatalError("Internal error: keys and values should be the same size.") } 9 | 10 | return Dictionary(uniqueKeysWithValues: zip(workaround.keys, workaround.values).lazy.compactMap { 11 | guard let converted = FirestoreDataConverter.value(field: $0.1) else { return nil } 12 | return (key: String($0.0), value: converted) 13 | }) 14 | } 15 | 16 | static func value(field: firebase.firestore.FieldValue) -> Any? { 17 | switch field.type() { 18 | case .null: 19 | return NSNull() 20 | case .boolean: 21 | return field.boolean_value() 22 | case .integer: 23 | return Int64(field.integer_value()) 24 | case .double: 25 | return field.double_value() 26 | case .timestamp: 27 | return field.timestamp_value() 28 | case .string: 29 | return String(field.string_value()) 30 | case .blob: 31 | guard let value = swift_firebase.swift_cxx_shims.firebase.firestore.field_get_blob(field) else { return nil } 32 | return Data(bytes: value, count: field.blob_size()) 33 | case .reference: 34 | return field.reference_value() 35 | case .geoPoint: 36 | return field.geo_point_value() 37 | case .array: 38 | return field.array_value().lazy.compactMap(FirestoreDataConverter.value(field:)) 39 | case .map: 40 | let value = swift_firebase.swift_cxx_shims.firebase.firestore.field_value_workaround(field.map_value()) 41 | assert(value.keys.size() == value.values.size()) 42 | return Dictionary(uniqueKeysWithValues: zip(value.keys, value.values).lazy) 43 | default: 44 | return nil 45 | } 46 | } 47 | 48 | /// Maps a Swift version of a Firestore document into a `MapFieldValue` as expected by the 49 | /// Firestore set and update functions. 50 | /// 51 | /// - Parameter document: The dictionary that describes the document of data you'd like to create. 52 | /// - Returns: a `MapFieldValue` with converted types that Firestore will understand. 53 | internal static func firestoreValue(document: [String: Any]) -> firebase.firestore.MapFieldValue { 54 | var map = firebase.firestore.MapFieldValue() 55 | for item in document { 56 | guard let value = firestoreValue(field: item.value) else { continue } 57 | map[std.string(item.key)] = value 58 | } 59 | return map 60 | } 61 | 62 | /// Converts Swift values to their corresponding Firestore `FieldValue`s. 63 | /// - Parameter field: A Swift value you'd like to convert into a `FieldValue` 64 | /// - Returns: The `FieldValue` or `nil` that is the Firestore analog of the passed in value. 65 | internal static func firestoreValue(field: Any) -> firebase.firestore.FieldValue? { 66 | switch field.self { 67 | case is NSNull: 68 | return firebase.firestore.FieldValue.Null() 69 | case is Bool: 70 | guard let bool = field as? Bool else { return nil } 71 | return firebase.firestore.FieldValue.Boolean(bool) 72 | case is Int: 73 | guard let int = field as? Int else { return nil } 74 | return firebase.firestore.FieldValue.Integer(Int64(int)) 75 | case is Double: 76 | guard let double = field as? Double else { return nil } 77 | return firebase.firestore.FieldValue.Double(double) 78 | case is Date: 79 | guard let date = field as? Date else { return nil } 80 | return firebase.firestore.FieldValue.Timestamp(date.firestoreTimestamp()) 81 | case is String: 82 | guard let string = field as? String else { return nil } 83 | return firebase.firestore.FieldValue.String(std.string(string)) 84 | case is Data: 85 | guard let data = field as? Data else { return nil } 86 | let size = data.count 87 | return firebase.firestore.FieldValue.Blob([UInt8](data), size) 88 | case is GeoPoint: 89 | guard let geopoint = field as? GeoPoint else { return nil } 90 | return firebase.firestore.FieldValue.GeoPoint(geopoint) 91 | case is Array: 92 | guard let array = field as? Array else { return nil } 93 | var vector = swift_firebase.swift_cxx_shims.firebase.firestore.FirestoreFieldValues() 94 | 95 | for element in array { 96 | guard let value = firestoreValue(field: element) else { continue } 97 | vector.push_back(value) 98 | } 99 | 100 | return firebase.firestore.FieldValue.Array(vector) 101 | case is Dictionary: 102 | guard let dictionary = field as? [String: Any] else { return nil } 103 | 104 | var map = firebase.firestore.MapFieldValue() 105 | for item in dictionary { 106 | guard let value = firestoreValue(field: item.value) else { continue } 107 | map[std.string(item.key)] = value 108 | } 109 | 110 | return firebase.firestore.FieldValue.Map(map) 111 | case let field as firebase.firestore.FieldValue: 112 | return field 113 | default: 114 | return nil 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/FirestoreErrorCode.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | @_spi(FirebaseInternal) 6 | import FirebaseCore 7 | 8 | public struct FirestoreErrorCode: 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.firestore.Error) { 18 | self.init(rawValue: Int(error.rawValue)) 19 | } 20 | } 21 | 22 | extension FirestoreErrorCode: RawRepresentable { 23 | public typealias RawValue = Int 24 | 25 | public init(rawValue: Int) { 26 | self.rawValue = rawValue 27 | localizedDescription = "\(rawValue)" 28 | } 29 | } 30 | 31 | extension FirestoreErrorCode { 32 | init(_ error: firebase.firestore.Error, errorMessage: String?) { 33 | self.init((code: numericCast(error.rawValue), message: errorMessage ?? "\(error.rawValue)")) 34 | } 35 | 36 | init?(_ error: firebase.firestore.Error?, errorMessage: UnsafePointer?) { 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 FirestoreErrorCode { 47 | public static var ok: Self { .init(firebase.firestore.kErrorOk) } 48 | public static var none: Self { .init(firebase.firestore.kErrorNone) } 49 | public static var cancelled: Self { .init(firebase.firestore.kErrorCancelled) } 50 | public static var unknown: Self { .init(firebase.firestore.kErrorUnknown) } 51 | public static var invalidArgument: Self { .init(firebase.firestore.kErrorInvalidArgument) } 52 | public static var deadlineExceeded: Self { .init(firebase.firestore.kErrorDeadlineExceeded) } 53 | public static var notFound: Self { .init(firebase.firestore.kErrorNotFound) } 54 | public static var alreadyExists: Self { .init(firebase.firestore.kErrorAlreadyExists) } 55 | public static var permissionDenied: Self { .init(firebase.firestore.kErrorPermissionDenied) } 56 | public static var resourceExhausted: Self { .init(firebase.firestore.kErrorResourceExhausted) } 57 | public static var failedPrecondition: Self { .init(firebase.firestore.kErrorFailedPrecondition) } 58 | public static var aborted: Self { .init(firebase.firestore.kErrorAborted) } 59 | public static var outOfRange: Self { .init(firebase.firestore.kErrorOutOfRange) } 60 | public static var unimplemented: Self { .init(firebase.firestore.kErrorUnimplemented) } 61 | public static var `internal`: Self { .init(firebase.firestore.kErrorInternal) } 62 | public static var unavailable: Self { .init(firebase.firestore.kErrorUnavailable) } 63 | public static var dataLoss: Self { .init(firebase.firestore.kErrorDataLoss) } 64 | public static var unauthenticated: Self { .init(firebase.firestore.kErrorUnauthenticated) } 65 | } 66 | 67 | extension FirestoreErrorCode: Equatable {} 68 | 69 | extension FirestoreErrorCode { 70 | // The Obj C API provides this type as well, so provide it here for consistency. 71 | public typealias Code = FirestoreErrorCode 72 | 73 | // This allows us to re-expose self as a code similarly 74 | // to what the Firebase SDK does when it creates the 75 | // underlying NSErrors on iOS/macOS. 76 | public var code: Code { 77 | return self 78 | } 79 | 80 | public init(_ code: Code) { 81 | self.init(rawValue: code.rawValue) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/FirestoreSource+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | public typealias FirestoreSource = firebase.firestore.Source 7 | 8 | extension FirestoreSource: @unchecked Sendable {} 9 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/ListenerRegistration.swift: -------------------------------------------------------------------------------- 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 struct ListenerRegistration { 12 | private let listener: Unmanaged 13 | private let registration: firebase.firestore.ListenerRegistration 14 | 15 | init(_ listener: Unmanaged, _ registration: firebase.firestore.ListenerRegistration) { 16 | self.listener = listener 17 | self.registration = registration 18 | } 19 | 20 | public func remove() { 21 | swift_firebase.swift_cxx_shims.firebase.firestore.listener_registration_remove(registration) 22 | _ = listener.takeRetainedValue() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/Query+Swift.swift: -------------------------------------------------------------------------------- 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 typealias Query = firebase.firestore.Query 12 | 13 | // Any types that extend from Query can conform to QueryProtocol to provide the 14 | // functionality of Query. This is needed since structs in Swift do not support 15 | // inheritance and C++ classes are exposed as structs to Swift. 16 | public protocol QueryProtocol { 17 | // This is an internal means to expose `Query` when the conforming type is 18 | // intended to be a subclass of `Query`. 19 | var _asQuery: Query { get } 20 | } 21 | 22 | extension Query: QueryProtocol { 23 | public var _asQuery: Query { self } 24 | } 25 | 26 | extension QueryProtocol { 27 | public var firestore: Firestore { 28 | .init(swift_firebase.swift_cxx_shims.firebase.firestore.query_firestore(_asQuery)) 29 | } 30 | 31 | // This variant is provided for compatibility with the ObjC API. 32 | public func getDocuments(source: FirestoreSource = .default, completion: @escaping (QuerySnapshot?, Error?) -> Void) { 33 | let future = swift_firebase.swift_cxx_shims.firebase.firestore.query_get(_asQuery, source) 34 | future.setCompletion({ 35 | let (snapshot, error) = future.resultAndError { FirestoreErrorCode($0) } 36 | DispatchQueue.main.async { 37 | completion(snapshot, error) 38 | } 39 | }) 40 | } 41 | 42 | public func getDocuments(source: FirestoreSource = .default) async throws -> QuerySnapshot { 43 | try await withCheckedThrowingContinuation { continuation in 44 | let future = swift_firebase.swift_cxx_shims.firebase.firestore.query_get(_asQuery, source) 45 | future.setCompletion({ 46 | let (snapshot, error) = future.resultAndError { FirestoreErrorCode($0) } 47 | if let error { 48 | continuation.resume(throwing: error) 49 | } else { 50 | continuation.resume(returning: snapshot ?? .init()) 51 | } 52 | }) 53 | } 54 | } 55 | 56 | public func addSnapshotListener(_ listener: @escaping (QuerySnapshot?, Error?) -> Void) -> ListenerRegistration { 57 | addSnapshotListener(includeMetadataChanges: false, listener: listener) 58 | } 59 | 60 | public func addSnapshotListener(includeMetadataChanges: Bool, listener: @escaping (QuerySnapshot?, Error?) -> Void) -> ListenerRegistration { 61 | typealias ListenerCallback = (QuerySnapshot?, Error?) -> Void 62 | let boxed = Unmanaged.passRetained(listener as AnyObject) 63 | let instance = swift_firebase.swift_cxx_shims.firebase.firestore.query_add_snapshot_listener( 64 | includeMetadataChanges, 65 | _asQuery, 66 | { snapshot, errorCode, errorMessage, pvListener in 67 | let callback = Unmanaged.fromOpaque(pvListener!).takeUnretainedValue() as! ListenerCallback 68 | 69 | let error = FirestoreErrorCode(errorCode, errorMessage: errorMessage) 70 | // We only return a snapshot if the error code isn't 0 (aka the 'ok' error code) 71 | let returned = error == nil ? snapshot?.pointee : nil 72 | 73 | // Make sure we dispatch our callback back into the main thread to keep consistent 74 | // with the reference API which will call back on the 'user_executor' which typically 75 | // ends up being the main queue. 76 | // Relevant code: 77 | // - https://github.com/firebase/firebase-ios-sdk/blob/main/Firestore/Source/API/FIRFirestore.mm#L210-L218 78 | // - https://github.com/firebase/firebase-ios-sdk/blob/main/Firestore/core/src/api/document_reference.cc#L236-L237 79 | DispatchQueue.main.async { 80 | callback(returned, error) 81 | } 82 | }, 83 | boxed.toOpaque() 84 | ) 85 | return ListenerRegistration(boxed, instance) 86 | } 87 | 88 | private func firestoreValueOrFail(_ value: Any) -> firebase.firestore.FieldValue { 89 | guard let value = FirestoreDataConverter.firestoreValue(field: value) else { 90 | fatalError("Unexpected value type: \(type(of: value))") 91 | } 92 | return value 93 | } 94 | 95 | public func whereField(_ field: String, isEqualTo value: Any) -> Query { 96 | swift_firebase.swift_cxx_shims.firebase.firestore.query_where_equal_to( 97 | _asQuery, std.string(field), firestoreValueOrFail(value) 98 | ) 99 | } 100 | 101 | public func whereField(_ field: String, isNotEqualTo value: Any) -> Query { 102 | swift_firebase.swift_cxx_shims.firebase.firestore.query_where_not_equal_to( 103 | _asQuery, std.string(field), firestoreValueOrFail(value) 104 | ) 105 | } 106 | 107 | public func whereField(_ field: String, isLessThan value: Any) -> Query { 108 | swift_firebase.swift_cxx_shims.firebase.firestore.query_where_less_than( 109 | _asQuery, std.string(field), firestoreValueOrFail(value) 110 | ) 111 | } 112 | 113 | public func whereField(_ field: String, isLessThanOrEqualTo value: Any) -> Query { 114 | swift_firebase.swift_cxx_shims.firebase.firestore.query_where_less_than_or_equal_to( 115 | _asQuery, std.string(field), firestoreValueOrFail(value) 116 | ) 117 | } 118 | 119 | public func whereField(_ field: String, isGreaterThan value: Any) -> Query { 120 | swift_firebase.swift_cxx_shims.firebase.firestore.query_where_greater_than( 121 | _asQuery, std.string(field), firestoreValueOrFail(value) 122 | ) 123 | } 124 | 125 | public func whereField(_ field: String, isGreaterThanOrEqualTo value: Any) -> Query { 126 | swift_firebase.swift_cxx_shims.firebase.firestore.query_where_greater_than_or_equal_to( 127 | _asQuery, std.string(field), firestoreValueOrFail(value) 128 | ) 129 | } 130 | 131 | /* TODO: Implement these remaining methods: 132 | 133 | public func whereFilter(_ filter: Filter) -> Query { 134 | } 135 | 136 | public func whereField(_ field: String, arrayContains value: Any) -> Query { 137 | } 138 | 139 | public func whereField(_ field: String, arrayContainsAny values: [Any]) -> Query { 140 | } 141 | 142 | public func whereField(_ field: String, in values: [Any]) -> Query { 143 | } 144 | 145 | public func whereField(_ field: String, notIn values: [Any]) -> Query { 146 | } 147 | 148 | public func whereField(_ path: FieldPath, isNotEqualTo value: Any) -> Query { 149 | } 150 | 151 | public func whereField(_ path: FieldPath, isEqualTo value: Any) -> Query { 152 | } 153 | 154 | public func whereField(_ path: FieldPath, isLessThan value: Any) -> Query { 155 | } 156 | 157 | public func whereField(_ path: FieldPath, isLessThanOrEqualTo value: Any) -> Query { 158 | } 159 | 160 | public func whereField(_ path: FieldPath, isGreaterThan value: Any) -> Query { 161 | } 162 | 163 | public func whereField(_ path: FieldPath, isGreaterThanOrEqualTo value: Any) -> Query { 164 | } 165 | 166 | public func whereField(_ path: FieldPath, arrayContains value: Any) -> Query { 167 | } 168 | 169 | public func whereField(_ path: FieldPath, arrayContainsAny values: [Any]) -> Query { 170 | } 171 | 172 | public func whereField(_ path: FieldPath, in values: [Any]) -> Query { 173 | } 174 | 175 | public func whereField(_ path: FieldPath, notIn values: [Any]) -> Query { 176 | } 177 | 178 | public func filter(using predicate: NSPredicate) -> Query { 179 | } 180 | 181 | public func order(by field: String) -> Query { 182 | } 183 | 184 | public func order(by path: FieldPath) -> Query { 185 | } 186 | 187 | public func order(by field: String, descending: Bool) -> Query { 188 | } 189 | 190 | public func order(by path: FieldPath, descending: Bool) -> Query { 191 | } 192 | 193 | public func limit(to limit: Int) -> Query { 194 | } 195 | 196 | public func limit(toLast limit: Int) -> Query { 197 | } 198 | 199 | public func start(atDocument document: DocumentSnapshot) -> Query { 200 | } 201 | 202 | public func start(at fieldValues: [Any]) -> Query { 203 | } 204 | 205 | public func start(afterDocument document: DocumentSnapshot) -> Query { 206 | } 207 | 208 | public func start(after fieldValues: [Any]) -> Query { 209 | } 210 | 211 | public func end(beforeDocument document: DocumentSnapshot) -> Query { 212 | } 213 | 214 | public func end(before fieldValues: [Any]) -> Query { 215 | } 216 | 217 | public func end(atDocument document: DocumentSnapshot) -> Query { 218 | } 219 | 220 | public func end(at fieldValues: [Any]) -> Query { 221 | } 222 | 223 | public var count: AggregateQuery { 224 | } 225 | 226 | */ 227 | } 228 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/QueryDocumentSnapshot.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | // Wraps a DocumentSnapshot providing a `data(with:)` implementation that 4 | // cannot return `nil`. 5 | public struct QueryDocumentSnapshot { 6 | private let snapshot: DocumentSnapshot 7 | 8 | internal init(snapshot: DocumentSnapshot) { 9 | self.snapshot = snapshot 10 | } 11 | 12 | public var reference: DocumentReference { 13 | snapshot.reference 14 | } 15 | 16 | public var exists: Bool { 17 | snapshot.exists 18 | } 19 | 20 | public var documentID: String { 21 | snapshot.documentID 22 | } 23 | 24 | public func data(with behavior: ServerTimestampBehavior = .default) -> [String : Any] { 25 | let data = swift_firebase.swift_cxx_shims.firebase.firestore.document_snapshot_get_data_workaround(snapshot, behavior) 26 | return FirestoreDataConverter.value(workaround: data) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/QuerySnapshot+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | import CxxShim 7 | 8 | public typealias QuerySnapshot = firebase.firestore.QuerySnapshot 9 | 10 | extension QuerySnapshot { 11 | public var query: Query { 12 | swift_firebase.swift_cxx_shims.firebase.firestore.query_snapshot_query(self) 13 | } 14 | 15 | public var metadata: SnapshotMetadata { 16 | swift_firebase.swift_cxx_shims.firebase.firestore.query_snapshot_metadata(self) 17 | } 18 | 19 | public var isEmpty: Bool { 20 | empty() 21 | } 22 | 23 | public var count: Int { 24 | swift_firebase.swift_cxx_shims.firebase.firestore.query_snapshot_size(self) 25 | } 26 | 27 | public var documents: [DocumentSnapshot] { 28 | Array(swift_firebase.swift_cxx_shims.firebase.firestore.query_snapshot_documents(self)) 29 | } 30 | 31 | public var documentChanges: [DocumentChange] { 32 | Array( 33 | swift_firebase.swift_cxx_shims.firebase.firestore.query_snapshot_document_changes(self, .exclude) 34 | ) 35 | } 36 | 37 | public func documentChanges(includeMetadataChanges: Bool) -> [DocumentChange] { 38 | Array( 39 | swift_firebase.swift_cxx_shims.firebase.firestore.query_snapshot_document_changes(self, includeMetadataChanges ? .include : .exclude) 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/Settings+Swift.swift: -------------------------------------------------------------------------------- 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 struct FirestoreSettings { 12 | var impl: firebase.firestore.Settings 13 | 14 | init(_ impl: firebase.firestore.Settings) { 15 | self.impl = impl 16 | } 17 | 18 | public init() { 19 | impl = .init() 20 | } 21 | 22 | public var debugDescription: String { 23 | String(impl.ToString()) 24 | } 25 | 26 | public var isPersistenceEnabled: Bool { 27 | get { 28 | impl.is_persistence_enabled() 29 | } 30 | set { 31 | impl.set_persistence_enabled(newValue) 32 | } 33 | } 34 | } 35 | 36 | extension FirestoreSettings: CustomDebugStringConvertible {} 37 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/SnapshotMetadata+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | public typealias SnapshotMetadata = firebase.firestore.SnapshotMetadata 7 | 8 | extension SnapshotMetadata { 9 | public var hasPendingWrites: Bool { 10 | has_pending_writes() 11 | } 12 | 13 | public var isFromCache: Bool { 14 | is_from_cache() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/Timestamp+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | import Foundation 3 | #if os(Windows) 4 | import WinSDK 5 | import CRT 6 | #endif 7 | 8 | public typealias Timestamp = firebase.Timestamp 9 | private let NanoSecondsPerSecond = 1_000_000_000 10 | 11 | extension Timestamp { 12 | public init(date: Date) { 13 | self = date.firestoreTimestamp() 14 | } 15 | 16 | public init(seconds: Int64, nanoseconds: Int32) { 17 | self = Timestamp(seconds, nanoseconds) 18 | } 19 | 20 | public func dateValue() -> Date { 21 | let seconds = TimeInterval(self.seconds()) 22 | let nanoseconds = TimeInterval(self.nanoseconds()) 23 | 24 | let interval = TimeInterval(seconds + nanoseconds / 1e9) 25 | 26 | return Date(timeIntervalSince1970: interval) 27 | } 28 | } 29 | 30 | extension Timestamp: Codable { 31 | private enum CodingKeys: String, CodingKey { 32 | case seconds 33 | case nanoseconds 34 | } 35 | 36 | public init(from decoder: Decoder) throws { 37 | let container = try decoder.container(keyedBy: CodingKeys.self) 38 | let seconds = try container.decode(Int64.self, forKey: .seconds) 39 | let nanoseconds = try container.decode(Int32.self, forKey: .nanoseconds) 40 | self.init(seconds: seconds, nanoseconds: nanoseconds) 41 | } 42 | 43 | public func encode(to encoder: Encoder) throws { 44 | var container = encoder.container(keyedBy: CodingKeys.self) 45 | try container.encode(seconds(), forKey: .seconds) 46 | try container.encode(nanoseconds(), forKey: .nanoseconds) 47 | } 48 | } 49 | 50 | extension Date { 51 | internal func firestoreTimestamp() -> Timestamp { 52 | var secondsDouble: Double = 0.0 53 | var fraction = modf(timeIntervalSince1970, &secondsDouble) 54 | 55 | // Re-implementation of https://github.com/firebase/firebase-ios-sdk/blob/master/Firestore/Source/API/FIRTimestamp.m#L50 56 | if (fraction < 0) { 57 | fraction += 1.0; 58 | secondsDouble -= 1.0; 59 | } 60 | 61 | let seconds = Int64(secondsDouble) 62 | let nanoseconds = Int32(fraction * Double(NanoSecondsPerSecond)) 63 | 64 | return Timestamp(seconds, nanoseconds) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/Transaction+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | import CxxShim 7 | import Foundation 8 | 9 | public typealias Transaction = swift_firebase.swift_cxx_shims.firebase.firestore.TransactionWeakReference 10 | 11 | extension Transaction { 12 | @discardableResult public func setData(_ data: [String : Any], forDocument document: DocumentReference) -> Transaction { 13 | setData(data, forDocument: document, merge: false) 14 | } 15 | 16 | @discardableResult public func setData(_ data: [String : Any], forDocument document: DocumentReference, merge: Bool) -> Transaction { 17 | guard is_valid() else { fatalError("Transaction accessed outside of updateBlock") } 18 | self.Set(document, FirestoreDataConverter.firestoreValue(document: data), merge ? .Merge() : .init()) 19 | return self 20 | } 21 | 22 | /* TODO: implement 23 | @discardableResult public func setData(_ data: [String : Any], forDocument document: DocumentReference, mergeFields: [Any]) -> Transaction { 24 | } 25 | */ 26 | 27 | @discardableResult public func updateData(_ fields: [String : Any], forDocument document: DocumentReference) -> Transaction { 28 | guard is_valid() else { fatalError("Transaction accessed outside of updateBlock") } 29 | self.Update(document, FirestoreDataConverter.firestoreValue(document: fields)) 30 | return self 31 | } 32 | 33 | @discardableResult public func deleteDocument(_ document: DocumentReference) -> Transaction { 34 | guard is_valid() else { fatalError("Transaction accessed outside of updateBlock") } 35 | Delete(document) 36 | return self 37 | } 38 | 39 | public func getDocument(_ document: DocumentReference) throws -> DocumentSnapshot { 40 | guard is_valid() else { fatalError("Transaction accessed outside of updateBlock") } 41 | 42 | var error = firebase.firestore.kErrorNone 43 | var errorMessage = std.string() 44 | 45 | let snapshot = Get(document, &error, &errorMessage) 46 | 47 | if error != firebase.firestore.kErrorNone { 48 | throw FirestoreErrorCode(error, errorMessage: String(errorMessage)) 49 | } 50 | 51 | return snapshot 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/TransactionOptions+Swift.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | 6 | public typealias TransactionOptions = firebase.firestore.TransactionOptions 7 | 8 | extension TransactionOptions { 9 | public var maxAttempts: Int { 10 | get { 11 | Int(max_attempts()) 12 | } 13 | set { 14 | set_max_attempts(Int32(newValue)) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/WriteBatch+Swift.swift: -------------------------------------------------------------------------------- 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 typealias WriteBatch = firebase.firestore.WriteBatch 12 | 13 | extension WriteBatch { 14 | public mutating func setData(_ data: [String : Any], forDocument document: DocumentReference) -> WriteBatch { 15 | setData(data, forDocument: document, merge: false) 16 | } 17 | 18 | public mutating func setData(_ data: [String : Any], forDocument document: DocumentReference, merge: Bool) -> WriteBatch { 19 | _ = swift_firebase.swift_cxx_shims.firebase.firestore.write_batch_set( 20 | self, document, FirestoreDataConverter.firestoreValue(document: data), merge ? .Merge() : .init() 21 | ) 22 | return self 23 | } 24 | 25 | /* TODO: implement 26 | public mutating func setData(_ data: [String : Any], forDocument document: DocumentReference, mergeFields: [Any]) -> WriteBatch { 27 | } 28 | */ 29 | 30 | public mutating func updateData(_ fields: [String : Any], forDocument document: DocumentReference) -> WriteBatch { 31 | _ = swift_firebase.swift_cxx_shims.firebase.firestore.write_batch_update( 32 | self, document, FirestoreDataConverter.firestoreValue(document: fields) 33 | ) 34 | return self 35 | } 36 | 37 | public mutating func deleteDocument(_ document: DocumentReference) -> WriteBatch { 38 | _ = swift_firebase.swift_cxx_shims.firebase.firestore.write_batch_delete( 39 | self, document 40 | ) 41 | return self 42 | } 43 | 44 | public mutating func commit(completion: @escaping ((Error?) -> Void) = { _ in }) { 45 | let future = swift_firebase.swift_cxx_shims.firebase.firestore.write_batch_commit(self) 46 | future.setCompletion({ 47 | let (_, error) = future.resultAndError { FirestoreErrorCode($0) } 48 | DispatchQueue.main.async { 49 | completion(error) 50 | } 51 | }) 52 | } 53 | 54 | public mutating func commit() async throws { 55 | try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in 56 | let future = swift_firebase.swift_cxx_shims.firebase.firestore.write_batch_commit(self) 57 | future.setCompletion({ 58 | let (_, error) = future.resultAndError { FirestoreErrorCode($0) } 59 | if let error { 60 | continuation.resume(throwing: error) 61 | } else { 62 | continuation.resume() 63 | } 64 | }) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/vendor/Codable/CodableErrors.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Google 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | public enum FirestoreDecodingError: Error { 18 | case decodingIsNotSupported(String) 19 | case fieldNameConflict(String) 20 | } 21 | 22 | public enum FirestoreEncodingError: Error { 23 | case encodingIsNotSupported(String) 24 | } 25 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/vendor/Codable/CodablePassThroughTypes.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Google 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import Foundation 18 | 19 | internal struct FirestorePassthroughTypes: StructureCodingPassthroughTypeResolver { 20 | static func isPassthroughType(_ t: T) -> Bool { 21 | return 22 | t is GeoPoint || 23 | t is Timestamp || 24 | t is FieldValue || 25 | t is DocumentReference 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/vendor/Codable/DocumentID.swift: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Google 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | extension CodingUserInfoKey { 18 | static let documentRefUserInfoKey = 19 | CodingUserInfoKey(rawValue: "DocumentRefUserInfoKey")! 20 | } 21 | 22 | /// A type that can initialize itself from a Firestore `DocumentReference`, 23 | /// which makes it suitable for use with the `@DocumentID` property wrapper. 24 | /// 25 | /// Firestore includes extensions that make `String` and `DocumentReference` 26 | /// conform to `DocumentIDWrappable`. 27 | /// 28 | /// Note that Firestore ignores fields annotated with `@DocumentID` when writing 29 | /// so there is no requirement to convert from the wrapped type back to a 30 | /// `DocumentReference`. 31 | public protocol DocumentIDWrappable { 32 | /// Creates a new instance by converting from the given `DocumentReference`. 33 | static func wrap(_ documentReference: DocumentReference) throws -> Self 34 | } 35 | 36 | extension String: DocumentIDWrappable { 37 | public static func wrap(_ documentReference: DocumentReference) throws -> Self { 38 | return documentReference.documentID 39 | } 40 | } 41 | 42 | extension DocumentReference: DocumentIDWrappable { 43 | public static func wrap(_ documentReference: DocumentReference) throws -> Self { 44 | return documentReference 45 | } 46 | } 47 | 48 | /// An internal protocol that allows Firestore.Decoder to test if a type is a 49 | /// DocumentID of some kind without knowing the specific generic parameter that 50 | /// the user actually used. 51 | /// 52 | /// This is required because Swift does not define an existential type for all 53 | /// instances of a generic class--that is, it has no wildcard or raw type that 54 | /// matches a generic without any specific parameter. Swift does define an 55 | /// existential type for protocols though, so this protocol (to which DocumentID 56 | /// conforms) indirectly makes it possible to test for and act on any 57 | /// `DocumentID`. 58 | internal protocol DocumentIDProtocol { 59 | /// Initializes the DocumentID from a DocumentReference. 60 | init(from documentReference: DocumentReference?) throws 61 | } 62 | 63 | /// A property wrapper type that marks a `DocumentReference?` or `String?` field to 64 | /// be populated with a document identifier when it is read. 65 | /// 66 | /// Apply the `@DocumentID` annotation to a `DocumentReference?` or `String?` 67 | /// property in a `Codable` object to have it populated with the document 68 | /// identifier when it is read and decoded from Firestore. 69 | /// 70 | /// - Important: The name of the property annotated with `@DocumentID` must not 71 | /// match the name of any fields in the Firestore document being read or else 72 | /// an error will be thrown. For example, if the `Codable` object has a 73 | /// property named `firstName` annotated with `@DocumentID`, and the Firestore 74 | /// document contains a field named `firstName`, an error will be thrown when 75 | /// attempting to decode the document. 76 | /// 77 | /// - Example Read: 78 | /// ```` 79 | /// struct Player: Codable { 80 | /// @DocumentID var playerID: String? 81 | /// var health: Int64 82 | /// } 83 | /// 84 | /// let p = try! await Firestore.firestore() 85 | /// .collection("players") 86 | /// .document("player-1") 87 | /// .getDocument(as: Player.self) 88 | /// print("\(p.playerID!) Health: \(p.health)") 89 | /// 90 | /// // Prints: "Player: player-1, Health: 95" 91 | /// ```` 92 | /// 93 | /// - Important: Trying to encode/decode this type using encoders/decoders other than 94 | /// Firestore.Encoder throws an error. 95 | /// 96 | /// - Important: When writing a Codable object containing an `@DocumentID` annotated field, 97 | /// its value is ignored. This allows you to read a document from one path and 98 | /// write it into another without adjusting the value here. 99 | @propertyWrapper 100 | public struct DocumentID: 101 | StructureCodingUncodedUnkeyed { 102 | private var value: Value? = nil 103 | 104 | public init(wrappedValue value: Value?) { 105 | if let value = value { 106 | logIgnoredValueWarning(value: value) 107 | } 108 | self.value = value 109 | } 110 | 111 | public var wrappedValue: Value? { 112 | get { value } 113 | set { 114 | if let someNewValue = newValue { 115 | logIgnoredValueWarning(value: someNewValue) 116 | } 117 | value = newValue 118 | } 119 | } 120 | 121 | private func logIgnoredValueWarning(value: Value) { 122 | print( 123 | """ 124 | [FirebaseFirestore] I-FST000002 125 | Attempting to initialize or set a @DocumentID property with a non-nil \ 126 | value: "\(value)". The document ID is managed by Firestore and any \ 127 | initialized or set value will be ignored. The ID is automatically set \ 128 | when reading from Firestore. 129 | """ 130 | ) 131 | } 132 | } 133 | 134 | extension DocumentID: DocumentIDProtocol { 135 | internal init(from documentReference: DocumentReference?) throws { 136 | if let documentReference = documentReference { 137 | value = try Value.wrap(documentReference) 138 | } else { 139 | value = nil 140 | } 141 | } 142 | } 143 | 144 | extension DocumentID: Codable { 145 | /// A `Codable` object containing an `@DocumentID` annotated field should 146 | /// only be decoded with `Firestore.Decoder`; this initializer throws if an 147 | /// unsupported decoder is used. 148 | /// 149 | /// - Parameter decoder: A decoder. 150 | /// - Throws: ``FirestoreDecodingError`` 151 | public init(from decoder: Decoder) throws { 152 | guard let reference = decoder 153 | .userInfo[CodingUserInfoKey.documentRefUserInfoKey] as? DocumentReference else { 154 | throw FirestoreDecodingError.decodingIsNotSupported( 155 | """ 156 | Could not find DocumentReference for user info key: \(CodingUserInfoKey 157 | .documentRefUserInfoKey). 158 | DocumentID values can only be decoded with Firestore.Decoder 159 | """ 160 | ) 161 | } 162 | try self.init(from: reference) 163 | } 164 | 165 | /// A `Codable` object containing an `@DocumentID` annotated field can only 166 | /// be encoded with `Firestore.Encoder`; this initializer always throws. 167 | /// 168 | /// - Parameter encoder: An invalid encoder. 169 | /// - Throws: ``FirestoreEncodingError`` 170 | public func encode(to encoder: Encoder) throws { 171 | throw FirestoreEncodingError.encodingIsNotSupported( 172 | "DocumentID values can only be encoded with Firestore.Encoder" 173 | ) 174 | } 175 | } 176 | 177 | extension DocumentID: Equatable where Value: Equatable {} 178 | extension DocumentID: Hashable where Value: Hashable {} 179 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/vendor/FirebaseDataEncoder/FirebaseRemoteConfigValueDecoding.swift: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | import Foundation 16 | 17 | /// Conform to this protocol for the Firebase Decoder to extract values as from a RemoteConfigValue 18 | /// object. 19 | public protocol FirebaseRemoteConfigValueDecoding { 20 | func numberValue() -> NSNumber 21 | func boolValue() -> Bool 22 | func stringValue() -> String 23 | func dataValue() -> Data 24 | func arrayValue() -> [AnyHashable]? 25 | func dictionaryValue() -> [String: AnyHashable]? 26 | } 27 | -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/vendor/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | 204 | 205 | ## Runtime Library Exception to the Apache 2.0 License: ## 206 | 207 | 208 | As an exception, if you use this Software to compile your source code and 209 | portions of this Software are embedded into the binary product as a result, 210 | you may redistribute such product without providing attribution as would 211 | otherwise be required by Sections 4(a), 4(b) and 4(d) of the License. -------------------------------------------------------------------------------- /Sources/FirebaseFirestore/vendor/README.md: -------------------------------------------------------------------------------- 1 | # Manually Vendored Files From `firebase-ios-sdk` 2 | 3 | This is a manual copy of select files from the Firebase iOS SDK Project, Folder Mapping: 4 | 5 | - `FirebaseDataEncoder` [firebase-ios-sdk link](https://github.com/firebase/firebase-ios-sdk/tree/master/FirebaseSharedSwift). 6 | - `Codable` [firebase-ios-sdk link](https://github.com/firebase/firebase-ios-sdk/tree/master/Firestore/Swift/Source/Codable). 7 | 8 | We've had to manually copy the files because: 9 | 10 | 1. SwiftPM on Windows (maybe Linux too?) seems unable to cope with the binary artifacts that the larger Firebase package file describes resulting in an inability to resolve the consuming package file if `firebase-ios-sdk` is included as a dependency. 11 | 2. The Firebase JSON decoding is a pretty heavily customized version of the JSON decoder, so rather than rolling our own or seeing if Apple's just works we should rely on the implementation from the Firebase Authors. 12 | 3. The contents of these files hasn't changed in about a year so they are relatively stable and the additional overhead of creating a forked repo and package feels like overkill. 13 | 14 | We've filed [12062](https://github.com/firebase/firebase-ios-sdk/issues/12062) on the Firebase repo to try to break these files out. -------------------------------------------------------------------------------- /Sources/FirebaseFunctions/Functions+Swift.swift: -------------------------------------------------------------------------------- 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 | } 29 | -------------------------------------------------------------------------------- /Sources/FirebaseFunctions/FunctionsErrorCode.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | @_spi(FirebaseInternal) 6 | import FirebaseCore 7 | 8 | public struct FunctionsErrorCode: 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 FunctionsErrorCode: RawRepresentable { 23 | public typealias RawValue = Int 24 | 25 | public init(rawValue: Int) { 26 | self.rawValue = rawValue 27 | localizedDescription = "\(rawValue)" 28 | } 29 | } 30 | 31 | extension FunctionsErrorCode { 32 | init(_ error: firebase.functions.Error, errorMessage: String?) { 33 | self.init((code: numericCast(error.rawValue), message: errorMessage ?? "\(error.rawValue)")) 34 | } 35 | 36 | init?(_ error: firebase.functions.Error?, errorMessage: UnsafePointer?) { 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 FunctionsErrorCode { 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 FunctionsErrorCode: Equatable {} 67 | 68 | extension FunctionsErrorCode { 69 | // The Obj C API provides this type as well, so provide it here for consistency. 70 | public typealias Code = FunctionsErrorCode 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 | } 83 | -------------------------------------------------------------------------------- /Sources/FirebaseFunctions/HTTPSCallable+Swift.swift: -------------------------------------------------------------------------------- 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: swift_firebase.swift_cxx_shims.firebase.functions.HttpsCallableRef 13 | 14 | init(_ impl: swift_firebase.swift_cxx_shims.firebase.functions.HttpsCallableRef) { 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) 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 { FunctionsErrorCode($0) } 43 | completion(result.map { .init($0) }, error) 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/FirebaseFunctions/HTTPSCallableResult+Swift.swift: -------------------------------------------------------------------------------- 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 | public 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 | } 22 | -------------------------------------------------------------------------------- /Sources/FirebaseStorage/Storage+Swift.swift: -------------------------------------------------------------------------------- 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 Storage { 11 | let impl: swift_firebase.swift_cxx_shims.firebase.storage.StorageRef 12 | 13 | init(_ impl: swift_firebase.swift_cxx_shims.firebase.storage.StorageRef) { 14 | self.impl = impl 15 | } 16 | 17 | public static func storage(app: FirebaseApp) -> Storage { 18 | let instance = swift_firebase.swift_cxx_shims.firebase.storage.storage_get_instance(app) 19 | guard swift_firebase.swift_cxx_shims.firebase.storage.storage_is_valid(instance) else { 20 | fatalError("Invalid Storage Instance") 21 | } 22 | return .init(instance) 23 | } 24 | 25 | public func reference(withPath path: String) -> StorageReference { 26 | .init(swift_firebase.swift_cxx_shims.firebase.storage.storage_get_reference(impl, path)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/FirebaseStorage/StorageErrorCode.swift: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | @_exported 4 | import firebase 5 | @_spi(FirebaseInternal) 6 | import FirebaseCore 7 | 8 | public struct StorageErrorCode: 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.storage.Error) { 18 | self.init(rawValue: Int(error.rawValue)) 19 | } 20 | } 21 | 22 | extension StorageErrorCode: RawRepresentable { 23 | public typealias RawValue = Int 24 | 25 | public init(rawValue: Int) { 26 | self.rawValue = rawValue 27 | localizedDescription = "\(rawValue)" 28 | } 29 | } 30 | 31 | extension StorageErrorCode { 32 | init(_ error: firebase.storage.Error, errorMessage: String?) { 33 | self.init((code: numericCast(error.rawValue), message: errorMessage ?? "\(error.rawValue)")) 34 | } 35 | 36 | init?(_ error: firebase.storage.Error?, errorMessage: UnsafePointer?) { 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 StorageErrorCode { 47 | public static var none: Self { .init(firebase.storage.kErrorNone) } 48 | public static var unknown: Self { .init(firebase.storage.kErrorUnknown) } 49 | public static var objectNotFound: Self { .init(firebase.storage.kErrorObjectNotFound) } 50 | public static var bucketNotFound: Self { .init(firebase.storage.kErrorBucketNotFound) } 51 | public static var projectNotFound: Self { .init(firebase.storage.kErrorProjectNotFound) } 52 | public static var quotaExceeded: Self { .init(firebase.storage.kErrorQuotaExceeded) } 53 | public static var unauthenticated: Self { .init(firebase.storage.kErrorUnauthenticated) } 54 | public static var unauthorized: Self { .init(firebase.storage.kErrorUnauthorized) } 55 | public static var retryLimitExceeded: Self { .init(firebase.storage.kErrorRetryLimitExceeded) } 56 | public static var nonMatchingChecksum: Self { .init(firebase.storage.kErrorNonMatchingChecksum) } 57 | public static var downloadSizeExceeded: Self { .init(firebase.storage.kErrorDownloadSizeExceeded) } 58 | public static var cancelled: Self { .init(firebase.storage.kErrorCancelled) } 59 | } 60 | 61 | extension StorageErrorCode: Equatable {} 62 | 63 | extension StorageErrorCode { 64 | // The Obj C API provides this type as well, so provide it here for consistency. 65 | public typealias Code = StorageErrorCode 66 | 67 | // This allows us to re-expose self as a code similarly 68 | // to what the Firebase SDK does when it creates the 69 | // underlying NSErrors on iOS/macOS. 70 | public var code: Code { 71 | return self 72 | } 73 | 74 | public init(_ code: Code) { 75 | self.init(rawValue: code.rawValue) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Sources/FirebaseStorage/StorageMetadata+Swift.swift: -------------------------------------------------------------------------------- 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 StorageMetadata { 11 | let impl: firebase.storage.Metadata 12 | 13 | init(_ impl: firebase.storage.Metadata) { 14 | self.impl = impl 15 | } 16 | 17 | public init() { 18 | self.impl = .init() 19 | } 20 | 21 | public var customMetadata: [String: String]? { 22 | get { 23 | let map = swift_firebase.swift_cxx_shims.firebase.storage.metadata_get_custom_metadata(impl) 24 | return map.toDict() 25 | } 26 | set { 27 | swift_firebase.swift_cxx_shims.firebase.storage.metadata_clear_custom_metadata(impl) 28 | guard let newValue else { return } 29 | for (key, value) in newValue { 30 | swift_firebase.swift_cxx_shims.firebase.storage.metadata_insert_custom_metadata( 31 | impl, std.string(key), std.string(value) 32 | ) 33 | } 34 | } 35 | } 36 | } 37 | 38 | // Workaround for https://github.com/apple/swift/issues/69711 39 | private extension swift_firebase.swift_cxx_shims.firebase.storage.CustomMetadata { 40 | borrowing func toDict() -> [String: String] { 41 | var result = [String: String]() 42 | var iterator = swift_firebase.swift_cxx_shims.firebase.storage.custom_metadata_begin(self) 43 | let endIterator = swift_firebase.swift_cxx_shims.firebase.storage.custom_metadata_end(self) 44 | 45 | while !swift_firebase.swift_cxx_shims.firebase.storage.custom_metadata_iterators_equal(iterator, endIterator) { 46 | let key = swift_firebase.swift_cxx_shims.firebase.storage.custom_metadata_iterator_first(iterator) 47 | let value = swift_firebase.swift_cxx_shims.firebase.storage.custom_metadata_iterator_second(iterator) 48 | result[String(key.pointee)] = String(value.pointee) 49 | iterator = iterator.successor() 50 | } 51 | return result 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/FirebaseStorage/StorageReference+Swift.swift: -------------------------------------------------------------------------------- 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 StorageReference { 12 | let impl: firebase.storage.StorageReference 13 | 14 | init(_ impl: firebase.storage.StorageReference) { 15 | self.impl = impl 16 | } 17 | 18 | public func child(_ path: String) -> StorageReference { 19 | .init(impl.Child(path)) 20 | } 21 | 22 | public func downloadURL(completion: @escaping (URL?, Error?) -> Void) { 23 | downloadURLImpl() { result, error in 24 | DispatchQueue.main.async { 25 | completion(result, error) 26 | } 27 | } 28 | } 29 | 30 | public func downloadURL() async throws -> URL { 31 | try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in 32 | downloadURLImpl() { result, error in 33 | if let error { 34 | continuation.resume(throwing: error) 35 | } else { 36 | continuation.resume(returning: result!) 37 | } 38 | } 39 | } 40 | } 41 | 42 | private func downloadURLImpl(completion: @escaping (URL?, Error?) -> Void) { 43 | let future = swift_firebase.swift_cxx_shims.firebase.storage.storage_reference_get_download_url(impl) 44 | future.setCompletion({ 45 | let (result, error) = future.resultAndError { StorageErrorCode($0) } 46 | completion(result.flatMap { .init(string: String($0)) }, error) 47 | }) 48 | } 49 | 50 | public func putDataAsync( 51 | _ uploadData: Data, 52 | metadata: StorageMetadata? = nil, 53 | onProgress: ((Progress?) -> Void)? = nil 54 | ) async throws -> StorageMetadata { 55 | // TODO(PRENG-63978): Add support for `onProgress` callback. 56 | assert(onProgress == nil, "Missing support for non-nil onProgress") 57 | let controller = ControllerRef() 58 | return try await withTaskCancellationHandler { 59 | try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in 60 | let future = uploadData.withUnsafeBytes { ptr in 61 | let bytes = ptr.baseAddress!.assumingMemoryBound(to: UInt8.self) 62 | let numBytes = uploadData.count 63 | if let metadata { 64 | return swift_firebase.swift_cxx_shims.firebase.storage.storage_reference_put_bytes( 65 | self.impl, bytes, numBytes, metadata.impl, &controller.impl 66 | ) 67 | } else { 68 | return swift_firebase.swift_cxx_shims.firebase.storage.storage_reference_put_bytes( 69 | self.impl, bytes, numBytes, &controller.impl 70 | ) 71 | } 72 | } 73 | future.setCompletion({ 74 | let (result, error) = future.resultAndError { StorageErrorCode($0) } 75 | if let error { 76 | continuation.resume(throwing: error) 77 | } else { 78 | continuation.resume(returning: result.map { .init($0) } ?? .init()) 79 | } 80 | }) 81 | } 82 | } onCancel: { 83 | controller.impl.Cancel() 84 | } 85 | } 86 | } 87 | 88 | // The underlying `firebase.storage.Controller` type is thread-safe. 89 | private class ControllerRef: @unchecked Sendable { 90 | var impl: firebase.storage.Controller = .init() 91 | } 92 | -------------------------------------------------------------------------------- /Sources/firebase/include/FirebaseApp.hh: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | #ifndef firebase_include_FirebaseApp_hh 4 | #define firebase_include_FirebaseApp_hh 5 | 6 | #include 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /Sources/firebase/include/FirebaseAuth.hh: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | #ifndef firebase_include_FirebaseAuth_hh 4 | #define firebase_include_FirebaseAuth_hh 5 | 6 | #include 7 | 8 | #include "FirebaseCore.hh" 9 | 10 | // https://github.com/apple/swift/issues/69959 11 | #if __has_include() 12 | #include 13 | #else 14 | #define SWIFT_UNSAFE_REFERENCE \ 15 | __attribute__((__swift_attr__("import_reference"))) \ 16 | __attribute__((__swift_attr__("retain:immortal"))) \ 17 | __attribute__((__swift_attr__("release:immortal"))) 18 | #endif 19 | 20 | namespace swift_firebase::swift_cxx_shims::firebase::auth { 21 | 22 | inline std::string 23 | user_display_name(const ::firebase::auth::User &user) noexcept { 24 | return user.display_name(); 25 | } 26 | 27 | inline std::string user_email(const ::firebase::auth::User &user) noexcept { 28 | return user.email(); 29 | } 30 | 31 | inline std::string 32 | user_phone_number(const ::firebase::auth::User &user) noexcept { 33 | return user.phone_number(); 34 | } 35 | 36 | inline std::string user_photo_url(const ::firebase::auth::User &user) noexcept { 37 | return user.photo_url(); 38 | } 39 | 40 | inline std::string 41 | user_provider_id(const ::firebase::auth::User &user) noexcept { 42 | return user.provider_id(); 43 | } 44 | 45 | inline std::string user_uid(const ::firebase::auth::User &user) noexcept { 46 | return user.uid(); 47 | } 48 | 49 | inline ::swift_firebase::swift_cxx_shims::firebase::VoidFuture 50 | user_reload(::firebase::auth::User user) { 51 | return ::swift_firebase::swift_cxx_shims::firebase::VoidFuture::From( 52 | user.Reload()); 53 | } 54 | 55 | inline ::swift_firebase::swift_cxx_shims::firebase::VoidFuture 56 | user_delete(::firebase::auth::User user) { 57 | return ::swift_firebase::swift_cxx_shims::firebase::VoidFuture::From( 58 | user.Delete()); 59 | } 60 | 61 | inline ::swift_firebase::swift_cxx_shims::firebase::Future< 62 | ::firebase::auth::AuthResult> 63 | user_reauthenticate_and_retrieve_data( 64 | ::firebase::auth::User user, 65 | const ::firebase::auth::Credential& credential) { 66 | return user.ReauthenticateAndRetrieveData(credential); 67 | } 68 | 69 | inline ::swift_firebase::swift_cxx_shims::firebase::Future<::std::string> 70 | user_get_token(::firebase::auth::User user, bool force_refresh) { 71 | return user.GetToken(force_refresh); 72 | } 73 | 74 | inline ::swift_firebase::swift_cxx_shims::firebase::VoidFuture 75 | user_send_email_verification(::firebase::auth::User user) { 76 | return ::swift_firebase::swift_cxx_shims::firebase::VoidFuture::From( 77 | user.SendEmailVerification()); 78 | } 79 | 80 | inline ::swift_firebase::swift_cxx_shims::firebase::Future< 81 | ::firebase::auth::Auth::FetchProvidersResult> 82 | auth_fetch_providers_for_email( 83 | ::firebase::auth::Auth* auth, const char* email) { 84 | return auth->FetchProvidersForEmail(email); 85 | } 86 | 87 | inline ::swift_firebase::swift_cxx_shims::firebase::Future< 88 | ::firebase::auth::AuthResult> 89 | auth_sign_in_with_email_and_password( 90 | ::firebase::auth::Auth* auth, const char* email, const char* password) { 91 | return auth->SignInWithEmailAndPassword(email, password); 92 | } 93 | 94 | inline ::swift_firebase::swift_cxx_shims::firebase::Future< 95 | ::firebase::auth::AuthResult> 96 | auth_create_user_with_email_and_password( 97 | ::firebase::auth::Auth* auth, const char* email, const char* password) { 98 | return auth->CreateUserWithEmailAndPassword(email, password); 99 | } 100 | 101 | inline ::swift_firebase::swift_cxx_shims::firebase::VoidFuture 102 | auth_send_password_reset_email( 103 | ::firebase::auth::Auth* auth, const char* email) { 104 | return ::swift_firebase::swift_cxx_shims::firebase::VoidFuture::From( 105 | auth->SendPasswordResetEmail(email)); 106 | } 107 | 108 | class SWIFT_UNSAFE_REFERENCE AuthStateListener 109 | : public ::firebase::auth::AuthStateListener { 110 | typedef void (*Handler)(::firebase::auth::Auth *auth, 111 | ::firebase::auth::User *user, void *user_data); 112 | 113 | public: 114 | AuthStateListener(Handler handler, void *data) noexcept 115 | : block_(handler), user_data_(data) {} 116 | 117 | AuthStateListener(const AuthStateListener &) = delete; 118 | AuthStateListener &operator=(const AuthStateListener &) = delete; 119 | 120 | AuthStateListener(AuthStateListener &&) = delete; 121 | AuthStateListener &operator=(AuthStateListener &&) = delete; 122 | 123 | void OnAuthStateChanged(::firebase::auth::Auth *auth) override { 124 | ::firebase::auth::User user = auth->current_user(); 125 | block_(auth, &user, user_data_); 126 | } 127 | 128 | /// @brief Allocate an auth listener on the heap and return it configured with the passed in parameters. 129 | /// @param handler This is the callback that you would like to run whenever Firebase tells us that auth has changed, typically you will construct this inline with the function call. 130 | /// @param user_data User specified data that will be passed to the handler. 131 | /// @return A heap-allocated auth listener. 132 | static inline ::firebase::auth::AuthStateListener * 133 | Create(Handler handler, void *user_data) noexcept { 134 | return new AuthStateListener(handler, user_data); 135 | } 136 | 137 | static inline void Destroy(void *listener) noexcept { 138 | delete reinterpret_cast(listener); 139 | } 140 | 141 | private: 142 | Handler block_; 143 | void *user_data_; 144 | }; 145 | 146 | } // namespace swift_firebase::swift_cxx_shims::firebase::auth 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /Sources/firebase/include/FirebaseCore.hh: -------------------------------------------------------------------------------- 1 | #ifndef firebase_include_FirebaseCore_hh 2 | #define firebase_include_FirebaseCore_hh 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace swift_firebase::swift_cxx_shims::firebase { 9 | 10 | typedef void (*FutureCompletionType)(void*); 11 | 12 | // This class exists to provide protocol conformance to FutureProtocol. It 13 | // also provides a method to invoke `OnCompletion` in a way that works from 14 | // Swift. We can ignore the `FutureBase` param as the Swift caller can just 15 | // retain the Future as part of its closure. 16 | template 17 | class SWIFT_CONFORMS_TO_PROTOCOL(FirebaseCore.FutureProtocol) 18 | Future : public ::firebase::Future { 19 | public: 20 | typedef R ResultType; 21 | 22 | Future(const ::firebase::Future& rhs) : ::firebase::Future(rhs) {} 23 | 24 | // Allow explicit conversion from `Future` in support of `VoidFuture`. 25 | static Future From(const ::firebase::Future& other) { 26 | static_assert(sizeof(::firebase::Future) == sizeof(::firebase::Future)); 27 | return Future(*reinterpret_cast*>(&other)); 28 | } 29 | 30 | void OnCompletion( 31 | _Nonnull FutureCompletionType completion, 32 | _Nullable void* user_data) const { 33 | ::firebase::FutureBase::OnCompletion( 34 | [completion, user_data](const ::firebase::FutureBase&) { 35 | completion(user_data); 36 | }); 37 | } 38 | 39 | // FIXME: Remove once https://github.com/apple/swift/issues/74578 is fixed. 40 | #if defined(SR74578) 41 | int error() const { 42 | return ::firebase::Future::error(); 43 | } 44 | 45 | const R *result() const { 46 | return ::firebase::Future::result(); 47 | } 48 | 49 | const char *error_message() const { 50 | return ::firebase::Future::error_message(); 51 | } 52 | #endif 53 | }; 54 | 55 | // As a workaround, use `int` here instead of `void` for futures with no 56 | // result. Swift is not able to handle a `ResultType` of `void`. 57 | typedef Future VoidFuture; 58 | 59 | // VARIANT support 60 | 61 | inline void 62 | variant_map_insert(::firebase::Variant* variant, 63 | const ::firebase::Variant& key, 64 | const ::firebase::Variant& value) { 65 | variant->map()[key] = value; 66 | } 67 | 68 | inline bool 69 | variant_bool_value(const ::firebase::Variant& variant) { 70 | return variant.bool_value(); // Someone was being too cute! 71 | } 72 | 73 | } // namespace swift_firebase::swift_cxx_shims::firebase 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /Sources/firebase/include/FirebaseFunctions.hh: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | #ifndef firebase_include_FirebaseFunctions_hh 4 | #define firebase_include_FirebaseFunctions_hh 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "FirebaseCore.hh" 13 | 14 | namespace swift_firebase::swift_cxx_shims::firebase::functions { 15 | 16 | typedef std::shared_ptr<::firebase::functions::Functions> FunctionsRef; 17 | typedef std::shared_ptr<::firebase::functions::HttpsCallableReference> HttpsCallableRef; 18 | 19 | inline bool 20 | functions_is_valid(const FunctionsRef& ref) { 21 | return ref.operator bool(); 22 | } 23 | 24 | inline FunctionsRef 25 | functions_get_instance(::firebase::App* app) { 26 | return FunctionsRef(::firebase::functions::Functions::GetInstance(app)); 27 | } 28 | 29 | inline HttpsCallableRef 30 | functions_get_https_callable(FunctionsRef ref, const char* name) { 31 | // Unfortunately `HttpsCallableReference` does not use internal reference 32 | // counting, and as a result, we need to avoid the copy-constructor for 33 | // `HttpsCallableReference`. Otherwise, if Swift creates a copy of the object 34 | // and deletes that copy, it will result in any pending `Call` being 35 | // interrupted or triggering memory corruption in the case that `Call` has 36 | // not completed. To avoid this and to prevent Swift from seeing the copy 37 | // constructor, wrap with a `std::shared_ptr`. 38 | return HttpsCallableRef(new ::firebase::functions::HttpsCallableReference( 39 | std::move(ref.get()->GetHttpsCallable(name)))); 40 | } 41 | 42 | inline ::swift_firebase::swift_cxx_shims::firebase::Future< 43 | ::firebase::functions::HttpsCallableResult> 44 | https_callable_call(HttpsCallableRef ref) { 45 | return ref.get()->Call(); 46 | } 47 | 48 | inline ::swift_firebase::swift_cxx_shims::firebase::Future< 49 | ::firebase::functions::HttpsCallableResult> 50 | https_callable_call(HttpsCallableRef ref, 51 | const ::firebase::Variant& data) { 52 | return ref.get()->Call(data); 53 | } 54 | 55 | inline ::firebase::Variant 56 | https_callable_result_data( 57 | const ::firebase::functions::HttpsCallableResult& result) { 58 | return result.data(); 59 | } 60 | 61 | } // namespace swift_firebase::swift_cxx_shims::firebase::functions 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /Sources/firebase/include/FirebaseLogging.hh: -------------------------------------------------------------------------------- 1 | 2 | #ifndef firebase_include_FirebaseLogging_hh 3 | #define firebase_include_FirebaseLogging_hh 4 | 5 | #include 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Sources/firebase/include/FirebaseStorage.hh: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | #ifndef firebase_include_FirebaseStorage_hh 4 | #define firebase_include_FirebaseStorage_hh 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "FirebaseCore.hh" 12 | 13 | namespace swift_firebase::swift_cxx_shims::firebase::storage { 14 | 15 | typedef std::shared_ptr<::firebase::storage::Storage> StorageRef; 16 | 17 | inline bool 18 | storage_is_valid(const StorageRef& ref) { 19 | return ref.operator bool(); 20 | } 21 | 22 | inline StorageRef 23 | storage_get_instance(::firebase::App* app) { 24 | return StorageRef(::firebase::storage::Storage::GetInstance(app)); 25 | } 26 | 27 | inline ::firebase::storage::StorageReference 28 | storage_get_reference(const StorageRef& ref, const char* path) { 29 | return ref->GetReference(path); 30 | } 31 | 32 | inline ::swift_firebase::swift_cxx_shims::firebase::Future<::std::string> 33 | storage_reference_get_download_url(::firebase::storage::StorageReference ref) { 34 | return ref.GetDownloadUrl(); 35 | } 36 | 37 | inline ::swift_firebase::swift_cxx_shims::firebase::Future<::firebase::storage::Metadata> 38 | storage_reference_put_bytes(::firebase::storage::StorageReference ref, 39 | const void* buffer, size_t buffer_size, 40 | ::firebase::storage::Controller* controller) { 41 | return ref.PutBytes(buffer, buffer_size, nullptr, controller); 42 | } 43 | 44 | inline ::swift_firebase::swift_cxx_shims::firebase::Future<::firebase::storage::Metadata> 45 | storage_reference_put_bytes(::firebase::storage::StorageReference ref, 46 | const void* buffer, size_t buffer_size, 47 | const ::firebase::storage::Metadata& metadata, 48 | ::firebase::storage::Controller* controller) { 49 | return ref.PutBytes(buffer, buffer_size, metadata, nullptr, controller); 50 | } 51 | 52 | typedef std::map CustomMetadata; 53 | 54 | inline CustomMetadata 55 | metadata_get_custom_metadata(const ::firebase::storage::Metadata& metadata) { 56 | return *metadata.custom_metadata(); 57 | } 58 | 59 | inline CustomMetadata::const_iterator 60 | custom_metadata_begin(const CustomMetadata& custom_metadata) { 61 | return custom_metadata.begin(); 62 | } 63 | 64 | inline CustomMetadata::const_iterator 65 | custom_metadata_end(const CustomMetadata& custom_metadata) { 66 | return custom_metadata.end(); 67 | } 68 | 69 | inline bool 70 | custom_metadata_iterators_equal(const CustomMetadata::const_iterator& a, 71 | const CustomMetadata::const_iterator& b) { 72 | return a == b; 73 | } 74 | 75 | inline const std::string& 76 | custom_metadata_iterator_first(const CustomMetadata::const_iterator& it) { 77 | it->first; 78 | } 79 | 80 | inline const std::string& 81 | custom_metadata_iterator_second(const CustomMetadata::const_iterator& it) { 82 | it->second; 83 | } 84 | 85 | inline void 86 | metadata_clear_custom_metadata(const ::firebase::storage::Metadata& metadata) { 87 | metadata.custom_metadata()->clear(); 88 | } 89 | 90 | inline void 91 | metadata_insert_custom_metadata(const ::firebase::storage::Metadata& metadata, 92 | const std::string& key, 93 | const std::string& value) { 94 | (*metadata.custom_metadata())[key] = value; 95 | } 96 | 97 | } // namespace swift_firebase::swift_cxx_shims::firebase::functions 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /Sources/firebase/include/TransactionWeakReference.hh: -------------------------------------------------------------------------------- 1 | #ifndef firebase_include_TransactionWeakReference_hh 2 | #define firebase_include_TransactionWeakReference_hh 3 | 4 | #include 5 | 6 | namespace swift_firebase::swift_cxx_shims::firebase::firestore { 7 | 8 | // Transaction is non-copyable so we need a wrapper type that is copyable. 9 | // This type will hold a valid Transaction during the scope of a RunTransaction 10 | // callback (see Firestore+Swift.swift for details). 11 | class TransactionWeakReference { 12 | public: 13 | ~TransactionWeakReference() = default; 14 | 15 | TransactionWeakReference(const TransactionWeakReference& other) 16 | : container_(other.container_) {} 17 | TransactionWeakReference& operator=(const TransactionWeakReference& other) { 18 | container_ = other.container_; 19 | return *this; 20 | } 21 | 22 | TransactionWeakReference(::firebase::firestore::Transaction* transaction = nullptr) 23 | : container_(std::make_shared(transaction)) {} 24 | void reset(::firebase::firestore::Transaction* transaction = nullptr) { 25 | container_->transaction = transaction; 26 | } 27 | 28 | bool is_valid() const { return container_->transaction != nullptr; } 29 | 30 | // API wrappers to access the underlying Transaction: 31 | 32 | void Set(const ::firebase::firestore::DocumentReference& document, 33 | const ::firebase::firestore::MapFieldValue& data, 34 | const ::firebase::firestore::SetOptions& options = 35 | ::firebase::firestore::SetOptions()) const { 36 | container_->transaction->Set(document, data, options); 37 | } 38 | 39 | void Update(const ::firebase::firestore::DocumentReference& document, 40 | const ::firebase::firestore::MapFieldValue& data) const { 41 | container_->transaction->Update(document, data); 42 | } 43 | 44 | void Update(const ::firebase::firestore::DocumentReference& document, 45 | const ::firebase::firestore::MapFieldPathValue& data) const { 46 | container_->transaction->Update(document, data); 47 | } 48 | 49 | void Delete(const ::firebase::firestore::DocumentReference& document) const { 50 | container_->transaction->Delete(document); 51 | } 52 | 53 | ::firebase::firestore::DocumentSnapshot Get( 54 | const ::firebase::firestore::DocumentReference& document, 55 | ::firebase::firestore::Error* error_code, 56 | std::string* error_message) const { 57 | return container_->transaction->Get(document, error_code, error_message); 58 | } 59 | 60 | private: 61 | struct Container { 62 | Container(::firebase::firestore::Transaction* transaction) 63 | : transaction(transaction) {} 64 | ::firebase::firestore::Transaction* transaction; 65 | }; 66 | std::shared_ptr container_; 67 | }; 68 | 69 | } // namespace swift_firebase::swift_cxx_shims::firebase::firestore 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /Sources/firebase/include/module.modulemap: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | 3 | module firebase [system] { 4 | requires cplusplus 5 | 6 | module app { 7 | header "FirebaseApp.hh" 8 | export * 9 | link "firebase_app" 10 | } 11 | 12 | module auth { 13 | header "FirebaseAuth.hh" 14 | export * 15 | link "firebase_auth" 16 | } 17 | 18 | module core { 19 | header "FirebaseCore.hh" 20 | export * 21 | } 22 | 23 | module logging { 24 | header "FirebaseLogging.hh" 25 | export * 26 | } 27 | 28 | module firestore { 29 | header "FirebaseFirestore.hh" 30 | export * 31 | link "firebase_firestore" 32 | } 33 | 34 | module functions { 35 | header "FirebaseFunctions.hh" 36 | export * 37 | } 38 | 39 | module storage { 40 | header "FirebaseStorage.hh" 41 | export * 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /patches/0001-Add-a-couple-of-workarounds-for-Swift-on-Windows.patch: -------------------------------------------------------------------------------- 1 | From 140b2a4d33c50deca8a1ab9284f1897c07a1ce52 Mon Sep 17 00:00:00 2001 2 | From: Saleem Abdulrasool 3 | Date: Thu, 3 Aug 2023 21:40:01 -0700 4 | Subject: [PATCH] Add a couple of workarounds for Swift on Windows 5 | 6 | The C++ Interop efforts in Swift currently have some limitations. In 7 | particular, it cannot support trivial types with non-trivial 8 | destructors. As a workaround, provide a copy constructor which can be 9 | used by the Swift interop while using the regular semantics for all 10 | other cases. 11 | 12 | A second issue arises in the handling of futures. Unfortunately, it is 13 | not currently possible to pass an indirect block parameter which 14 | prevents the construction of a callback. Workaround this by providing 15 | an inline shim to use a direct parameter (i.e. indirect value through a 16 | pointer) which then allows a callback to be formed. 17 | 18 | Both of these items are being tracked upstream but seem to be 19 | potentially sufficient to enable the use of Swift for using the C++ SDK 20 | for desktop scenarios. 21 | --- 22 | app/src/include/firebase/future.h | 15 +++++++++++++++ 23 | auth/CMakeLists.txt | 1 + 24 | auth/src/auth_swift.cc | 26 ++++++++++++++++++++++++++ 25 | auth/src/include/firebase/auth.h | 7 +++++++ 26 | 4 files changed, 49 insertions(+) 27 | create mode 100644 auth/src/auth_swift.cc 28 | 29 | diff --git a/app/src/include/firebase/future.h b/app/src/include/firebase/future.h 30 | index 0d09fc07..9e325e3d 100644 31 | --- a/app/src/include/firebase/future.h 32 | +++ b/app/src/include/firebase/future.h 33 | @@ -407,6 +407,11 @@ class Future : public FutureBase { 34 | /// when you set up the callback. 35 | typedef void (*TypedCompletionCallback)(const Future& result_data, 36 | void* user_data); 37 | +#if defined(__swift__) 38 | + // TODO(apple/swift#67662) indirect block parameters are unsupported 39 | + typedef void (*TypedCompletionCallback_SwiftWorkaround)(const Future* result_data, 40 | + void* user_data); 41 | +#endif 42 | 43 | /// Construct a future. 44 | Future() {} 45 | @@ -464,6 +469,16 @@ class Future : public FutureBase { 46 | inline void OnCompletion(TypedCompletionCallback callback, 47 | void* user_data) const; 48 | 49 | +#if defined(__swift__) 50 | + // TODO(apple/swift#67662) indirect block parameters are unsupported 51 | + inline void OnCompletion_SwiftWorkaround(TypedCompletionCallback_SwiftWorkaround callback, 52 | + void *user_data) const { 53 | + OnCompletion([callback, user_data](const Future& future) { 54 | + callback(&future, user_data); 55 | + }); 56 | + } 57 | +#endif 58 | + 59 | #if defined(FIREBASE_USE_STD_FUNCTION) || defined(DOXYGEN) 60 | /// Register a single callback that will be called at most once, when the 61 | /// future is completed. 62 | diff --git a/auth/CMakeLists.txt b/auth/CMakeLists.txt 63 | index 2dc7c4e2..8ee2a964 100644 64 | --- a/auth/CMakeLists.txt 65 | +++ b/auth/CMakeLists.txt 66 | @@ -51,6 +51,7 @@ build_flatbuffers("${flatbuffer_schemas}" 67 | # Common source files used by all platforms 68 | set(common_SRCS 69 | src/auth.cc 70 | + src/auth_swift.cc 71 | src/credential.cc 72 | src/common.cc 73 | src/common.h 74 | diff --git a/auth/src/auth_swift.cc b/auth/src/auth_swift.cc 75 | new file mode 100644 76 | index 00000000..63248c18 77 | --- /dev/null 78 | +++ b/auth/src/auth_swift.cc 79 | @@ -0,0 +1,26 @@ 80 | +/* 81 | + * Copyright 2016 Google LLC 82 | + * 83 | + * Licensed under the Apache License, Version 2.0 (the "License"); 84 | + * you may not use this file except in compliance with the License. 85 | + * You may obtain a copy of the License at 86 | + * 87 | + * http://www.apache.org/licenses/LICENSE-2.0 88 | + * 89 | + * Unless required by applicable law or agreed to in writing, software 90 | + * distributed under the License is distributed on an "AS IS" BASIS, 91 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 92 | + * See the License for the specific language governing permissions and 93 | + * limitations under the License. 94 | + */ 95 | + 96 | +#define __swift__ 97 | +#include "auth/src/include/firebase/auth.h" 98 | + 99 | +#if FIREBASE_PLATFORM_WINDOWS 100 | +namespace firebase { 101 | +namespace auth { 102 | +Auth::Auth(const Auth &) noexcept = default; 103 | +} 104 | +} 105 | +#endif 106 | diff --git a/auth/src/include/firebase/auth.h b/auth/src/include/firebase/auth.h 107 | index bec3ce8f..71bdaa12 100644 108 | --- a/auth/src/include/firebase/auth.h 109 | +++ b/auth/src/include/firebase/auth.h 110 | @@ -147,6 +147,13 @@ class Auth { 111 | 112 | ~Auth(); 113 | 114 | +#if defined(__swift__) 115 | +#if FIREBASE_PLATFORM_WINDOWS 116 | + // TODO(apple/swift#67288) support trivial C++ types with non-trivial dtors 117 | + Auth(const Auth &) noexcept; 118 | +#endif 119 | +#endif 120 | + 121 | /// Synchronously gets the cached current user, or returns an object where 122 | /// is_valid() == false if there is none. 123 | /// 124 | -- 125 | 2.39.2.windows.1 126 | 127 | --------------------------------------------------------------------------------