Native iOS SDK v0.1.0 Swift

The official Swift SDK for Keme brings drop-in in-app player support into your iOS game or app. It wraps the Keme REST portal (auth, tickets, knowledge base), persists the player token, unwraps the {success,data} envelope, and ships a ready-made support UI for both UIKit and SwiftUI.

Overview

The SDK targets iOS 15+ on Swift 5.9+, is a pure Foundation + UIKit/SwiftUI package with zero third-party dependencies, and is imported as import KemeSDK.

    Minimum deployment target: iOS 15.0Swift language version: 5.9 or laterZero mandatory third-party dependencies (pure Foundation + UIKit/SwiftUI)Package managers: Swift Package Manager and CocoaPodsUI frameworks: UIKit (presentSupport) and SwiftUI (KemeSupportView) both supported
Note:The SDK is published in the monorepo at github.com/kemegames-studio/keme-sdk. Current version v0.1.0.

Installation

Swift Package Manager (recommended)

In Xcode: File ▸ Add Package Dependencies… and paste the package URL:

bash
1https://github.com/kemegames-studio/keme-sdk.git

Or add it to your Package.swift from 0.1.0:

Package.swift
1dependencies: [
2 .package(url: "https://github.com/kemegames-studio/keme-sdk.git", from: "0.1.0")
3],
4targets: [
5 .target(name: "MyGame", dependencies: [
6 .product(name: "KemeSDK", package: "keme-sdk")
7 ])
8]

CocoaPods

Add the pod to your Podfile and run pod install:

Podfile
1pod 'KemeSDK'
Note:After installing via CocoaPods, always open the .xcworkspace file, not the .xcodeproj.

Quickstart

Three lines get you a working support surface. configure creates the client, login authenticates the player (auto-provisioning them on first login), and presentSupport shows the drop-in screen.

swift
1import KemeSDK
2
3let keme = Keme.configure(KemeConfig(gameId: "your-game-id", appName: "Marble Sort"))
4try await keme.login(userId: "player-123", username: "Ari") // auto-provisions the player
5keme.presentSupport(from: self) // drop-in support screen

Keme.configure(_:) also registers a Keme.shared singleton, so you can reach the client anywhere via Keme.shared (or try Keme.requireShared()). If you prefer to hold the instance yourself, Keme.init(config:) is an alias.

Tip:All async methods are async throws. UI methods (presentSupport, the view controllers) must be called on the main thread; internal UI updates are marked @MainActor.

SwiftUI

In SwiftUI, present the drop-in support screen with KemeSupportView inside a sheet.

swift
1import SwiftUI
2import KemeSDK
3
4struct GameView: View {
5 @State private var showSupport = false
6 let keme = Keme.shared!
7
8 var body: some View {
9 Button("Contact Support") { showSupport = true }
10 .sheet(isPresented: $showSupport) {
11 KemeSupportView(client: keme) // or KemeSupportView() to use Keme.shared
12 }
13 }
14}

Authentication

Call login with the player's userId (and optional username / email). The first login for a given userId auto-provisions the player — there is no separate registration step — and the 7-day token is persisted automatically.

swift
1let player = try await keme.login(userId: "player-123", username: "Ari")
2
3let signedIn = keme.isLoggedIn() // true if a non-empty token is stored
4let me = try await keme.getPlayer()
5
6keme.logout() // clears the stored token
Warning:A banned player's login throws KemeError with status 403. Reuse the same userId for the same player across sessions and devices so their ticket history follows them.

Presenting the Support Screen

keme.presentSupport(from:) presents the drop-in support screen modally from any view controller — ticket list, ticket detail with reply, and a new-ticket form. The player must be logged in first, since the screen calls authenticated endpoints.

swift
1// UIKit
2keme.presentSupport(from: self)
3
4// SwiftUI — see the SwiftUI section for KemeSupportView

Tickets

Drive tickets directly through keme.tickets without the drop-in UI. Subjects are 5–200 characters and descriptions are 10–5000. All methods are async throws.

swift
1let ticket = try await keme.tickets.create(
2 CreateTicketParams(
3 subject: "Lost my gems after the update",
4 description: "I had 500 gems before v1.4 and now they're gone.",
5 category: .billing,
6 priority: .p2
7 )
8)
9
10let mine = try await keme.tickets.list(ListTicketsParams()) // the player's tickets
11let detail = try await keme.tickets.get(ticket.id) // includes messages
12try await keme.tickets.reply(ticket.id, body: "Any update?") // body 1–5000
13try await keme.tickets.rate(ticket.id, score: 5, comment: "Fast fix, thanks!") // 1–5

Categories & priorities

    TicketCategory: bug | billing | account | gameplay | otherTicketPriority: P1 | P2 | P3 | P4Unknown enum values decode gracefully (TicketCategory → .other, VipTier → .other(raw)) so new server values never break the client.

Knowledge Base

Surface self-serve help content through keme.kb.

swift
1let results = try await keme.kb.search("gems")
2let categories = try await keme.kb.categories()
3let featured = try await keme.kb.featured()
4let article = try await keme.kb.article("how-to-restore-purchases")

Errors

Every non-2xx response, network failure, or decode failure throws a KemeError:

swift
1public struct KemeError: Error {
2 let message: String // server message (String or [String] joined with ", ")
3 let status: Int // HTTP status; 0 = network/transport, -1 = decode
4 let body: Any? // raw parsed JSON body when available
5}

gameId vs userId

These are two different identifiers — don't mix them up:

    gameId identifies your game/app (e.g. game-marble-sort). Set it once in KemeConfig; it is attached to every created ticket. Fetch valid ids with keme.games().userId identifies the player uniquely within your game (maps to the portal gameUid). Pass it to login(userId:). The first login auto-provisions the player — no separate registration step.

Token Storage & Base URL

The token (7-day JWT) is persisted in UserDefaults under key keme.portal.token by default. Provide your own via KemeConfig(storage:) — implement the TokenStorage protocol (e.g. Keychain-backed). MemoryTokenStorage (non-persistent) is included for tests.

The base URL defaults to production https://api.kemegames.com/api/v1. Override it for staging:

swift
1Keme.configure(KemeConfig(
2 gameId: "your-game-id",
3 apiBase: "https://staging-api.kemegames.com/api/v1"
4))
Last updated: July 18, 2026Edit this page