Skip to main content
The iOS WhopChat SDK is coming soon. This documentation is a preview.
Embedded chat allows you to integrate Whop’s real-time messaging directly into your native iOS app.

Setup Requirements

Before using the chat SDK, you need to configure OAuth authentication:
  1. Register your app in the Whop Dashboard > Developer to get an app ID
  2. Inside the app you just created, go to OAuth and add a redirect URL with your bundle ID: com.yourapp.bundle://oauth/callback (or configure a custom one)
  3. On the same page, copy the required scopes from “View available scopes”
  4. Configure the SDK with your app ID, scopes, and redirect URI as early as possible in your app lifecycle

OAuth Configuration

Initialize the SDK with OAuth before using any chat features:
import WhopElements

struct ContentView: View {
    var body: some View {
        NavigationStack {
            // Your app content
        }
        .task {
            // Configure OAuth on app launch
            await WhopSDK.configureWithOAuth(
                appId: "app_XXXXXXXXXXXXXX",
                scopes: [
                    "openid", "profile", "email", // basic profile info
                    "chat:message:create", "chat:read", // experience chats
                    "dms:read", "dms:message:manage", "dms:channel:manage", // direct messages
                ]
            )
        }
    }
}

Custom redirect URI

By default, the SDK uses your bundle identifier for the redirect URI (com.yourapp.bundle://oauth/callback). You can customize this if needed:
await WhopSDK.configureWithOAuth(
    appId: "app_XXXXXXXXXXXXXX",
    redirectUri: "myapp://auth/callback",
    scopes: [
        "openid", "profile", "email",
        "chat:message:create", "chat:read",
        "dms:read", "dms:message:manage", "dms:channel:manage",
    ]
)
Make sure the redirect URI matches what you configured in the Whop Dashboard > Developer > App > OAuth.

Chat View

Display a chat channel with WhopChatView:
import SwiftUI
import WhopElements

struct ChatView: View {
    var body: some View {
        WhopChatView(
            channelId: "chat_XXXXXXXXXXXXXX",
            style: .imessage
        )
    }
}

Chat Styles

Choose between Discord-style or iMessage-style chat. iMessage style (bubble chat) is the default if not provided:
// iMessage style (bubble chat)
WhopChatView(
    channelId: "chat_XXXXXXXXXXXXXX",
    style: .imessage
)

// Discord style (full-width messages)
WhopChatView(
    channelId: "chat_XXXXXXXXXXXXXX",
    style: .discord
)

Deeplinking to messages

You can deeplink to a specific message in the chat by providing a deeplinkToPostId parameter. The view will automatically scroll to and highlight the specified message. The parameter responds to changes, so you can update it dynamically:
struct ChatView: View {
    @State private var targetPostId: String? = nil

    var body: some View {
        WhopChatView(
            channelId: "chat_XXXXXXXXXXXXXX",
            deeplinkToPostId: targetPostId,
            style: .imessage
        )
    }
}
When deeplinkToPostId changes, the chat will automatically navigate to the new message.

Event Handling

Listen to chat events using the onEvent parameter:
WhopChatView(
    channelId: "chat_XXXXXXXXXXXXXX",
    style: .imessage,
    onEvent: { event in
        switch event {
        case let .profileTap(username):
            print("Profile tapped: \(username)")
        case let .urlTap(url):
            print("URL tapped: \(url)")
        case let .messageSent(content):
            print("Message sent: \(content)")
        }
    }
)

DMs List

Display a list of the user’s direct messages with WhopDMsListView:
struct MessagesView: View {
    @State private var selectedChannel: DMChannel?

    var body: some View {
        WhopDMsListView(
            onEvent: { event in
                switch event {
                case let .channelSelected(channel):
                    selectedChannel = channel
                }
            }
        )
        .navigationDestination(item: $selectedChannel) { channel in
            WhopChatView(
                channelId: channel.id,
                style: .imessage
            )
            .navigationBarTitleDisplayMode(.inline)
            .navigationTitle(channel.name)
        }
    }
}

Complete Example

Here’s a complete example showing OAuth setup, manual and automatic authentication, chat, and DMs:
import SwiftUI
import WhopElements

struct WhopChatExample: View {
    @State private var selectedDmChannel: DMChannel?
    @State private var isAuthenticated = false

    var body: some View {
        List {
            Section {
                NavigationLink {
                    WhopChatView(
                        channelId: "chat_XXXXXXXXXXXXXX",
                        style: .imessage,
                        onEvent: { event in
                            switch event {
                            case let .profileTap(username):
                                print("Profile tapped: \(username)")
                            case let .urlTap(url):
                                print("URL tapped: \(url)")
                            case let .messageSent(content):
                                print("Message sent: \(content)")
                            }
                        }
                    )
                    .navigationTitle("Chat Demo")
                } label: {
                    Text("Open Chat")
                }

                NavigationLink {
                    WhopDMsListView(
                        onEvent: { event in
                            switch event {
                            case let .channelSelected(channel):
                                selectedDmChannel = channel
                            }
                        }
                    )
                    .navigationTitle("Messages")
                } label: {
                    Text("Open DMs List")
                }
            }

            Section {
                if isAuthenticated {
                    Button(role: .destructive) {
                        WhopSDK.signOut()
                    } label: {
                        Text("Sign Out")
                    }
                } else {
                    Button("Sign In") {
                        Task {
                            do {
                                try await WhopSDK.signIn()
                            } catch {
                                print("Error signing in: \(error)")
                            }
                        }
                    }
                }
            }
        }
        .navigationTitle("Chat")
        .task {
            // Configure OAuth on view load
            await WhopSDK.configureWithOAuth(
                appId: "app_XXXXXXXXXXXXXX",
                scopes: [
                    "openid", "profile", "email", // basic profile info
                    "chat:message:create", "chat:read", // experience chats
                    "dms:read", "dms:message:manage", "dms:channel:manage", // direct messages
                ]
            )
        }
        .whopAuthState($isAuthenticated)
        .navigationDestination(item: $selectedDmChannel) { channel in
            WhopChatView(
                channelId: channel.id,
                style: .imessage
            )
            .navigationBarTitleDisplayMode(.inline)
            .navigationTitle(channel.name)
        }
    }
}

Manual authentication (optional)

The SDK handles authentication automatically. When a user navigates to a chat view, the OAuth flow is triggered if they’re not already authenticated. You can also manually sign-in and sign-out:
Button("Sign In") {
    Task {
        do {
            try await WhopSDK.signIn()
        } catch {
            print("Error signing in: \(error)")
        }
    }
}
Button("Sign Out") {
    WhopSDK.signOut()
}

Pre-filling tokens from your backend

If your app already has the user’s Whop tokens (from a web OAuth flow or synced from your backend), you can skip the OAuth webview entirely using preSignIn:
// Fetch tokens from your backend
let tokens = await myBackend.getWhopTokens(for: userLinkedAccount)

// Pre-fill tokens - no OAuth step needed if valid
try await WhopSDK.preSignIn(
    accessToken: tokens.accessToken,
    refreshToken: tokens.refreshToken
)
This is useful when:
  • Your app has a web version where users already authenticated with Whop
  • You sync user sessions across devices via your own backend
  • You want to provide a seamless experience without requiring users to re-authenticate on each device
The SDK automatically extracts the token expiration from the JWT and handles token refresh when needed. Use the .whopAuthState modifier to track the user’s authentication status:
struct ChatView: View {
    @State private var isAuthenticated = false

    var body: some View {
        VStack {
            if isAuthenticated {
                Text("Signed In")
            } else {
                Text("Not Signed In")
            }
        }
        .whopAuthState($isAuthenticated)
    }
}

Push Notifications

Push notifications work automatically with zero code required.
This section is under construction. This documentation is provisional.

Setup

Upload your APNs certificate to the Whop Dashboard (one-time setup). That’s it! The SDK automatically handles:
  • Requesting notification permission on first launch
  • Intercepting and registering the device token with Whop
  • Receiving push notifications for new messages
  • Opening the correct chat when tapped
The SDK uses method swizzling to automatically capture the device token from Apple, so no AppDelegate code is needed.