Custom persistence provider
Replace the default session storage with your own remote service, an encrypted file store, or an in-memory implementation for tests.
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
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.
IPersistenceProvider — full-featured storage
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:
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
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.
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
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 moduleLast updated
Was this helpful?