Choose a mobile SDK
Connect iOS, Android, React Native, or Flutter activity to the same customer story.
On this page
Anectico's mobile SDKs share the same identity, session, event, error, crash, retry, and propagation contract. Choose the SDK that owns your application's runtime:
| Application | Guide | Minimum or key requirement |
|---|---|---|
| Native Apple | iOS | iOS 15+, Swift Package Manager |
| Native Android | Android | minSdk 24, Kotlin and Gradle |
| React Native | React Native | React Native 0.73+, native build setup |
| Flutter | Flutter | Flutter plugin with native Android and iOS SDKs |
During early access, some registry packages may not be published. Use the source coordinate or package artifact shown in the platform onboarding instructions.
Shared behavior
Configure the SDK once at application startup, identify after login, and call reset at logout. A
stable distinct_id connects product events, caught errors, native crashes, and backend requests to
one customer. Use an id from your own system: an email address sent as a property is stored as
[REDACTED] (see What Anectico redacts and withholds). On Android, iOS, and their React Native bridges, reset also clears global
error-user context and buffered breadcrumbs so prior-account diagnostics cannot cross a shared-device
logout boundary.
Use separate project-bound credentials: apiKey is a native_ingest key with ingest:write;
captureApiKey is a mobile_capture key with analytics:write. See
native intake keys. Never include server, read, management
or agent credentials in an application build. Without a capture key, product and identity collection
is disabled locally while OTLP remains available. Supplied malformed or shared capture keys reject
configuration. Mobile capture is public-client provenance, not trusted server authority.
Each SDK persists diagnostic events before batching and retries failed delivery with stable message IDs. Fatal native crashes are spooled and sent on the next launch. Optional structural replay has independent controls; source coverage and device qualification remain separate.
Native Android additionally exposes durable Anectico.captureLog OTLP logs. It snapshots occurrence
time and customer, session, app and device context before an atomic sandbox write and retries across
process recreation. iOS, React Native, and Flutter do not yet expose the same public log API.
Remote flags and headless surveys
Bounded remote flags and application-rendered surveys are available in the canonical Swift source and Kotlin source. React Native and Flutter delivery use typed native bridges. All four SDKs expose these optional APIs.
Alongside core tests, an isolated Android API 36 x86_64 emulator run has built and installed standard Android, React Native and Flutter apps. Their flag exposures and survey answers join one person while preserving each original session. This covers the tested Android emulator configuration; Apple runtimes, physical devices and published-package installation require separate verification.
Start with the guide for your runtime:
- iOS flags and headless surveys
- Android flags and headless surveys
- React Native flags and surveys
- Flutter flags and headless surveys
Your application renders the survey and reports actual display, response or dismissal. Test the linked runtime, delivery and stored person and session evidence independently. Native replay has its own setup below.
Hosts and four credentials
Both native SDKs use applicationEndpoint, projectID, flagDeliveryApiKey,
surveyDeliveryApiKey and surveyScreenPaths. applicationEndpoint is the application HTTPS
host for /api/v1/flags/evaluate and survey delivery. Ingestion endpoint is a different base
for OTLP and /api/v1/capture; never assume the two hosts are interchangeable.
| Option | Project-bound purpose | Exact scope |
|---|---|---|
apiKey |
native_ingest |
ingest:write |
captureApiKey |
mobile_capture |
analytics:write |
flagDeliveryApiKey |
CLIENT_DELIVERY |
flags:evaluate |
surveyDeliveryApiKey |
CLIENT_DELIVERY |
surveys:respond |
Issue two delivery keys by default using the existing preview and confirmation flow.
CLI purpose is client_delivery. Keep a stable idempotency key and fixed expiry per proposal;
the secret is shown once. Agent and operator credentials stay outside application builds.
Never embed flags:read, MCP scopes or a server APPLICATION key with experiments:deliver,
and never widen intake permissions. A delivery key cannot send exposure events.
Use the private HTTPS hosts supplied by your operator and the approved private network path on every testing device. Configure both endpoints explicitly; a successful core test does not prove the device can reach either. Do not expose a private API publicly to make setup work.
Current identity and targeting
Use the same stable signed-in ID in the same project on web and mobile. Each device has its own anonymous ID and session. Changed exact identity clears groups; identifying the same exact ID preserves them. Identify updates local state only after admission and joins aliases asynchronously. Rejected admission leaves the old identity; an admitted reset rotates identity and session. Read aliases and the timeline before claiming that devices joined. A canonical person joins evidence; flag hashing still uses the request's distinct ID. Equal variants require the same current identity and targeting context, not merely aliases of one person.
With disabled analytics or no capture key, evaluation requires an explicit current binding:
Swift setEvaluationBinding(FlagEvaluationBinding(distinctID:personProperties:groups:)), Kotlin
setEvaluationBinding(distinctId, personProperties, groups). Without one the outcome is
identity_unavailable and no request is sent. Binding replacement and clearing invalidate old
decisions independently of telemetry identity and session. Withdrawal retires implicit work;
renewed consent does not apply an earlier refused identify or reset. An explicit decision for B
cannot emit an exposure attributed to frozen telemetry subject A.
Request properties are declarations. Client keys cannot read stored person or group properties, cohort membership or event content; withheld sources stay withheld. Protected targeting requires your own authenticated backend with the needed authority, never broader embedded permissions.
Evaluate selected keys and inspect states
Your agent uses its own flags:evaluate credential and mcp:read for the read gateway:
anectico flags evaluate user_8842 --key enabled --key disabled --key checkout_variant \
--key missing --key dependency_error --person-properties '{"plan":"pro"}' \
--groups '{"company":"acme"}' --project PROJECT --json
MCP: execute_read_action with action evaluate_client_flags, exact project_id, distinct_id,
flag_keys, person_properties and groups. Repeat --key; each context flag takes one JSON
object. Independently configure the test definitions before expecting these example results:
| Selected key | Expected state and value |
|---|---|
enabled |
evaluated, boolean true |
disabled |
evaluated, boolean false |
checkout_variant |
evaluated, string treatment |
missing |
unavailable_flag, no value |
dependency_error |
evaluation_error, server default marked is_fallback, no version |
A name alone does not create a fixture or cause an error. All absent or false results prove no
positive parity. Native boolean reads require an explicit application fallback and retain its
use and the original reason. Decisions expire at original evaluated_at plus 30 seconds and
remain only in memory. Bootstrap must match the original project, current identity, context,
keys and generation; installing it cannot renew its lifetime. Reload selects 1–100 unique keys,
at most ten groups and 16 KiB of targeting context. No all-flags or local-definition client mode exists.
Exposures are separate evidence
Served value and payload reads may admit $feature_flag_called when product consent, a
mobile_capture key and the matching telemetry context permit it. Status inspection emits
nothing. Preserve the evaluated definition's exact definitionVersion string from reload,
cache and bootstrap through exposure $feature_flag_version; do not round its six fractional
digits or substitute decision_context_id. Unavailable, fallback and unversioned decisions
invent no version.
Exposure metadata includes $feature_flag, $feature_flag_response,
$feature_flag_evaluation_status, $feature_flag_is_fallback, $feature_flag_evaluation,
$feature_flag_input_context and optional $feature_flag_test_case_id. Deduplication includes
identity, key, value and exact version: equal values at different versions must yield separate
exposures. Immutable retries retain the original message ID, identity, session, time and metadata.
Verify the original application's message_id, source_id, distinct ID and session using
anectico doctor events --file verify.json (verify_integration_events through the read gateway)
and the flag verifier. Read version and input
context agreement independently; unversioned evidence is not_comparable. A current decision,
cache freshness or aggregate flush does not prove that exposure was served or stored.
The current native flag APIs expose no public per-exposure receipt, so exact attribution of a
particular native attempt remains unqualified. An ID found only after storage cannot fill that gap.
Flag evaluation has no proof link.
Headless display, delivery and storage
Map explicit screen names with surveyScreenPaths, for example Checkout to /checkout or
/mobile/checkout; never include raw URLs or route arguments. Your app renders questions,
records shown only after actual display, and submits or dismisses only on the user's action.
The native guides show single choice, multiple choice, integer rating including zero, and open text.
The server owns audience and flag targeting, sampling, caps and cooldown. Refresh coalesces with
a five-second floor and no unconditional foreground polling. At most three offers describe one
screen, identity and time. A valid empty list differs from a transport error or
SURVEY_IDENTITY_COVERAGE_UNSUPPORTED; permanent delivery refusal closes the handle.
Keep the original delivery ID, offered revision, answers, identity and optional source session immutable through uncertain retries on the same handle. Do not generate a new ID or change answers to retry. Identity or context changes, screen replacement, withdrawal, shutdown and reconfiguration retire incompatible handles and clear answer memory. There is no new persisted answer spool and the SDK emits no duplicate survey outcome events.
The clients freeze source sessions and recognize the coverage refusal. Source-session propagation and complete-alias refusal are implemented; independently verify stored correlation on the deployment and runtime you use. Earlier browser or REST survey results do not qualify the client-delivery authority change. Always provision the literal client-delivery purpose.
An accepted receipt precedes event visibility. Read retained delivery evidence,
stored answers and the joined person timeline separately. Multiple selected options create multiple
response event rows; those rows are not response totals. Agent definitions need surveys:read;
mutations additionally need surveys:write, mcp:write and server preview and confirmation. Results
and answers need analytics:read, analytics:query, persons:read, agents:content:read and
mcp:read over MCP. SDK delivery has only surveys:respond; there is no agent visitor-submission command.
Privacy and qualification limits
Answers and exposure events remain subject to content permissions and current policy, event retention, person erasure and export and project or workspace deletion. Local withdrawal clears pending memory and prevents new collection; it does not erase previously stored data. Evidence lifetime follows its source: a platform-held result or receipt cannot preserve erased or expired answers. Chat copies or downloaded exports already delivered outside the platform cannot be recalled. Unavailable, withheld, erased and not observed are separate states. Use existing person and result proof links where returned. Native replay is a separate capability and is not qualified here.
Mobile parity is declared and unqualified. Record each SDK, operating system, runtime, source revision, package installation, copied-source equality and required stored readback independently as pass, fail, skip or blocked. Core tests, source presence, package coordinates and a flush do not establish runtime support or installability. No prices or plan allowances are implied.
Native structural replay
Optional recorders and adapters use native_structural version 1 (NR1). Each requires independent
replay consent, an explicit current owner, current native capture policy and native store permission,
and a separate project-bound replay_upload key with only replay:write.
| Runtime | Setup and source coverage |
|---|---|
| iOS | Explicit UIKit source; unqualified SwiftUI and embedded regions remain gaps. |
| Android | Android View recorder; Compose, custom and embedded regions remain gaps. |
| React Native | Host refs and component privacy; inspect the source matrix for the hosts you use. |
| Flutter | Dart structural adapter; native view traversal cannot observe Dart UI. |
Replay does not start with analytics consent or a flag identity. Follow recording and verification and native recording privacy. An upload receipt alone does not establish retained playback, complete source coverage or physical-device qualification.
Check plan allowances
Read the current plan allowances and limits before a rollout. Your agent's budgets and spend controls are separate from SDK delivery outcomes. Recorder safety ceilings are not sustainable device rates or included allowances.
Record an explicit agent run
All four mobile SDKs provide startAgentRun to record a source-declared invoke_agent span.
Start it when your app's agent begins work and end it once with the real terminal outcome; do not
use it as a generic checkout or network span. Options, status enums, validation, and return values
vary by language:
Releases and readable stacks
Set a stable release and optional dist for every production build. Upload the matching artifacts:
- iOS: dSYM;
- Android: R8 or ProGuard
mapping.txt; - React Native: native artifacts plus Metro source maps; and
- Flutter: native artifacts. Obfuscated Dart frames are not currently symbolicated by Anectico.
Release and distribution values must match the captured error exactly. The same release value
also stamps the reserved $release/$app_version string properties on every captured event, not
only on errors — see each SDK's own guide and
Reserved event properties.
Connect a mobile request to the backend
Use the SDK's propagation helper to add W3C traceparent and baggage to calls sent to a trusted,
instrumented backend. Do not attach identity baggage to arbitrary third-party requests. The backend
must treat customer identity headers from public devices as untrusted and verify or replace them at
its gateway.
Verify
Launch a release build, identify a test customer, capture one event and one caught error, and make one
request to an instrumented backend. Ask your agent to read that customer's timeline
(get_person_timeline over MCP, or anectico persons timeline <customer-id>) and confirm the mobile
and backend evidence is connected under the same ID. The MCP result carries a link that opens the
customer as a read-only proof page. Then ask your agent for the issue
(get_issue, or anectico issues view <issue-id>) and verify its stack is readable after uploading
symbols.