diff --git a/.github/workflows/docc.yml b/.github/workflows/docc.yml index 0b7c4d2..e045153 100644 --- a/.github/workflows/docc.yml +++ b/.github/workflows/docc.yml @@ -36,16 +36,23 @@ jobs: - uses: actions/checkout@v4 - name: Select Xcode run: sudo xcode-select -s "/Applications/$XCODE_VERSION.app" - - name: Generate DocC - run: xcodebuild docbuild -scheme OAuthKit -derivedDataPath /tmp/docbuild -destination 'generic/platform=macOS'; - - name: Transform DocC + - name: Add Plugin Dependency + run: swift package add-dependency "https://github.com/apple/swift-docc-plugin" --branch main + - name: Generate Documentation run: | - $(xcrun --find docc) process-archive \ - transform-for-static-hosting /tmp/docbuild/Build/Products/Debug/OAuthKit.doccarchive \ + swift package \ + --allow-writing-to-package-directory \ + --allow-writing-to-directory ${{github.workspace}}/docs \ + generate-documentation \ + --target OAuthKit \ + --transform-for-static-hosting \ --hosting-base-path OAuthKit \ - --output-path docs; + --output-path ${{github.workspace}}/docs \ + --source-service github \ + --source-service-base-url https://github.com/codefiesta/OAuthKit/blob/main \ + --checkout-path ${{github.workspace}}; - name: Update Index - run: echo "" > docs/index.html; + run: echo "" > ${{github.workspace}}/docs/index.html; - name: Upload Pages Artifact uses: actions/upload-pages-artifact@v3 with: diff --git a/README.md b/README.md index e0d1c28..47adde4 100644 --- a/README.md +++ b/README.md @@ -17,15 +17,15 @@ OAuthKit is a contemporary, event-driven Swift Package that utilizes the [Observ ## OAuthKit Features -OAuthKit is a small, lightweight package that provides out of the box [Swift Concurrency](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/) safety support and [Observable](https://github.com/codefiesta/OAuthKit/blob/main/Sources/OAuthKit/OAuth.swift#L36) OAuth 2.0 state events that allow fine grained control over when and how to start authorization flows. +OAuthKit is a small, lightweight package that provides out of the box [Swift Concurrency](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/) safety support and [Observable](https://codefiesta.github.io/OAuthKit/documentation/oauthkit/oauth/state-swift.enum) OAuth 2.0 state events that allow fine grained control over when and how to start authorization flows. Key features include: - [Simple Configuration](#oauthkit-configuration) -- [Keychain protection with biometrics or companion device](#oauth-initialized-with-keychain-protection-and-private-browsing) -- [Private Browsing with non-persistent WebKit Datastores](#oauth-initialized-with-keychain-protection-and-private-browsing) -- [Custom URLSession](#oauth-initialized-with-keychain-protection-and-private-browsing) configuration for complete control custom protocol specific data -- [Observable State](https://github.com/codefiesta/OAuthKit/blob/main/Sources/OAuthKit/OAuth.swift#L36) driven events to allow full control over when and if users are prompted to authenticate with an OAuth provider +- [Keychain protection with biometrics or companion device](https://codefiesta.github.io/OAuthKit/documentation/oauthkit/configuration#Keychain-Protection) +- [Private Browsing with non-persistent WebKit Datastores](https://codefiesta.github.io/OAuthKit/documentation/oauthkit/configuration#Private-Browsing) +- [Custom URLSession](https://codefiesta.github.io/OAuthKit/documentation/oauthkit/configuration#URL-Session) configuration for complete control custom protocol specific data +- [Observable State](https://codefiesta.github.io/OAuthKit/documentation/oauthkit/gettingstarted#Observing-OAuth-State) driven events to allow full control over when and if users are prompted to authenticate with an OAuth provider - Supports all Apple Platforms (iOS, macOS, tvOS, visionOS, watchOS) - [Support for every OAuth 2.0 Flow](#oauthkit-configuration) - [Authorization Code](#oauth-20-authorization-code-flow) @@ -160,7 +160,7 @@ struct ContentView: View { ``` ## OAuthKit Configuration -By default, the easiest way to configure OAuthKit is to simply drop an `oauth.json` file into your main bundle and it will get automatically loaded into your swift application and available as an [Environment](https://developer.apple.com/documentation/swiftui/environment) property wrapper. You can find an example `oauth.json` file [here](https://github.com/codefiesta/OAuthKit/blob/main/Tests/OAuthKitTests/Resources/oauth.json). OAuthKit provides flexible constructor options that allows developers to customize how their oauth client is initialized and what features they want to implement. See the [oauth.init(\_:bundle:options)](https://github.com/codefiesta/OAuthKit/blob/main/Sources/OAuthKit/OAuth.swift#L98) method for details. +By default, the easiest way to configure OAuthKit is to simply drop an `oauth.json` file into your main bundle and it will get automatically loaded into your swift application and available as an [Environment](https://developer.apple.com/documentation/swiftui/environment) property wrapper. You can find an example `oauth.json` file [here](https://github.com/codefiesta/OAuthKit/blob/main/Tests/OAuthKitTests/Resources/oauth.json). OAuthKit provides flexible constructor options that allows developers to customize how their oauth client is initialized and what features they want to implement. See the [oauth.init(\_:bundle:options)](https://codefiesta.github.io/OAuthKit/documentation/oauthkit/oauth) method for details. ### OAuth initialized from main bundle (default) ```swift @@ -222,7 +222,7 @@ let oauth: OAuth = .init(.main, options: options) ``` ## OAuthKit Authorization Flows -OAuth 2.0 authorization flows are started by calling the [oauth.authorize(provider:grantType:)](https://github.com/codefiesta/OAuthKit/blob/main/Sources/OAuthKit/OAuth.swift#L117) method. +OAuth 2.0 authorization flows are started by calling the [oauth.authorize(provider:grantType:)](https://codefiesta.github.io/OAuthKit/documentation/oauthkit/oauth#Starting-an-authorization-flow) method. A good resource to help understand the detailed steps involved in OAuth 2.0 authorization flows can be found on the [OAuth 2.0 Playground](https://www.oauth.com/playground/index.html). diff --git a/Sources/OAuthKit/OAuth+State.swift b/Sources/OAuthKit/OAuth+State.swift index 87031c0..cb3df55 100644 --- a/Sources/OAuthKit/OAuth+State.swift +++ b/Sources/OAuthKit/OAuth+State.swift @@ -10,43 +10,6 @@ import Foundation extension OAuth { /// Holds the OAuth state that is published to subscribers via the ``state`` property. - /// - /// A n example of observing the ``state`` property in SwiftUI: - /// ```swift - ///struct ContentView: View { - /// - /// @Environment(\.oauth) - /// var oauth: OAuth - /// - /// var body: some View { - /// VStack { - /// switch oauth.state { - /// case .empty: - /// providerList - /// case .authorizing(let provider, let grantType): - /// Text("Authorizing [\(provider.id)] with [\(grantType.rawValue)]") - /// case .requestingAccessToken(let provider): - /// Text("Requesting Access Token [\(provider.id)]") - /// case .requestingDeviceCode(let provider): - /// Text("Requesting Device Code [\(provider.id)]") - /// case .authorized(let provider, _): - /// Button("Authorized [\(provider.id)]") { - /// oauth.clear() - /// } - /// case .receivedDeviceCode(_, let deviceCode): - /// Text("To login, visit") - /// Text(.init("[\(deviceCode.verificationUri)](\(deviceCode.verificationUri))")) - /// .foregroundStyle(.blue) - /// Text("and enter the following code:") - /// Text(deviceCode.userCode) - /// } - /// } - /// .onChange(of: oauth.state) { oldState, newState in - /// handle(state: newState) - /// } - /// } - /// } - /// ``` public enum State: Equatable, Sendable { /// The state is empty and no authorizations or tokens have been issued. diff --git a/Sources/OAuthKit/OAuthKit.docc/Extensions/State.md b/Sources/OAuthKit/OAuthKit.docc/Extensions/State.md new file mode 100644 index 0000000..097bd55 --- /dev/null +++ b/Sources/OAuthKit/OAuthKit.docc/Extensions/State.md @@ -0,0 +1,48 @@ +# ``OAuthKit/OAuth/State`` + +@Metadata { + @Available(iOS, introduced: "18.0") + @Available(macOS, introduced: "15.0") + @Available(tvOS, introduced: "18.0") + @Available(visionOS, introduced: "2.0") + @Available(watchOS, introduced: "11.0") +} + +## Overview +An example of observing the ``state`` property in SwiftUI: + +```swift +struct ContentView: View { + + @Environment(\.oauth) + var oauth: OAuth + + var body: some View { + VStack { + switch oauth.state { + case .empty: + providerList + case .authorizing(let provider, let grantType): + Text("Authorizing [\(provider.id)] with [\(grantType.rawValue)]") + case .requestingAccessToken(let provider): + Text("Requesting Access Token [\(provider.id)]") + case .requestingDeviceCode(let provider): + Text("Requesting Device Code [\(provider.id)]") + case .authorized(let provider, _): + Button("Authorized [\(provider.id)]") { + oauth.clear() + } + case .receivedDeviceCode(_, let deviceCode): + Text("To login, visit") + Text(.init("[\(deviceCode.verificationUri)](\(deviceCode.verificationUri))")) + .foregroundStyle(.blue) + Text("and enter the following code:") + Text(deviceCode.userCode) + } + } + .onChange(of: oauth.state) { oldState, newState in + handle(state: newState) + } + } +} +``` diff --git a/Sources/OAuthKit/OAuthKit.docc/OAuthKit.md b/Sources/OAuthKit/OAuthKit.docc/OAuthKit.md index 36a5547..6338fe2 100644 --- a/Sources/OAuthKit/OAuthKit.docc/OAuthKit.md +++ b/Sources/OAuthKit/OAuthKit.docc/OAuthKit.md @@ -41,4 +41,5 @@ This has the advantage of avoiding direct coupling with an authorization flow, e - ``OAuth/GrantType`` ### State Observability +- ``OAuth/State`` - ``OAuth/state``