Skip to main content

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​

OptionTypeDefaultDescription
appKeyString?nilYour Toggly.io application key
environmentString"Production"Environment name (e.g., "Staging", "Development")
baseURIString"https://definitions.toggly.io"API endpoint URL
identityString?nilUser identifier for feature targeting
featureDefaultsFeatureFlags[:]Default feature flag values
showFeatureDuringEvaluationBoolfalseShow content during initial loading
refreshIntervalTimeInterval180Auto-refresh interval in seconds (0 to disable)
useSignedDefinitionsBoolfalseUse signed definitions for enhanced security
enableVariantsBoolfalseFetch server-evaluated variants; enables getVariant/getVariantValue
connectTimeoutTimeInterval10Connection timeout in seconds
requestTimeoutTimeInterval30Request timeout in seconds
storageTogglyStorage?nilCustom storage implementation
instanceIdString?nilBackend-minted token; nonblank token takes precedence over client targeting
groups[String][]Initial targeting memberships
claims[String: String][:]Initial targeting attributes
verifySignaturesBoolfalseVerify signed definition responses
maxSignatureAgeSecondsInt64?nilMaximum signed-envelope age; nil or nonpositive disables freshness enforcement
enableLiveUpdatesBooltrueEnable WebSocket live updates
enableTelemetryBooltrueCompact reporter enabled with an app key; false opts out
metricsBaseUrlString"https://metrics.toggly.io"Independent telemetry endpoint base URL
telemetryFlushIntervalMsInt45_000Telemetry base interval in ms; outside 30,000–60,000 falls back to 45,000
onTelemetryDiagnosticTelemetryReporter.Diagnostic? (@Sendable (String) -> Void)nilOptional 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
)
note

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)")
}