Skip to content

Commit

Permalink
Add documentation catalogs for all targets (#26)
Browse files Browse the repository at this point in the history
# Add documentation catalogs for all targets

## ♻️ Current situation & Problem
This PR adds documentation catalogs for all targets and upgrades all
dependencies to their latest 1.0 releases.


## ⚙️ Release Notes 
* Add documentation catalogs for all targets.
* Upgrade all dependencies to their latest 1.0 releases.
* Fixed an issue where Account association was removed when no network
coverage is available


## 📝 Code of Conduct & Contributing Guidelines 

By submitting creating this pull request, you agree to follow our [Code
of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md):
- [x] I agree to follow the [Code of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md).
  • Loading branch information
Supereg authored Jan 10, 2024
1 parent db46d60 commit ca1edf6
Show file tree
Hide file tree
Showing 15 changed files with 302 additions and 37 deletions.
8 changes: 4 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ let package = Package(
.library(name: "SpeziFirebaseAccountStorage", targets: ["SpeziFirebaseAccountStorage"])
],
dependencies: [
.package(url: "https://github.com/StanfordSpezi/Spezi", .upToNextMinor(from: "0.8.0")),
.package(url: "https://github.com/StanfordSpezi/SpeziViews.git", .upToNextMinor(from: "0.6.1")),
.package(url: "https://github.com/StanfordSpezi/SpeziAccount", .upToNextMinor(from: "0.8.1")),
.package(url: "https://github.com/StanfordSpezi/SpeziStorage", .upToNextMinor(from: "0.5.0")),
.package(url: "https://github.com/StanfordSpezi/Spezi", from: "1.0.0"),
.package(url: "https://github.com/StanfordSpezi/SpeziViews.git", from: "1.0.0"),
.package(url: "https://github.com/StanfordSpezi/SpeziStorage", from: "1.0.0"),
.package(url: "https://github.com/StanfordSpezi/SpeziAccount", from: "1.0.0"),
.package(url: "https://github.com/firebase/firebase-ios-sdk", from: "10.13.0")
],
targets: [
Expand Down
77 changes: 75 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,87 @@ SPDX-License-Identifier: MIT
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FStanfordSpezi%2FSpeziFirebase%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/StanfordSpezi/SpeziFirebase)
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FStanfordSpezi%2FSpeziFirebase%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/StanfordSpezi/SpeziFirebase)

Google Firebase modules for the [Spezi framework](https://github.com/StanfordSpezi/Spezi).
Integrate Google Firebase services into your Spezi application.

## Overview

This Module allows you to use the [Google Firebase](https://firebase.google.com/) platform as a managed backend for
authentication and data storage in your apps built with the [Spezi framework](https://github.com/StanfordSpezi/Spezi).

We currently implement support for Authentication, Storage, and Firestore services.

## Setup

You need to add the Spezi Firebase Swift package to
[your app in Xcode](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app#) or
[Swift package](https://developer.apple.com/documentation/xcode/creating-a-standalone-swift-package-with-xcode#Add-a-dependency-on-another-Swift-package).

> [!IMPORTANT]
> If your application is not yet configured to use Spezi, follow the [Spezi setup article](https://swiftpackageindex.com/stanfordspezi/spezi/documentation/spezi/initial-setup)#
to set up the core Spezi infrastructure.


## Examples

The below section walks you through the necessary steps to set up the Spezi Firebase Module for your application.

### 1. Set up your Firebase Account

To connect your app to the Firebase cloud platform, you will need to first create an account at
[firebase.google.com](https://firebase.google.com) then start the process to
[register a new iOS app](https://firebase.google.com/docs/ios/setup).

Once your Spezi app is registered with Firebase, place the generated `GoogleService-Info.plist` configuration file
into the root of your Xcode project.
You do not need to add the Firebase SDKs to your app or initialize Firebase in your app,
since the Spezi Firebase Module will handle these tasks for you.

You can also install and run the Firebase Local Emulator Suite for local development.
To do this, please follow the [installation instructions](https://firebase.google.com/docs/emulator-suite/install_and_configure).

### 2. Add Spezi Firebase as a Dependency

First, you will need to add the SpeziFirebase Swift package to
[your app in Xcode](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app#) or
[Swift package](https://developer.apple.com/documentation/xcode/creating-a-standalone-swift-package-with-xcode#Add-a-dependency-on-another-Swift-package).

### 3. Register the Spezi Firebase Modules

In the example below, we configure our Spezi application to use Firebase Authentication with both email & password login
and Sign in With Apple, and Cloud Firestore for data storage.

```swift
import Spezi
import SpeziAccount
import SpeziFirebaseAccount
import SpeziFirebaseStorage
import SpeziFirestore
import SwiftUI


class ExampleDelegate: SpeziAppDelegate {
override var configuration: Configuration {
Configuration {
AccountConfiguration(configuration: [
.requires(\.userId),
.collects(\.name)
])
Firestore()
FirebaseAccountConfiguration[
authenticationMethods: [.emailAndPassword, .signInWithApple]
]
}
}
}
```

For more information, please refer to the [API documentation](https://swiftpackageindex.com/StanfordSpezi/SpeziFirebase/documentation).


## The Spezi Template Application

The [Spezi Template Application](https://github.com/StanfordSpezi/SpeziTemplateApplication) provides a great starting point and example using the Spezi Firebase Modules.
The Spezi Firebase Module comes pre-configured in the [Spezi Template Application](https://github.com/StanfordSpezi/SpeziTemplateApplication),
which is a great way to get started on your Spezi Application.


## Contributing
Expand Down
11 changes: 3 additions & 8 deletions Sources/SpeziFirebaseAccount/FirebaseAccountConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,13 @@ import SpeziLocalStorage
import SpeziSecureStorage


/// Configures Firebase Auth `AccountService`s that can be used in any views of the `Account` module.
/// Configures an `AccountService` to interact with Firebase Auth.
///
/// The ``FirebaseAccountConfiguration`` offers a ``user`` property to access the current Firebase Auth user from, e.g., a SwiftUI view's environment:
/// ```
/// @EnvironmentObject var firebaseAccountConfiguration: FirebaseAccountConfiguration</* ... */>
/// ```
///
/// The ``FirebaseAccountConfiguration`` can, e.g., be used to to connect to the Firebase Auth emulator:
/// The `FirebaseAccountConfiguration` can, e.g., be used to to connect to the Firebase Auth emulator:
/// ```
/// class ExampleAppDelegate: SpeziAppDelegate {
/// override var configuration: Configuration {
/// Configuration(standard: /* ... */) {
/// Configuration {
/// FirebaseAccountConfiguration(emulatorSettings: (host: "localhost", port: 9099))
/// // ...
/// }
Expand Down
8 changes: 7 additions & 1 deletion Sources/SpeziFirebaseAccount/Models/FirebaseContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@ actor FirebaseContext {

// if there is a cached user, we refresh the authentication token
Auth.auth().currentUser?.getIDTokenForcingRefresh(true) { _, error in
if error != nil {
if let error {
let code = AuthErrorCode(_nsError: error as NSError)

guard code.code != .networkError else {
return // we make sure that we don't remove the account when we don't have network (e.g., flight mode)
}

Task {
try await self.notifyUserRemoval(for: self.lastActiveAccountService)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# ``SpeziFirebaseAccount``

<!--
This source file is part of the Stanford Spezi open-source project
SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
SPDX-License-Identifier: MIT
-->

Firebase Auth support for SpeziAccount.

## Overview

This Module adds support for Firebase Auth for SpeziAccount by implementing a respective
[AccountService](https://swiftpackageindex.com/stanfordspezi/speziaccount/documentation/speziaccount/accountservice).

The `FirebaseAccountConfiguration` can, e.g., be used to to connect to the Firebase Auth emulator:
```
class ExampleAppDelegate: SpeziAppDelegate {
override var configuration: Configuration {
Configuration {
FirebaseAccountConfiguration(emulatorSettings: (host: "localhost", port: 9099))
// ...
}
}
}
```

## Topics

### Firebase Account

- ``FirebaseAccountConfiguration``
- ``FirebaseAuthAuthenticationMethods``

### Account Keys

- ``FirebaseEmailVerifiedKey``
- ``SpeziAccount/AccountValues/isEmailVerified``
- ``SpeziAccount/AccountKeys/isEmailVerified``

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import SpeziAccount
import SpeziFirestore


/// Store additional account details directly in firestore.
/// Store additional account details directly in Firestore.
///
/// Certain account services, like the account services provided by Firebase, can only store certain account details.
/// The `FirestoreAccountStorage` can be used to store additional account details, that are not supported out of the box by your account services,
Expand All @@ -23,7 +23,7 @@ import SpeziFirestore
///
/// Once you have [AccountConfiguration](https://swiftpackageindex.com/stanfordspezi/speziaccount/documentation/speziaccount/initial-setup#Account-Configuration)
/// and the [FirebaseAccountConfiguration](https://swiftpackageindex.com/stanfordspezi/spezifirebase/documentation/spezifirebaseaccount/firebaseaccountconfiguration)
/// set up, you can adopt the [AccountStorageStandard](https://swiftpackageindex.com/stanfordspezi/speziaccount/documentation/speziaccount/accountstoragestandard)
/// set up, you can adopt the [AccountStorageConstraint](https://swiftpackageindex.com/stanfordspezi/speziaccount/documentation/speziaccount/accountstorageconstraint)
/// protocol to provide a custom storage for SpeziAccount.
///
/// - Important: In order to use the `FirestoreAccountStorage`, you must have [Firestore](https://swiftpackageindex.com/stanfordspezi/spezifirebase/main/documentation/spezifirestore/firestore)
Expand All @@ -36,7 +36,7 @@ import SpeziFirestore
/// import SpeziFirebaseAccountStorage
///
///
/// actor ExampleStandard: Standard, AccountStorageStandard {
/// actor ExampleStandard: Standard, AccountStorageConstraint {
/// // Define the collection where you want to store your additional user data, ...
/// static var collection: CollectionReference {
/// Firestore.firestore().collection("users")
Expand All @@ -46,7 +46,7 @@ import SpeziFirestore
/// @Dependency private var accountStorage = FirestoreAccountStorage(storedIn: Self.collection)
///
///
/// // ... and forward all implementations of `AccountStorageStandard` to the `FirestoreAccountStorage`.
/// // ... and forward all implementations of `AccountStorageConstraint` to the `FirestoreAccountStorage`.
///
/// public func create(_ identifier: AdditionalRecordId, _ details: SignupDetails) async throws {
/// try await accountStorage.create(identifier, details)
Expand All @@ -69,7 +69,7 @@ import SpeziFirestore
/// }
/// }
/// ```
public actor FirestoreAccountStorage: Module, AccountStorageStandard {
public actor FirestoreAccountStorage: Module, AccountStorageConstraint {
@Dependency private var firestore: SpeziFirestore.Firestore // ensure firestore is configured

private let collection: () -> CollectionReference
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# ``SpeziFirebaseAccountStorage``

<!--
This source file is part of the Stanford Spezi open-source project
SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
SPDX-License-Identifier: MIT
-->

Store additional account details directly in Firestore.

## Overview

Certain account services, like the account services provided by Firebase, can only store certain account details.
The ``FirestoreAccountStorage`` can be used to store additional account details, that are not supported out of the box by your account services,
inside Firestore in a custom user collection.

For more detailed information, refer to the documentation of ``FirestoreAccountStorage``.

### Example

Once you have [AccountConfiguration](https://swiftpackageindex.com/stanfordspezi/speziaccount/documentation/speziaccount/initial-setup#Account-Configuration)
and the [FirebaseAccountConfiguration](https://swiftpackageindex.com/stanfordspezi/spezifirebase/documentation/spezifirebaseaccount/firebaseaccountconfiguration)
set up, you can adopt the [AccountStorageConstraint](https://swiftpackageindex.com/stanfordspezi/speziaccount/documentation/speziaccount/accountstorageconstraint)
protocol to provide a custom storage for SpeziAccount.


```swift
import FirebaseFirestore
import Spezi
import SpeziAccount
import SpeziFirebaseAccountStorage


actor ExampleStandard: Standard, AccountStorageConstraint {
// Define the collection where you want to store your additional user data, ...
static var collection: CollectionReference {
Firestore.firestore().collection("users")
}

// ... define and initialize the `FirestoreAccountStorage` dependency ...
@Dependency private var accountStorage = FirestoreAccountStorage(storedIn: Self.collection)


// ... and forward all implementations of `AccountStorageConstraint` to the `FirestoreAccountStorage`.

public func create(_ identifier: AdditionalRecordId, _ details: SignupDetails) async throws {
try await accountStorage.create(identifier, details)
}

public func load(_ identifier: AdditionalRecordId, _ keys: [any AccountKey.Type]) async throws -> PartialAccountDetails {
try await accountStorage.load(identifier, keys)
}

public func modify(_ identifier: AdditionalRecordId, _ modifications: AccountModifications) async throws {
try await accountStorage.modify(identifier, modifications)
}

public func clear(_ identifier: AdditionalRecordId) async {
await accountStorage.clear(identifier)
}

public func delete(_ identifier: AdditionalRecordId) async throws {
try await accountStorage.delete(identifier)
}
}
```

## Topics

### Storage

- ``FirestoreAccountStorage``
8 changes: 4 additions & 4 deletions Sources/SpeziFirebaseConfiguration/ConfigureFirebaseApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import FirebaseCore
import Spezi


/// Shared component to serve as a single point to configure the Firebase set of dependencies.
/// Module to configure the Firebase set of dependencies.
///
/// The ``configure()`` method calls `FirebaseApp.configure()`.
/// Use the `@Dependency` property wrapper to define a dependency on this component and ensure that `FirebaseApp.configure()` is called before any
/// other Firebase-related components:
/// Use the `@Dependency` property wrapper to define a dependency on this module and ensure that `FirebaseApp.configure()` is called before any
/// other Firebase-related modules:
/// ```swift
/// public final class YourFirebaseComponent<ComponentStandard: Standard>: Component {
/// public final class YourFirebaseModule: Module {
/// @Dependency private var configureFirebaseApp: ConfigureFirebaseApp
///
/// // ...
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# ``FirebaseConfiguration``
# ``SpeziFirebaseConfiguration``

<!--
Expand All @@ -10,17 +10,17 @@ SPDX-License-Identifier: MIT
-->

Shared component to serve as a single point to configure the Firebase set of dependencies.
Module to configure the Firebase set of dependencies.

## Overview

The `configure()` method calls `FirebaseApp.configure()`.
The ``ConfigureFirebaseApp/configure()`` method calls `FirebaseApp.configure()`.

Use the `@Dependency` property wrapper to define a dependency on this component and ensure that `FirebaseApp.configure()` is called before any
other Firebase-related components:
Use the `@Dependency` property wrapper to define a dependency on this module and ensure that `FirebaseApp.configure()` is called before any
other Firebase-related modules:

```swift
public final class YourFirebaseComponent<ComponentStandard: Standard>: Component {
public final class YourFirebaseModule: Module {
@Dependency private var configureFirebaseApp: ConfigureFirebaseApp

// ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import SpeziFirebaseConfiguration
/// ```
/// class ExampleAppDelegate: SpeziAppDelegate {
/// override var configuration: Configuration {
/// Configuration(standard: /* ... */) {
/// Configuration {
/// FirebaseStorageConfiguration(emulatorSettings: (host: "localhost", port: 9199))
/// // ...
/// }
Expand Down
Loading

0 comments on commit ca1edf6

Please sign in to comment.