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
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:
1https://github.com/kemegames-studio/keme-sdk.git
Or add it to your Package.swift from 0.1.0:
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:
1pod 'KemeSDK'
.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.
1import KemeSDK23let keme = Keme.configure(KemeConfig(gameId: "your-game-id", appName: "Marble Sort"))4try await keme.login(userId: "player-123", username: "Ari") // auto-provisions the player5keme.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.
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.
1import SwiftUI2import KemeSDK34struct GameView: View {5 @State private var showSupport = false6 let keme = Keme.shared!78 var body: some View {9 Button("Contact Support") { showSupport = true }10 .sheet(isPresented: $showSupport) {11 KemeSupportView(client: keme) // or KemeSupportView() to use Keme.shared12 }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.
1let player = try await keme.login(userId: "player-123", username: "Ari")23let signedIn = keme.isLoggedIn() // true if a non-empty token is stored4let me = try await keme.getPlayer()56keme.logout() // clears the stored token
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.
1// UIKit2keme.presentSupport(from: self)34// 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.
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: .p27 )8)910let mine = try await keme.tickets.list(ListTicketsParams()) // the player's tickets11let detail = try await keme.tickets.get(ticket.id) // includes messages12try await keme.tickets.reply(ticket.id, body: "Any update?") // body 1–500013try 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.
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:
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 = decode4 let body: Any? // raw parsed JSON body when available5}
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:
1Keme.configure(KemeConfig(2 gameId: "your-game-id",3 apiBase: "https://staging-api.kemegames.com/api/v1"4))