-
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enable a Swift Package Index-based Documentation Hosting (#35)
Co-authored-by: Vishnu Ravi <vishnu.ravi@gmail.com>
- Loading branch information
1 parent
97269e8
commit f9afdce
Showing
52 changed files
with
1,030 additions
and
151 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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`` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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`` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.