Install Vaka in an iOS app
These steps are written for a coding agent. Do them in order and verify each one before moving on.
- Endpoint:
https://vaka.example.com - Ingest key:
vk_your_ingest_key(write-only, safe to commit and ship in the app) - Project:
your-project
Requirements: iOS 15+ (macOS 12+ and tvOS 15+ also work), Swift 5.9+ / Xcode 15+.
1. Add the Swift package
The package is https://github.com/ErikHellman/vaka, product Vaka.
Pick the case that matches the project:
A. The app is built from a Package.swift (or you are adding Vaka to a local package that the app uses).
Add the dependency and the product:
dependencies: [
.package(url: "https://github.com/ErikHellman/vaka", from: "0.1.0"),
],
targets: [
.target(name: "MyApp", dependencies: [.product(name: "Vaka", package: "vaka")]),
]
If version resolution fails because no release tag exists yet, use branch: "main" instead of from: "0.1.0".
B. The project is generated by XcodeGen (project.yml) or Tuist (Project.swift). Add the package there,
not in the .xcodeproj. XcodeGen:
packages:
Vaka:
url: https://github.com/ErikHellman/vaka
from: 0.1.0
targets:
MyApp:
dependencies:
- package: Vaka
Then regenerate the project (xcodegen generate / tuist generate).
C. A plain .xcodeproj. Editing project.pbxproj by hand is error-prone. Ask the human to add it in Xcode:
File > Add Package Dependencies…, paste https://github.com/ErikHellman/vaka, and add the Vaka library to the
app target. Continue with step 2 while they do; the code will not compile until the package is added.
2. Start the SDK at launch
Start Vaka as early as possible, before other SDKs that install crash handlers.
SwiftUI app (@main struct ...: App):
import SwiftUI
import Vaka
@main
struct MyApp: App {
init() {
Vaka.start(Vaka.Options(
key: "vk_your_ingest_key",
endpoint: URL(string: "https://vaka.example.com")!,
environment: {
#if DEBUG
"debug"
#else
"production"
#endif
}()
))
}
var body: some Scene {
WindowGroup { ContentView() }
}
}
UIKit app (AppDelegate):
import UIKit
import Vaka
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Vaka.start(Vaka.Options(key: "vk_your_ingest_key", endpoint: URL(string: "https://vaka.example.com")!))
return true
}
release defaults to CFBundleShortVersionString (the app's MARKETING_VERSION). Leave it, or pass the same
string you will use for the dSYM upload in step 4.
The SDK then automatically:
- reports crashes (signals and uncaught Objective-C exceptions) on the next launch,
- tracks sessions for crash-free rates (start on foreground, end after 30 s in background),
- queues everything on disk and sends in gzipped batches every 10 s, retrying when offline.
3. Add screen views, events and handled errors
Add these where they fit the app. Do not add user identification unless the human asks for it.
Vaka.screen("Checkout") // in .onAppear / viewDidAppear of main screens
Vaka.track("purchase", ["plan": "pro"]) // key product events; values are String, number or Bool
Vaka.addBreadcrumb(category: "cart", message: "Added item 42")
do {
try checkout()
} catch {
Vaka.capture(error) // handled errors you want to see
}
Vaka.identify("user-123") // opt-in only; Vaka.identify(nil) on logout
For SwiftUI, a simple pattern is .onAppear { Vaka.screen("Settings") } on each top-level screen view.
4. Upload dSYMs so crashes get function names, files and lines
Crash reports contain raw addresses. The server symbolicates them with the dSYM uploaded for the same build.
Make sure release builds produce dSYMs: build setting
DEBUG_INFORMATION_FORMAT = dwarf-with-dsymfor the Release configuration (this is Xcode's default).Install the CLI on the machine that builds releases (developer Mac or CI):
cargo install --git https://github.com/ErikHellman/vaka vakaCreate an API token with write access to the project (admin UI at
https://vaka.example.com/ui, or ask the human). Never commit it: put it in CI secrets, or in a git-ignored.vaka.envfile next to the.xcodeproj:VAKA_TOKEN=vt_...Add a Run Script build phase as the last phase of the app target:
# Upload dSYMs to Vaka (release builds only). [ "$CONFIGURATION" = "Release" ] || exit 0 [ -f "$SRCROOT/.vaka.env" ] && . "$SRCROOT/.vaka.env" && export VAKA_TOKEN if ! command -v vaka >/dev/null 2>&1; then [ -x "$HOME/.cargo/bin/vaka" ] && PATH="$HOME/.cargo/bin:$PATH" || { echo "warning: vaka CLI not installed; dSYMs not uploaded"; exit 0; } fi [ -n "$VAKA_TOKEN" ] || { echo "warning: VAKA_TOKEN not set; dSYMs not uploaded"; exit 0; } vaka upload dsym --url "https://vaka.example.com" --project "your-project" --release "$MARKETING_VERSION" "$DWARF_DSYM_FOLDER_PATH" \ || echo "warning: Vaka dSYM upload failed"- Input file:
${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${EXECUTABLE_NAME}(so Xcode runs the phase after the dSYM exists). - Xcode 15+ sandboxes build scripts: set
ENABLE_USER_SCRIPT_SANDBOXING = NOon the app target, or the script cannot read the dSYM folder and.vaka.env.
For XcodeGen, add the same script under
postBuildScriptswithinputFiles.- Input file:
CI / archives instead of a build phase: after xcodebuild archive, run
vaka upload dsym --url "https://vaka.example.com" --project "your-project" --release "<version>" path/to/MyApp.xcarchive/dSYMs.
Without the CLI: upload each DWARF file with curl (send kind before file):
curl -H "Authorization: Bearer $VAKA_TOKEN" \
-F kind=native -F release="2.3.1" \
-F file=@MyApp.app.dSYM/Contents/Resources/DWARF/MyApp \
"https://vaka.example.com/api/v1/projects/your-project/artifacts"
release must equal the app's CFBundleShortVersionString (or the release passed to Vaka.Options).
5. Verify
- Build and run the app once; confirm it compiles and launches. With
debug: trueinVaka.Options, the SDK logs send failures to the console. - Check that data arrives:
vaka --url "https://vaka.example.com" --project "your-project" issues(needsVAKA_TOKEN), the MCP tools, or the admin UI. Screen views and events show up in analytics within a few seconds of the next flush. - Optional crash test (run without the debugger attached, because Xcode's debugger catches the crash first):
add a temporary button that calls
fatalError("Vaka test crash"), tap it, relaunch the app, and look for anEXC_BREAKPOINTissue. Remove the button afterwards.
Notes
- Do not install another crash reporter alongside Vaka; only one can own the signal handlers reliably.
- To disable the SDK (e.g. for UI tests), pass
enabled: falseinVaka.Options. - API reference and behaviour: sdk-ios.md. Wire format: ingest.md.