← Blog

Claude API Mobile App Integration for iOS

2026-10-21 · 5 min read · SubToAPI Team

If you're building an iOS app and want to add Claude for chat, summarization, or tool-calling features, the short answer is: don't call the Claude API directly from the app. Call it from a backend, and have your iOS app talk to that backend instead. This article walks through why, and shows a concrete architecture and Swift code you can use today.

The reason is simple: any secret embedded in an iOS binary can be extracted. API keys shipped inside your app bundle — even "hidden" in obfuscated strings, Info.plist, or a compiled binary — show up within minutes to anyone who decompiles the IPA. If your Claude API key leaks this way, someone else runs up your bill. This isn't a theoretical risk; it's the default outcome unless you add a server layer.

The architecture: app → backend → Claude

The pattern every production iOS app uses for LLM features looks like this:

iOS App  →  Your Backend (auth, rate limiting)  →  Claude API

Your backend is where the real API key lives. The iOS app only ever holds a token that's scoped to your own users — Firebase auth token, Sign in with Apple session, or a simple API key you issue per device. That backend then forwards (and shapes) requests to Claude.

You have two options for that backend layer:

  1. Build it yourself — a Node/Python/Go service that wraps the Anthropic SDK, handles retries, logs usage, and enforces per-user quotas.
  2. Use a managed layer like SubToAPI — which gives you an HTTPS API with application keys (sub_live_...), streaming, tool use, and usage metadata out of the box, so your "backend" work is mostly just proxying requests and mapping your own user IDs to API calls.

Either way, the iOS app never sees the raw Claude credential. This also solves a second problem: Apple's App Store review process scrutinizes apps that expose third-party API keys client-side, and apps with uncontrolled AI costs tend to get pulled fast when a key leaks publicly on GitHub or in a network trace.

Setting up the backend quickly

If you don't want to run and maintain your own proxy server, you can point your backend (even a thin one) at SubToAPI's endpoint and get streaming, tool calls, and per-key usage without building that infrastructure yourself:

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $SUBTOAPI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5",
    "max_tokens": 1024,
    "messages": [
      {"role": "user", "content": "Summarize this app review in one sentence."}
    ]
  }'

This request still runs server-side — inside your backend process, a Lambda, or a Vercel function — never inside the app itself. Your iOS app calls your own endpoint (e.g. /api/chat), and your server calls SubToAPI (or Anthropic directly). See the quickstart and messages docs for the full request/response shape.

Calling your backend from Swift

Once your backend endpoint exists, the iOS side is ordinary networking code. Here's a minimal URLSession call using async/await:

struct ChatRequest: Encodable {
    let message: String
}

struct ChatResponse: Decodable {
    let reply: String
}

func sendMessage(_ text: String) async throws -> String {
    let url = URL(string: "https://yourapp.com/api/chat")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.setValue("Bearer \(userAuthToken)", forHTTPHeaderField: "Authorization")
    request.httpBody = try JSONEncoder().encode(ChatRequest(message: text))

    let (data, response) = try await URLSession.shared.data(for: request)
    guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {
        throw URLError(.badServerResponse)
    }
    return try JSONDecoder().decode(ChatResponse.self, from: data).reply
}

userAuthToken here is your own app's session token, not a Claude or SubToAPI key. Your backend validates it and decides which model, budget, or system prompt to apply per user.

Streaming responses on iOS

Chat UIs feel much better with token-by-token streaming instead of waiting for a full response. Claude supports server-sent events, and so does SubToAPI — see streaming for the full format. On iOS, you can consume an SSE stream from your backend using URLSession.bytes(for:):

func streamMessage(_ text: String, onToken: @escaping (String) -> Void) async throws {
    let url = URL(string: "https://yourapp.com/api/chat/stream")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpBody = try JSONEncoder().encode(ChatRequest(message: text))

    let (bytes, _) = try await URLSession.shared.bytes(for: request)
    for try await line in bytes.lines {
        guard line.hasPrefix("data: ") else { continue }
        let payload = line.dropFirst(6)
        if payload == "[DONE]" { break }
        onToken(String(payload))
    }
}

Your backend endpoint simply relays the SSE stream it gets from Claude (or from SubToAPI) through to the app, token by token. This keeps latency low and avoids buffering a whole response in memory on the server.

Tool use from a mobile app

If your app needs Claude to call functions — fetch order status, search a local database, trigger an action in your app — you define tool schemas on the backend and let Claude decide when to invoke them, following the same pattern as the tools guide. The iOS app doesn't need to know tool definitions exist; it just sends messages and renders whatever the backend returns, including any tool-result content blocks.

Managing keys and costs across a team

If multiple developers or environments (dev, staging, TestFlight, production) need Claude access, don't reuse one key everywhere. A per-environment key setup, with usage visible per key, makes it much easier to spot a runaway build or a leaked TestFlight binary before it becomes expensive. SubToAPI's dashboard gives you separate application keys per project with usage metadata, and team seats if more than one person needs access to the dashboard. Start with the free trial at /signup to wire up a test endpoint before shipping to TestFlight.

Questions

Can I call the Claude API directly from Swift code without a backend? Technically yes, but any key embedded in the app binary can be extracted by decompiling the IPA. For anything beyond a personal prototype, route requests through a backend you control.

Does streaming work well over cellular connections on iOS? Yes — SSE over HTTPS works fine on cellular and Wi-Fi. Handle connection drops gracefully (retry with the last received token count) since mobile networks are less stable than desktop connections.

What's the fastest way to prototype Claude in an iOS app before building a full backend? Stand up a minimal serverless function (Vercel, AWS Lambda) that forwards requests to /docs/messages with your SubToAPI key, then point your Swift networking code at that single endpoint while you build out the rest of the app.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →