Sign users in to ChatGPT (OAuth 2.0 + PKCE, Keychain-backed) and call the
Codex backend on the user's plan instead of your API key — no per-token
billing. Two SwiftPM products — pick CodingPlanAuth alone for OAuth, add
CodingPlanCodex when you want plan-bound chat / streaming / image
generation / usage.
.package(url:"https://github.com/atom2ueki/CodingPlanKit.git", from:"0.1.1"),.target(
name:"MyApp",
dependencies:[.product(name:"CodingPlanAuth",package:"CodingPlanKit"),.product(name:"CodingPlanCodex",package:"CodingPlanKit"),])For iOS, register a custom URL scheme in Info.plist (e.g. myapp) so
ASWebAuthenticationSession can return cleanly from the OAuth redirect.
CodingPlanKit ships a Claude Code skill so an AI agent in your editor can integrate the SDK without you having to read the source. Inside Claude Code:
/plugin marketplace add atom2ueki/CodingPlanKit
/plugin install coding-plan-kit@coding-plan-kit
After that, prompts like "add CodingPlanKit auth to this iOS app" or
"stream a Codex response with image generation" trigger the skill
automatically. The skill reads from llms.txt for the full
public surface, so it stays in sync with the source.
import SwiftUI
import CodingPlanAuth
@MainActor@ObservablefinalclassSignIn{privateletservice:AuthServiceprivateletprovider=OpenAIAuthProvider(callbackScheme:"myapp")privateletbrowser=BrowserAuthSession()varcredentials:Credentials?init()throws{
// KeychainTokenStorage() throws when there's no servicePrefix and
// no Bundle.main.bundleIdentifier (CLI/test contexts). In a normal
// iOS/macOS app target the bundle id is always present and this
// never throws — propagating rather than crashing keeps misconfigured
// hosts (e.g. CLI tools without an explicit servicePrefix) loud.
service =AuthService(storage:tryKeychainTokenStorage())}func signIn()asyncthrows{await service.register(provider)letsession=tryawait service.beginLogin(providerId:"openai")letcallbackURL=tryawait browser.authenticate(
url: session.authURL,
callbackScheme:"myapp")
credentials =tryawait service.completeLogin(
session: session,
with: callbackURL
)}}Then call plan-bound APIs with CodingPlanCodex:
import CodingPlanCodex
letcodex=OpenAICodexClient()
// One-shot:
letresponse=tryawait codex.createTextResponse(
prompt:"Refactor this Swift function...",
credentials: credentials
)
// Stream deltas:
fortryawaitdeltain codex.streamTextResponse(prompt:"...", credentials: credentials){print(delta, terminator:"")}llms.txt— file-by-file index for AI agents and humans.- DocC catalogs:
CodingPlanAuth,CodingPlanCodex.
- SwiftWebServer — local loopback HTTP listener used for the OAuth callback during sign-in.
MIT © 2026 Tony Li
