Skip to main content
This guide walks you from a blank iOS app to a working ChatGPT-powered assistant in five steps. You will add the package, wire up authentication, create a client with a harness, send a message, and handle the most common error conditions. By the end, your app will be having real conversations through the user’s own ChatGPT account.
1

Add the package

Open your Xcode project, go to File → Add Package Dependencies…, and enter the repository URL:
https://github.com/b-nnett/chappie-sdk.git
Select Up to Next Major Version starting from 0.1.0, then add the ChappieSDK product to your app target. For full installation options including the Swift Package Manager manifest approach, see the Installation guide.
2

Sign the user in

Chappie authenticates users through their own ChatGPT account using an OAuth device-code flow. Create a shared ChappieAuthSession at the app level with Chappie.authSession(), then show ChappieSignIn when the user is not yet signed in. The SDK handles the onboarding sheet, credential storage, and token refresh automatically.
import ChappieSDK
import SwiftUI

@main
struct MyApp: App {
    @StateObject private var authSession = Chappie.authSession()

    var body: some Scene {
        WindowGroup {
            ContentView(authSession: authSession)
        }
    }
}

struct ContentView: View {
    @ObservedObject var authSession: ChappieAuthSession

    var body: some View {
        if authSession.state.isSignedIn {
            ChatView()
        } else {
            ChappieSignIn(authSession: authSession)
                .buttonStyle(.borderedProminent)
                .padding()
        }
    }
}
Chappie.authSession() must be called on the main actor, which @StateObject initializers satisfy automatically. The session restores previously stored credentials from the Keychain on creation, so returning users are already signed in before they see the UI.
3

Create a client with a harness

A ChappieHarness tells the model which app surface it is running in, what capabilities are available, and which tools it can call. Chappie prepends the harness manifest to every transcript, so the model always has the right context before the user’s first message. Create your client with Chappie.client(harness:) once — typically as a stored property or environment value — and reuse it across turns.
import ChappieSDK

let client = Chappie.client(harness: ChappieHarness(
    name: "My App Harness",
    summary: "Native iOS assistant for my app.",
    capabilities: ["multi-turn transcript", "streaming responses"]
))
For quick experiments you can pass a string literal directly, or use the built-in default harness:
// Inline instruction harness
let pirateClient = Chappie.client(harness: "You're a pirate. Speak like it. Don't ever stray from the seas, or ship-talk.")

// SDK default harness
let defaultClient = Chappie.client(harness: .default)
Passing an empty string ("") disables Chappie’s default guardrail entirely and sends no harness manifest. Use a structured ChappieHarness value whenever you need declared capabilities or host tools.
4

Send your first message

With a client in hand, you are ready to talk to the model. Use client.send(_:) for a single-turn reply, or client.stream(_:) to receive a token-by-token AsyncThrowingStream of ChappieStreamEvent values. Both methods update the client’s active transcript automatically, so multi-turn conversations build naturally.
import ChappieSDK

// Returns the complete assistant reply as a String
let reply = try await client.send("Summarize what this app does.")
print(reply)
Streaming prefers a WebSocket connection and retries up to five times before falling back to HTTP/SSE. You can force SSE-only streaming by creating the client with streamingTransportPolicy: .httpSSEOnly.
5

Handle usage limits and auth errors

Chappie surfaces usage-limit and credential errors as typed ChappieClientError cases so you can give users clear feedback rather than a generic failure. Catch usageLimitReached to show when the user’s plan resets, and catch missingCredential to redirect them back to the sign-in flow.
import ChappieSDK

do {
    let reply = try await client.send("Hello")
    print(reply)
} catch ChappieClientError.usageLimitReached(let limit) {
    // The user has hit their ChatGPT plan's message limit
    print("Plan: \(limit.plan?.displayName ?? "Unknown")")
    print("Resets at: \(limit.resetsAt as Any)")
} catch ChappieClientError.missingCredential {
    // The user needs to sign in — route them back to ChappieSignIn
} catch {
    print("Unexpected error: \(error)")
}
Chappie automatically backs off on 429 responses, respecting Retry-After when present and using bounded exponential backoff otherwise. The default policy retries before surfacing usageLimitReached. You can tune or disable this through ChappieRateLimitBackoffPolicy on the client.

What’s Next

Now that you have a working assistant in your app, explore authentication configuration and harness capabilities in depth.

Authentication Overview

Learn how device-code sign-in works, how to support multi-account apps with custom Keychain configurations, and how to handle manual device-code flows and auth state transitions.

Harnesses

Declare your app’s capabilities, register executable host tools with Swift handlers, use sample harnesses for common surfaces, and bundle reusable harnesses as plugin manifests.