Custom persistence provider

Replace PlayerPrefs session storage with a cloud backend, encrypted file store, or in-memory implementation by implementing IKeyValueStore or IPersistenceProvider.

The Convai Unity SDK stores session data — connection state, session IDs, resume tokens, and the editor end-user GUID — in PlayerPrefs by default via PlayerPrefsKeyValueStore. If PlayerPrefs works for your deployment, you do not need this page. Replace the persistence provider when you need cloud save, encrypted storage, server-side session management, or isolated storage for automated testing and CI.

What the SDK writes to storage

Key prefix
Contents
Why it matters

convai.session.*

Session ID and resume token

Allows the SDK to resume a disconnected session without restarting the AI turn

convai.end_user_id

Editor-only device GUID (fallback)

Used by DeviceEndUserIdProvider in the Unity Editor when hardware ID is unavailable

convai.prefs.*

User preferences (e.g., muted state)

Persists SDK-level settings across app launches

Replacing the persistence provider replaces where all of these are written and read. Your implementation must handle every key the SDK touches — the adapter pattern below ensures nothing is missed.

Persistence interfaces

IKeyValueStore — simple storage

namespace Convai.Domain.Abstractions
{
    public interface IKeyValueStore
    {
        string GetString(string key, string defaultValue = null);
        void   SetString(string key, string value);
        bool   HasKey(string key);
        void   DeleteKey(string key);
        void   Save();
    }
}

Save() is called after write operations. For in-memory stores it is a no-op; for file-backed stores it flushes to disk. Implement this interface for local storage scenarios where async operations are not needed.

builder.UsePersistence() accepts IPersistenceProvider. If your implementation is IKeyValueStore, wrap it in an adapter (see Adapter pattern below). Async operations (SyncAsync, SaveVersionedAsync) can return stub results if your backend is synchronous.

Which interface to implement:

Scenario
Implement

Local file, encrypted SQLite, in-memory

IKeyValueStore

Cloud save, server-side storage, multi-device sync

IPersistenceProvider

Existing IKeyValueStore with cloud sync added on top

Both — delegate sync/versioned ops to the cloud client

Supporting types

PersistenceResult

Member
Type
Description

Success

bool

Whether the operation succeeded.

ErrorMessage

string

Error description if Success is false.

Timestamp

DateTime

When the operation completed.

Version

long

Version number after the operation (for versioned ops).

PersistenceResult.Succeeded(version)

static

Creates a successful result.

PersistenceResult.Failed(error)

static

Creates a failed result.

ConflictResolutionStrategy

Used by SaveVersionedAsync to resolve write conflicts in async/cloud scenarios.

Value
Behavior

LastWriteWins

The most recently written value wins based on timestamp.

HighestVersionWins

The value with the higher version number wins.

LocalWins

Local data always overwrites remote.

RemoteWins

Remote data always overwrites local.

Manual

Returns conflict info to the caller for explicit resolution.

PersistenceOptions

Implementation examples

In-memory store (testing / CI)

Useful for automated tests and CI runs where persistent state between runs would corrupt results.

Encrypted file store

Satisfies compliance requirements that prohibit plain PlayerPrefs for session data.

Call Save() after every write, or flush periodically. Writes are buffered in memory — data written since the last Save() is lost on crash.

Adapter pattern for IKeyValueStore implementations

builder.UsePersistence() requires IPersistenceProvider. Use this adapter to wrap any IKeyValueStore:

DeleteAll(string prefix) returns a failed result in this adapter. The SDK calls DeleteAll during session reset operations. If your deployment requires full session resets, implement DeleteAll by iterating your store's keys and removing those that match the prefix.

Register the provider

Troubleshooting

Symptom
Likely cause
Fix

Session does not resume after restart

GetString returns null for session keys on reload

Ensure Save() is called synchronously before the application quits. Subscribe to Application.quitting if needed.

NullReferenceException inside IPersistenceProvider implementation

Async methods are called before the store is initialized

Initialize the backing store in the provider's constructor, before UsePersistence() is called.

Data loss on crash

SetString writes are buffered in memory and Save() is not called

Call Save() after every write, or flush on a periodic timer.

Session reset does not clear all SDK data

DeleteAll(prefix) returns a failed result in the adapter

Implement DeleteAll by iterating your store's key collection and removing prefix-matching entries.

Next steps

Credentials, identity, and storageRuntime module systemImplement a custom module

Last updated

Was this helpful?