Theme, language, logging, and telemetry
Theme
Configure the native UI palette when constructing the session:
import UIKit
import GraffityARCloud
@MainActor
func makeBrandedSession(accessToken: String) throws -> GraffityARCloud {
let theme = GraffityARTheme(
primary: .systemIndigo,
surface: .white,
onSurface: .black,
onSurfaceSecondary: .darkGray,
danger: .systemRed
)
return try GraffityARCloud(configuration: GraffityARCloudConfiguration(
accessToken: accessToken,
theme: theme
))
}| Slot | Default | Use |
|---|---|---|
primary | #19A6B6 | Continue, dismiss, and retry fills; choose a fill readable under white titles. |
secondary | #002D40 | Reserved; currently has no visible effect. |
surface | #FFFFFF | Native panels, including navigation banner and minimap sheet. |
onSurface | #1A1A1A | Primary panel text and glyphs. |
onSurfaceSecondary | #8A8A8E | Secondary panel text. |
danger | #FF3B30 | Navigation exit fill. |
GraffityARTheme.default uses the brand palette. init?(primaryHex:secondaryHex:) accepts six-digit RGB strings, optionally prefixed with #, returning nil for invalid input. Neutral slots retain defaults; use the color initializer to customize them. Pass fixed colors when your palette should stay stable over the camera feed.
The native palette is shared internally: overlapping sessions use the most recently installed palette. Prefer one active session. There is no public theme API for styling the destination web map.
Language
The SDK bundles English and Thai. It matches preferred languages against bundled languages and falls back to English. Missing strings fall back to English, then the key.
GraffityARLocalizationProvider declares string(forKey:locale:) -> String?, but the current configuration has no provider injection point and no public locale setter exists. Host apps cannot install custom translations yet. Localize your permission descriptions separately with your app’s InfoPlist.strings.
Logging
GraffityARLogging.default uses the SDK’s os.Logger adapter at .info and above. Severities are .debug, .info, .warning, and .error, ordered lowest to highest. Supply a Sendable sink with .custom(_:minimum:):
import OSLog
import GraffityARCloud
final class AppARLogger: GraffityARLogger {
private let logger = Logger(subsystem: "com.example.app", category: "ARCloud")
func log(
_ level: GraffityARLogLevel,
_ message: @autoclosure () -> String,
file: String,
line: Int
) {
let text = message()
switch level {
case .debug: logger.debug("\(text, privacy: .public)")
case .info: logger.info("\(text, privacy: .public)")
case .warning: logger.warning("\(text, privacy: .public)")
case .error: logger.error("\(text, privacy: .public)")
}
}
}
@MainActor
func makeLoggedSession(accessToken: String) throws -> GraffityARCloud {
try GraffityARCloud(configuration: GraffityARCloudConfiguration(
accessToken: accessToken,
logging: .custom(AppARLogger(), minimum: .debug)
))
}Sinks may be called from different actors; make them thread-safe. The SDK filters severity and scrubs recognized token/coordinate patterns before forwarding messages. Custom sinks receive a category prefix; file and line identify the source.
sdk.setLogger(logger) changes the sink at runtime. Passing nil restores the default sink. Neither changes the configured minimum severity.
Disable all output
Although .disabled exists, 5.1.1 still routes error-level records to fallback os.Logger for that setting. For silence, use a no-op sink:
import GraffityARCloud
final class SilentARLogger: GraffityARLogger {
func log(
_ level: GraffityARLogLevel,
_ message: @autoclosure () -> String,
file: String,
line: Int
) {}
}
@MainActor
func makeSilentSession(accessToken: String) throws -> GraffityARCloud {
try GraffityARCloud(configuration: GraffityARCloudConfiguration(
accessToken: accessToken,
logging: .custom(SilentARLogger(), minimum: .error)
))
}Telemetry
GraffityARTelemetry.default is .disabled. Opt in with telemetry: .enabled or sdk.setTelemetryEnabled(true). Turn analytics off with sdk.setTelemetryEnabled(false). Connect this to your app’s user preference or consent flow where appropriate.
Telemetry controls analytics. It does not disable camera-frame localization, location lookup, assets, map requests, or player collection requests required for the experience. userId scopes content and collection independently of telemetry.