Skip to content

Commit

Permalink
Enable a Swift Package Index-based Documentation Hosting (#35)
Browse files Browse the repository at this point in the history
Co-authored-by: Vishnu Ravi <vishnu.ravi@gmail.com>
  • Loading branch information
PSchmiedmayer and vishnuravi authored Jan 15, 2023
1 parent 97269e8 commit f9afdce
Show file tree
Hide file tree
Showing 52 changed files with 1,030 additions and 151 deletions.
24 changes: 24 additions & 0 deletions .spi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#
# This source file is part of the HealthKitOnFHIR open source project
#
# SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)
#
# SPDX-License-Identifier: MIT
#

version: 1
builder:
configs:
- platform: ios
documentation_targets:
- Account
- CardinalKit
- Contact
- FHIR
- HealthKitDataSource
- LocalStorage
- Onboarding
- Scheduler
- SecureStorage
- Views
- XCTRuntimeAssertions
149 changes: 149 additions & 0 deletions Sources/Account/Account.docc/Account.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# ``Account``

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

Provides account-related functionalty including login, sign up, and reset password functionality.

## The Account Actor

The Account module provides several UI components that are powered using the ``Account/Account`` actor that must be injected into the SwiftUI environment.
Views like the ``Login`` and ``SignUp`` views use the ``Account/Account``'s ``AccountService``s provided in the initializer (``Account/Account/init(accountServices:)``) to
populate the views.

More specialized views like the ``UsernamePasswordLoginView``, ``UsernamePasswordSignUpView``, ``UsernamePasswordResetPasswordView`` which are automatically provided if you use the
``Login`` view. It uses individual ``AccountService``s like the ``UsernamePasswordAccountService`` to populate the view content and react to user interactions.

You can use the following example using a CardinalKit Component and the configuration as a mechanism to inject the ``Account/Account`` actor into the SwiftUI
environment. Alternatively, you can use the `environmentObject(_:)` view modifier to manually inject the ``Account/Account`` actor into the SwiftUI environment.


The following example shows a CardinalKit component that creates a `User` class/actor defined within the project to store user-related information and passes it down to an
`ExampleUsernamePasswordAccountService` ``AccountService`` that can then modify the `User` instance based on the login or sign up procedure.
The `Component` injects the `Account` and `User` instances into the SwiftUI environment so they can be used by SwiftUI views:
```swift
final class ExampleAccountConfiguration<ComponentStandard: Standard>: Component, ObservableObjectProvider {
private let account: Account
private let user: User


var observableObjects: [any ObservableObject] {
[
account,
user
]
}


init() {
self.user = User()
let accountServices: [any AccountService] = [
ExampleUsernamePasswordAccountService(user: user)
]
self.account = Account(accountServices: accountServices)
}
}
```

The `ExampleAccountConfiguration` must be added to an instance of `CardinalKitAppDelegate` to inform CardinalKit about your configuration and allow CardinalKit
to take the `observableObjects`, including the ``Account/Account`` actor, into the SwiftUI environment to make it available for your custom views
and, e.g., the ``Login`` and ``SignUp`` views:

```swift
class TestAppDelegate: CardinalKitAppDelegate {
override var configuration: Configuration {
Configuration(standard: TestAppStandard()) {
ExampleAccountConfiguration()
// ...
}
}
}
```

## Account Views

You can then access the ``Account/Account`` actor in any SwiftUI view.
``AccountService``s must set the ``Account/Account/signedIn`` property to true if the user is signed in, making it possible for your to change your UI based on the
user's sign in status.

The following example shows an example view that uses the ``Account/Account`` actor to observe the user sign in state and offers the possibility to login or sign up
if the user is not signed in using a SwiftUI sheet to display the ``Login`` and ``SignUp`` views:
```swift
struct AccountExampleView: View {
@EnvironmentObject var account: Account
@State var showLogin = false
@State var showSignUp = false


var body: some View {
List {
if account.signedIn {
Text("You are signed in!")
} else {
Button("Login") {
showLogin.toggle()
}
Button("SignUp") {
showSignUp.toggle()
}
}
}
.sheet(isPresented: $showLogin) {
NavigationStack {
Login()
}
}
.sheet(isPresented: $showSignUp) {
NavigationStack {
SignUp()
}
}
.onChange(of: account.signedIn) { signedIn in
if signedIn {
showLogin = false
showSignUp = false
}
}
}
}
```

## Topics

### Account Services

- <doc:CreateAnAccountService>
- ``Account/AccountService``
- ``Account/Account``

### Views

Views that can be used to provide login, sign up, and reset password flows using the defined ``Account/AccountService``s.

- ``Account/SignUp``
- ``Account/Login``

### Username And Password Account Service

The ``UsernamePasswordAccountService`` provides a starting point for a username and password-based ``AccountService`` that can be subclassed or extended
to fit the need of the specific application. The ``UsernamePasswordSignUpView``, ``UsernamePasswordLoginView``, and ``UsernamePasswordResetPasswordView``
all rely on the ``UsernamePasswordAccountService`` to be present in the SwiftUI environment.

- ``UsernamePasswordAccountService``
- ``UsernamePasswordSignUpView``
- ``UsernamePasswordLoginView``
- ``UsernamePasswordResetPasswordView``

### Email And Password Account Service

The ``EmailPasswordAccountService`` is a ``UsernamePasswordAccountService`` subclass providing email-related validation rules and
customized view buttons enabling the creation of an email and password-based ``AccountService``.

- ``EmailPasswordAccountService``
167 changes: 167 additions & 0 deletions Sources/Account/Account.docc/CreateAnAccountService.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Create an Account Service

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

Account services describe the mechanism for account management components to display login, signUp, and account-related UI elements.

## Create Your Account Service

You can create new account services by conforming to the ``AccountService`` protocol.
An ``AccountService`` has to provide an ``AccountService/loginButton`` and ``AccountService/signUpButton-8r2hk`` that is used in the
``Login`` and ``SignUp`` views.

The ``AccountService/inject(account:)`` function provides an ``AccountService`` to get access to the ``Account/Account`` actor.
An ``AccountService`` generally stores the ``Account/Account`` actor using a weak reference to avoid reference cycles.

The following example demonstrates an example of an ``AccountService``:
```swift
class ExampleAccountService: @unchecked Sendable, AccountService, ObservableObject {
weak var account: Account?


var loginButton: AnyView {
AnyView(
NavigationLink {
Text("Your Login View ...")
.environmentObject(self)
} label: {
Text("Login")
}
)
}

var signUpButton: AnyView {
AnyView(
NavigationLink {
Text("Your Sign Up View ...")
.environmentObject(self)
} label: {
Text("Sign Up")
}
)
}


public init() { }


func inject(account: Account) {
self.account = account
}

func login(/* ... */) async throws { }

func signUp(/* ... */) async throws { }
}
```

## Username Password-based Account Services

The ``UsernamePasswordAccountService`` provides a starting point for a username and password-based ``AccountService`` that can be subclassed or extended
to fit the need of the specific application. The ``UsernamePasswordSignUpView``, ``UsernamePasswordLoginView``, and ``UsernamePasswordResetPasswordView``
all rely on the ``UsernamePasswordAccountService`` to be present in the SwiftUI environment.

The following example uses the `User` type defined below to login and sign up a user.
```swift
actor User: ObservableObject {
@MainActor @Published var username: String?
@MainActor @Published var name = PersonNameComponents()
@MainActor @Published var gender: GenderIdentity?
@MainActor @Published var dateOfBirth: Date?


init(
username: String? = nil,
name: PersonNameComponents = PersonNameComponents(),
gender: GenderIdentity? = nil,
dateOfBirth: Date? = nil
) {
Task { @MainActor in
self.username = username
self.name = name
self.gender = gender
self.dateOfBirth = dateOfBirth
}
}
}
```

Subclassing ``UsernamePasswordAccountService`` enables a built-in functionality to handle username and password-related sign up and login functionality.
The following example demonstrates subclassing the ``UsernamePasswordAccountService`` with custom login and sign up functions.
```swift
class ExampleUsernamePasswordAccountService: UsernamePasswordAccountService {
let user: User


init(user: User) {
self.user = user
super.init()
}


override func login(username: String, password: String) async throws {
try await Task.sleep(for: .seconds(5))

guard username == "lelandstanford", password == "StanfordRocks123!" else {
throw MockAccountServiceError.wrongCredentials
}

await MainActor.run {
account?.signedIn = true
user.username = username
}
}


override func signUp(signUpValues: SignUpValues) async throws {
try await Task.sleep(for: .seconds(5))

guard signUpValues.username != "lelandstanford" else {
throw MockAccountServiceError.usernameTaken
}

await MainActor.run {
account?.signedIn = true
user.username = signUpValues.username
user.name = signUpValues.name
user.dateOfBirth = signUpValues.dateOfBirth
user.gender = signUpValues.genderIdentity
}
}

override func resetPassword(username: String) async throws {
try await Task.sleep(for: .seconds(5))
}
}

```

## Build-in Account Services

### Account Service

- ``AccountService``

### Username And Password Account Service

The ``UsernamePasswordAccountService`` provides a username and password-account management.

- ``UsernamePasswordAccountService``
- ``UsernamePasswordSignUpView``
- ``UsernamePasswordLoginView``
- ``UsernamePasswordResetPasswordView``

### Email And Password Account Service

The ``EmailPasswordAccountService`` is a ``UsernamePasswordAccountService`` subclass providing email related validation rules and
customized view buttons enabeling the creation of an email and password-based ``AccountService``.

- ``EmailPasswordAccountService``
4 changes: 3 additions & 1 deletion Sources/Account/Account.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import CardinalKit
import SwiftUI


/// The ``Account`` type is used to store and inject account-related information into the SwiftUI View hieray and enables interaction with the ``AccountService``s.
/// Account-related CardinalKit module managing a collection of ``AccountService``s.
///
/// The ``Account/Account`` type also enables interaction with the ``AccountService``s from anywhere in the view hierachy.
public actor Account: ObservableObject {
/// The ``Account/Account/signedIn`` determines if the the current Account context is signed in or not yet signed in.
@MainActor @Published
Expand Down
8 changes: 5 additions & 3 deletions Sources/Account/AccountService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
import SwiftUI


/// An ``AccountService`` describes the mechanism for account management components to display login, signUp, and account-related UI elements
/// Describe the mechanism for account management components to display login, signUp, and account-related UI elements.
///
/// You can learn more about creating an account service at: <doc:CreateAnAccountService>.
public protocol AccountService: Sendable, AnyObject, Identifiable {
/// A `View` erased as an `AnyView` that will be displayd in login-related user interfaces
/// A `View` erased as an `AnyView` that will be displayd in login-related user interfaces.
var loginButton: AnyView { get }
/// A `View` erased as an `AnyView` that will be displayd in sign up-related user interfaces
/// A `View` erased as an `AnyView` that will be displayd in sign up-related user interfaces.
var signUpButton: AnyView { get }


Expand Down
Loading

0 comments on commit f9afdce

Please sign in to comment.