> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chappiesdk.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Add a Sign In with ChatGPT Button to Your SwiftUI App

> Drop ChappieSignIn into any SwiftUI view for a guided ChatGPT sign-in experience, with optional custom labels and manual device-code control.

`ChappieSignIn` is the primary way to let users authenticate with their ChatGPT account. Drop it into any SwiftUI view alongside your `ChappieAuthSession` and the button handles the entire device-code flow — requesting the code, copying it to the clipboard, opening the verification URL, and polling for approval — all with an optional onboarding sheet that walks the user through each step.

## Basic usage

Attach your `authSession` and apply any standard SwiftUI button modifiers:

```swift theme={null}
@StateObject private var authSession = Chappie.authSession()

var body: some View {
    ChappieSignIn(authSession: authSession)
        .buttonStyle(.borderedProminent)
        .padding()
}
```

The button disables itself while sign-in is in progress and hides once the user is signed in, so no additional conditional logic is required.

<Tip>
  `ChappieSignIn` requests notification permission before starting the device-code flow by default. When the user approves the device code in their browser, the SDK fires a local notification — "ChatGPT Linked! Return to the app" — so they know it's time to switch back.
</Tip>

***

## Onboarding sheet

By default `ChappieSignIn` presents a built-in sheet that guides users through three steps:

1. **Open OpenAI** — a button that launches the verification URL in Safari
2. **Continue with ChatGPT** — a reminder to tap Continue when prompted
3. **Paste your device code** — the device code displayed in a tap-to-copy chip

This sheet is the recommended experience for most apps because it eliminates the need for custom instructions.

***

## Handling the device code yourself

If you want to integrate the device code into your own UI — for example, to display it inline or deep-link into a custom web view — disable the onboarding sheet and receive the code in the `onDeviceCode` closure:

```swift theme={null}
ChappieSignIn(
    authSession: authSession,
    showsOnboardingSheet: false,
    onDeviceCode: { deviceCode in
        print("User code:", deviceCode.userCode)
        print("Verification URL:", deviceCode.verificationURL)
    }
)
```

The button opens the verification URL automatically when `showsOnboardingSheet` is `false`. You can use `onDeviceCode` to update your own UI state at the same time.

***

## Controlling notification permission

`ChappieSignIn` asks for notification permission the first time the button is tapped. You can suppress this if your app manages notification permission elsewhere:

```swift theme={null}
ChappieSignIn(
    authSession: authSession,
    requestsNotificationPermission: false
)
```

When you opt out, the SDK still sends the "ChatGPT Linked" local notification if permission was already granted through another code path.

***

## Custom label

Provide a `label` closure to replace the default `ChappieSignInLabel` with any SwiftUI view. The closure receives the button's current title string so you can incorporate it into your own layout:

```swift theme={null}
ChappieSignIn(authSession: authSession) { title in
    Label(title, systemImage: "person.badge.plus")
        .font(.headline)
}
```

The title string updates automatically as sign-in progresses — `"Sign in with ChatGPT"` → `"Preparing sign in"` → `"Open ChatGPT sign in"`.

***

## Legacy wrapper: `SignInWithChatGPTButton`

`SignInWithChatGPTButton` is a pre-styled wrapper around `ChappieSignIn` that fills the available width and applies a bold font. It is still available for compatibility, but new code should use `ChappieSignIn` directly for full control over layout and customisation.

```swift theme={null}
SignInWithChatGPTButton(authSession: authSession)
```

***

## Manual device-code flow

For advanced scenarios — custom authentication screens, command-line tooling, or integration tests — you can start the device-code flow directly without using any button:

```swift theme={null}
let deviceCode = try await authSession.startDeviceCodeSignIn()

// Present these to the user however you like:
print("User code:", deviceCode.userCode)
print("Verification URL:", deviceCode.verificationURL)

// Then watch state or deviceLinkState for completion:
// authSession.state → .signedIn(expiresAt:)
// authSession.deviceLinkState → .linked(expiresAt:)
```

`startDeviceCodeSignIn()` returns as soon as the device code is ready. Background polling starts immediately — you don't need to call anything else. Observe `authSession.state` or `authSession.deviceLinkState` to detect when the user approves the code.

### `ChappieDeviceLinkState` values

| Case                                   | Meaning                                                                   |
| -------------------------------------- | ------------------------------------------------------------------------- |
| `.idle`                                | No sign-in is in progress                                                 |
| `.requestingCode`                      | The SDK is requesting a device code from OpenAI                           |
| `.awaitingUserCode(ChappieDeviceCode)` | The device code is ready and waiting to be presented to the user          |
| `.polling(ChappieDeviceCode)`          | The SDK is polling OpenAI to check whether the user has approved the code |
| `.linked(expiresAt:)`                  | Sign-in succeeded; the credential is stored in Keychain                   |
| `.expired`                             | The device code expired before the user approved it                       |
| `.cancelled`                           | Sign-in was cancelled                                                     |
| `.failed(ChappieAuthError)`            | An error occurred during the device-code exchange                         |

<Note>
  `deviceLinkState` gives you finer-grained progress than `state`. Use it when you want to show a progress indicator that distinguishes between requesting the code, waiting for user action, and active polling.
</Note>

***

## Full initialiser reference

<CodeGroup>
  ```swift Default label theme={null}
  ChappieSignIn(
      authSession: authSession,                       // Required
      showsOnboardingSheet: true,                     // Default: true
      requestsNotificationPermission: true,           // Default: true
      onDeviceCode: nil                               // Default: nil
  )
  ```

  ```swift Custom label theme={null}
  ChappieSignIn(
      authSession: authSession,
      showsOnboardingSheet: true,
      requestsNotificationPermission: true,
      onDeviceCode: { deviceCode in
          // Called when the device code is ready
      }
  ) { title in
      // Return any SwiftUI view
      Label(title, systemImage: "person.badge.plus")
  }
  ```
</CodeGroup>
