Configuration
Learn how to configure the Toggly iOS SDK for different use cases.
Telemetry and session ownership
Use the existing Core service for frontend telemetry
and session token rotation. Telemetry is on by
default with an app key; configure enableTelemetry: false to opt out. metricsBaseUrl and
telemetryFlushIntervalMs are independent of definition refresh settings.
UI evaluation records checks; record usage and views explicitly from the real
interaction or navigation callback. Forward background lifecycle to the owning
service and dispose directly owned services at teardown. Storage and UI
companions reuse the owner; they do not create additional reporters.
TogglyConfig
The TogglyConfig struct configures the public SDK options below.
import TogglyCore
let config = TogglyConfig(
appKey: "your-app-key",
environment: "Production",
baseURI: "https://definitions.toggly.io",
identity: "user-123",
featureDefaults: ["feature-a": true, "feature-b": false],
showFeatureDuringEvaluation: false,
refreshInterval: 180,
useSignedDefinitions: false,
verifySignatures: false,
enableVariants: false,
connectTimeout: 10,
requestTimeout: 30,
storage: nil,
enableLiveUpdates: true,
maxSignatureAgeSeconds: nil,
groups: [],
claims: [:],
enableTelemetry: true,
metricsBaseUrl: "https://metrics.toggly.io",
telemetryFlushIntervalMs: 45_000,
onTelemetryDiagnostic: { code in print("Toggly telemetry: \(code)") },
instanceId: nil // Replace with a backend-minted token when available.
)
Initial groups and claims
Supply known context before initialization so the first evaluated request has the intended user, memberships and rule attributes:
let service = TogglyService(config: TogglyConfig(
appKey: "your-app-key",
identity: "user-123", // Stable user identifier.
groups: ["beta"], // Memberships used by targeting rules.
claims: ["plan": "pro"] // String rule attributes.
))
await service.initialize()
Empty groups/claims add no memberships/attributes. Omitted identity retains the stored/generated device ID; explicit empty identity stays empty. Context is copied at construction. Blank groups and empty claim names/values are omitted, with at most 20 claims sent in deterministic order. Use separate group entries, not commas inside group names. Per-check entity context is separate from this user targeting.
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
appKey | String? | nil | Your Toggly.io application key |
environment | String | "Production" | Environment name (e.g., "Staging", "Development") |
baseURI | String | "https://definitions.toggly.io" | API endpoint URL |
identity | String? | nil | User identifier for feature targeting |
featureDefaults | FeatureFlags | [:] | Default feature flag values |
showFeatureDuringEvaluation | Bool | false | Show content during initial loading |
refreshInterval | TimeInterval | 180 | Auto-refresh interval in seconds (0 to disable) |
useSignedDefinitions | Bool | false | Use signed definitions for enhanced security |
enableVariants | Bool | false | Fetch server-evaluated variants; enables getVariant/getVariantValue |
connectTimeout | TimeInterval | 10 | Connection timeout in seconds |
requestTimeout | TimeInterval | 30 | Request timeout in seconds |
storage | TogglyStorage? | nil | Custom storage implementation |
instanceId | String? | nil | Backend-minted token; nonblank token takes precedence over client targeting |
groups | [String] | [] | Initial targeting memberships |
claims | [String: String] | [:] | Initial targeting attributes |
verifySignatures | Bool | false | Verify signed definition responses |
maxSignatureAgeSeconds | Int64? | nil | Maximum signed-envelope age; nil or nonpositive disables freshness enforcement |
enableLiveUpdates | Bool | true | Enable WebSocket live updates |
enableTelemetry | Bool | true | Compact reporter enabled with an app key; false opts out |
metricsBaseUrl | String | "https://metrics.toggly.io" | Independent telemetry endpoint base URL |
telemetryFlushIntervalMs | Int | 45_000 | Telemetry base interval in ms; outside 30,000–60,000 falls back to 45,000 |
onTelemetryDiagnostic | TelemetryReporter.Diagnostic? (@Sendable (String) -> Void) | nil | Optional Sendable callback receiving bounded status-code strings without event payloads |
The diagnostic callback is optional (nil by default); the example supplies a
Sendable closure that logs only its status-code argument. It does not receive
keys, identities, or event payloads. Definition refresh uses seconds, while
telemetryFlushIntervalMs uses milliseconds.
Environments
Configure different environments for development, staging, and production:
#if DEBUG
let config = TogglyConfig(
appKey: "dev-app-key",
environment: "Development",
refreshInterval: 30 // More frequent refreshes in dev
)
#else
let config = TogglyConfig(
appKey: "prod-app-key",
environment: "Production",
refreshInterval: 180
)
#endif
Feature Defaults
Provide default values for offline scenarios or faster initial rendering:
let config = TogglyConfig(
appKey: "your-app-key",
featureDefaults: [
"new-dashboard": false, // Disabled by default
"dark-mode": true, // Enabled by default
"experimental": false,
"maintenance-banner": false
]
)
These defaults are used when:
- The SDK hasn't initialized yet
- The device is offline
- A specific feature key doesn't exist on the server
Storage Options
Memory Storage (Default)
Data is stored in memory and lost when the app terminates:
let config = TogglyConfig(
appKey: "your-app-key",
storage: nil // Uses MemoryStorage by default
)
UserDefaults Storage
Persists data across app launches:
import TogglyCore
let config = TogglyConfig(
appKey: "your-app-key",
storage: UserDefaultsStorage()
)
With custom UserDefaults suite:
let customDefaults = UserDefaults(suiteName: "group.com.myapp.shared")!
let config = TogglyConfig(
appKey: "your-app-key",
storage: UserDefaultsStorage(defaults: customDefaults, keyPrefix: "toggly_")
)
Custom Storage
Implement the TogglyStorage protocol for custom storage:
import TogglyCore
actor KeychainStorage: TogglyStorage {
func get(_ key: String) async -> String? {
// Read from Keychain
}
func set(_ key: String, value: String) async {
// Write to Keychain
}
func delete(_ key: String) async {
// Delete from Keychain
}
func clear() async {
// Clear all Toggly keys from Keychain
}
}
let config = TogglyConfig(
appKey: "your-app-key",
storage: KeychainStorage()
)
Initialization
Shared Instance
Use the shared instance for most apps:
import TogglyCore
// Configure once at app startup
Toggly.configure(config: TogglyConfig(
appKey: "your-app-key",
environment: "Production"
))
// Initialize (async)
Task {
await Toggly.shared.initialize()
}
Custom Instance
Create separate instances for different use cases:
let analyticsService = TogglyService(config: TogglyConfig(
appKey: "analytics-key",
environment: "Production"
))
let experimentService = TogglyService(config: TogglyConfig(
appKey: "experiment-key",
environment: "Production"
))
Task {
await analyticsService.initialize()
await experimentService.initialize()
}
Refresh Configuration
Automatic Refresh
Configure automatic background refresh:
let config = TogglyConfig(
appKey: "your-app-key",
refreshInterval: 60 // Refresh every 60 seconds
)
Set to 0 to disable automatic refresh:
let config = TogglyConfig(
appKey: "your-app-key",
refreshInterval: 0 // Manual refresh only
)
Manual Refresh
Trigger a manual refresh:
Task {
let response = await Toggly.shared.refresh()
print("Refreshed: \(response.status)")
}
Timeouts
Configure network timeouts:
let config = TogglyConfig(
appKey: "your-app-key",
connectTimeout: 5, // 5 seconds to establish connection
requestTimeout: 15 // 15 seconds for entire request
)
Signed Definitions
Enable signed definitions for enhanced security:
let config = TogglyConfig(
appKey: "your-app-key",
useSignedDefinitions: true
)
Signed definitions require additional server-side configuration. Contact Toggly support for setup.
Local-Only Mode
Use the SDK without connecting to Toggly.io:
let config = TogglyConfig(
featureDefaults: [
"feature-a": true,
"feature-b": false,
"feature-c": true
]
)
Toggly.configure(config: config)
await Toggly.shared.initialize()
// All evaluations use the defaults
let isEnabled = await Toggly.shared.isFeatureOn("feature-a") // true
App Lifecycle Integration
UIKit (AppDelegate)
import UIKit
import TogglyCore
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Toggly.configure(config: TogglyConfig(
appKey: "your-app-key",
environment: "Production"
))
Task {
await Toggly.shared.initialize()
}
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
Task {
await Toggly.shared.setAppState(.active)
}
}
func applicationDidEnterBackground(_ application: UIApplication) {
Task {
await Toggly.shared.setAppState(.background)
}
}
}
SwiftUI (App)
import SwiftUI
import TogglyCore
@main
struct MyApp: App {
@Environment(\.scenePhase) private var scenePhase
init() {
Toggly.configure(config: TogglyConfig(
appKey: "your-app-key",
environment: "Production"
))
Task {
await Toggly.shared.initialize()
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
.onChange(of: scenePhase) { _, newPhase in
Task {
switch newPhase {
case .active:
await Toggly.shared.setAppState(.active)
case .inactive:
await Toggly.shared.setAppState(.inactive)
case .background:
await Toggly.shared.setAppState(.background)
@unknown default:
break
}
}
}
}
}
Environment Variables
Store configuration in environment variables:
let appKey = ProcessInfo.processInfo.environment["TOGGLY_APP_KEY"] ?? ""
let environment = ProcessInfo.processInfo.environment["TOGGLY_ENVIRONMENT"] ?? "Production"
let config = TogglyConfig(
appKey: appKey,
environment: environment
)
Debug Information
Get debug info about the SDK state:
Task {
let debugInfo = await Toggly.shared.getDebugInfo()
print("Identity: \(debugInfo.identity ?? "none")")
print("App Key: \(debugInfo.appKey ?? "none")")
print("Environment: \(debugInfo.environment)")
print("Last Synced: \(debugInfo.lastSynced?.description ?? "never")")
print("Last Error: \(debugInfo.lastError ?? "none")")
print("Network: \(debugInfo.networkState?.isConnected == true ? "online" : "offline")")
print("App State: \(debugInfo.appState)")
}