Documentation Index
Fetch the complete documentation index at: https://docs.ditto.live/llms.txt
Use this file to discover all available pages before exploring further.
Overview
This guide covers the essential changes needed to migrate your Ditto C# app from v4 to v5. The main architectural shift is moving from identity-based initialization to a four-phase configuration model with async/await APIs. Also required: v5 uses DQL (Ditto Query Language) for all data operations. See the DQL Migration Guide for query migration steps.AI Agent Prompt
Use this prompt when working with an AI coding assistant to migrate your Ditto C# app from v4 to v5.Copy AI Migration Prompt (Click to Expand)
Copy AI Migration Prompt (Click to Expand)
Ditto Instance Initialization
v5 separates initialization into four distinct phases for better clarity and control:- Clear separation of connection, initialization, and authentication concerns
- Async initialization prevents blocking the main thread
- No workarounds needed (transport config, DQL strict mode, v3 sync disable)
- Type-safe configuration with compile-time validation
Replace Initialization
Replace Key changes:
new Ditto() constructor with await Ditto.OpenAsync(config).- Use
DittoConfiginstead ofDittoIdentity appIdβdatabaseId.OnlinePlaygroundβnew DittoConfigConnect.Server(new Uri(...)).OfflinePlaygroundβnew DittoConfigConnect.SmallPeersOnly(privateKey: null)- Add
awaitfor async initialization - Remove
UpdateTransportConfig,DisableSyncWithV3(), and DQL strict mode queries
Update Authentication
Set authentication handler separately after initialization.Key changes:
- Authentication now uses
DittoAuthenticationExpirationHandlerasync delegate - Delegate signature:
Task DittoAuthenticationExpirationHandler(Ditto ditto, TimeSpan timeUntilExpiration) - First parameter is the
Dittoinstance β access auth viaditto.Auth.LoginAsync() - Second parameter is
TimeSpanβ checktimeUntilExpiration == TimeSpan.Zerofor initial auth - Handler called when auth required (
TimeSpan.Zero) or token expiring (> TimeSpan.Zero) - Use
DittoAuthenticationProvider.Developmentfor playground tokens - Custom auth:
new DittoAuthenticationProvider("your-provider")
Additional Changes
DQL Strict Mode Behavior Change
Choose the appropriate migration path based on your current v4 configuration:Currently Using DQL with DQL_STRICT_MODE=true (v4 default)
Currently Using DQL with DQL_STRICT_MODE=true (v4 default)
If youβre currently using the v4 default (
DQL_STRICT_MODE=true), you must explicitly set strict mode to true in v5 before starting sync or executing any queries.To migrate to
DQL_STRICT_MODE=false (the new v5 default), contact Ditto Customer Support for guidance.Currently Using DQL with DQL_STRICT_MODE=false
Currently Using DQL with DQL_STRICT_MODE=false
If you explicitly set
DQL_STRICT_MODE=false in v4, no changes are required.v5 uses DQL_STRICT_MODE=false as the default, so your existing DQL queries will behave identically. You can upgrade freely.Optional: You can remove the explicit ALTER SYSTEM SET DQL_STRICT_MODE = false statement in v5 since this is now the default behavior.Currently Using Legacy Query Builder
Currently Using Legacy Query Builder
The Legacy Query Builder has been removed in v5. All queries must be converted to DQL before upgrading.Good news: Legacy Query Builder functionality has 1:1 support with
DQL_STRICT_MODE=false (the v5 default), making migrations straightforward.Migration Steps:-
In v4: Set
DQL_STRICT_MODE=falseafter initialization: - Convert all queries from Legacy Query Builder to DQL See the C# LegacyβDQL Migration Guide for detailed conversion examples.
-
Upgrade to v5
No DQL configuration changes requiredβv5 defaults to
DQL_STRICT_MODE=false.
Default Persistence Directory
v5 includes the database ID in the default directory name:ditto-{databaseId} instead of ditto
To maintain v4 compatibility:
WifiAwareConfig (new)
DittoSyncPermissions (MAUI only)
Back Pressure (RegisterObserver with signalNext)
Use theAction<DittoQueryResult, Action> overload of RegisterObserver for manual back-pressure control. The signalNext Action must be called when your handler is ready for the next update.
Coalescing: changes that occur while your handler executes are coalesced β you get the latest state.
signalNext parameter is a plain Action (no named type).
Disk Usage
Ditto Logger Changes
In v5, you now set logging enabled or disabled using theDittoLogger.IsEnable property.
Namespace Changes
Types have moved to new namespaces in v5:| Type | Old Namespace | New Namespace |
|---|---|---|
DittoLogLevel, DittoLogger | DittoSDK | DittoSDK.Logging |
DittoStoreObserver, DittoQueryResult, DittoQueryResultItem | DittoSDK | DittoSDK.Store |
DittoSyncSubscription | DittoSDK | DittoSDK.Sync |
DittoPeer, DittoPresenceGraph, DittoPresence, DittoConnection, DittoConnectionType | DittoSDK | DittoSDK.Transport |
DittoAuthenticator, DittoAuthenticationProvider, DittoAuthenticationExpirationHandler | DittoSDK | DittoSDK.Auth |
DittoException | DittoSDK | DittoSDK.Exceptions |
DittoDiskUsageItem | DittoSDK | DittoSDK.DiskUsage |
Property changes
| Type | V4 Property | V5 Property |
|---|---|---|
DittoPeer | PeerKeyString | PeerKey |
DittoPeer | Os (string) | Os (DittoPeerOS?) β type changed |
DittoPeer | IsDittoCloudConnected | IsConnectedToDittoServer |
DittoConnection | PeerKeyString1 | PeerKey1 |
DittoConnection | PeerKeyString2 | PeerKey2 |
DittoConnection | ApproximateDistanceInMeters | REMOVED β no replacement |
API Renames
| v4.14 | v5.0 |
|---|---|
ditto.IsSyncActive | ditto.Sync.IsActive |
new Ditto(DittoIdentity, string) | Ditto.Open(DittoConfig) or Ditto.OpenAsync(DittoConfig) |
Ditto.StartSync() | Ditto.Sync.Start() |
Ditto.StopSync() | Ditto.Sync.Stop() |
Ditto.SiteId | Removed β use peer identity from presence graph |
Ditto.SDKVersion | Ditto.Version (static property) |
Ditto.Identity property | Removed β use DittoConfig.DatabaseId |
IDittoAuthenticationDelegate interface | DittoAuthenticationExpirationHandler async delegate |
DittoAuthenticator.AuthenticationDelegate | DittoAuthenticator.ExpirationHandler |
DittoAuthenticator.LoginWithToken() / Login() / LoginWithCredentials() | DittoAuthenticator.LoginAsync() |
DittoStore.Write(Action<DittoWriteTransaction>) | DittoStore.TransactionAsync(Func<DittoTransaction, Task>) |
DittoLiveQuery | DittoStoreObserver |
DittoSubscription | DittoSyncSubscription |
DittoDiskUsage.Exec() | DittoDiskUsage.Item (property) |
DittoDiskUsageChild | DittoDiskUsageItem |
DittoError class | Exception types in DittoSDK.Exceptions namespace |
DittoException (in DittoSDK) | DittoException (in DittoSDK.Exceptions) |
PeerKeyString | PeerKey |
IsDittoCloudConnected | IsConnectedToDittoServer |
APIs Removed
| Removed API | Notes |
|---|---|
Ditto.DisableSyncWithV3() | v3 peer-to-peer sync interoperability dropped entirely |
Ditto.GetTransportDiagnostics() | Transport diagnostics removed |
Ditto.SiteId | Removed β use peer identity from presence graph |
Ditto.SDKVersion (instance) | Use static Ditto.Version instead |
DittoLiveQueryEvent.Hash() | Document change hashing removed; no v5 equivalent |
DittoLiveQueryEvent.HashMnemonic() | Human-readable hash mnemonic removed |
DittoWriteStrategy.UpdateDifferentValues | No ON ID CONFLICT DO UPDATE_DIFFERENT_VALUES in DQL |
IDittoAuthenticationDelegate.AuthenticationStatusDidChange() | Status observation now via DittoAuthenticator.ObserveStatus() |
DittoUpdateResult (and subclasses) | DQL mutations return affected-row counts only |
DittoPendingCursorOperation.ObserveLocalWithNextSignal() | Use RegisterObserver with signalNext overload instead |
DittoSmallPeerInfoSyncScope enum | Sync scope is now a DQL ALTER SYSTEM string setting |
DittoSortDirection enum | Use ASC/DESC in DQL ORDER BY; no enum type in v5 |
DittoStore.FetchAttachment(DittoAttachmentFetchDelegate) | Use async Func/Task overload |
DittoIdentityType enum | Replaced by DittoConfig factory methods |
New APIs in v5
Ditto.Sync.Start()/Ditto.Sync.Stop()β replacesDitto.StartSync()/StopSync()Ditto.Version(static) β replacesDitto.SDKVersionDitto.DeviceNameβ custom peer device name (get/set)DittoAuthenticationExpirationHandlerβ async delegate replacingIDittoAuthenticationDelegateDittoAuthenticator.ObserveStatus()β returnsIDisposableobserver for auth status changesDittoAuthenticator.Logout()β explicit logout supportDittoSDK.Authnamespace β all authentication types moved hereDittoStore.TransactionAsync()β async DQL transaction usingDittoTransactionDittoStoreObserverβ replacesDittoLiveQueryDittoSyncSubscriptionβ replacesDittoSubscription; exposesQueryString,QueryArguments,Cancel()DittoQueryResultItemβ new type for single DQL result row with.ValuepropertyWifiAwareConfig/WifiAwareConfigBuilderβ new transport config for Android Wi-Fi Aware (MAUI)DittoSyncPermissions(MAUI) βRequestPermissionsAsync()for Bluetooth/Wi-Fi permissionsDittoDiskUsageItemβ replacesDittoDiskUsageChild; usesSystem.Text.JsonDittoDiskUsage.Item(property) β replacesExec()methodDittoSDK.DiskUsagenamespace β disk usage types moved hereDittoSDK.Exceptionsnamespace β exception types moved here
Migration Checklist
Update NuGet Package
- Update
DittoSDKNuGet package to v5.x
Fix Namespace Imports
- Add
using DittoSDK.Auth;where auth types are used - Add
using DittoSDK.Exceptions;whereDittoExceptionis caught - Add
using DittoSDK.Logging;whereDittoLoggerorDittoLogLevelare used - Update any other namespace references per compiler errors
Initialization
- Replace
new Ditto()withawait Ditto.OpenAsync(config) - Create
DittoConfigwithdatabaseIdandconnectmode - Add
awaitfor async initialization - Update
.OnlinePlaygroundβnew DittoConfigConnect.Server(new Uri(...)) - Update
.OfflinePlaygroundβnew DittoConfigConnect.SmallPeersOnly(privateKey: null) - Remove
UpdateTransportConfigcalls - Remove
ALTER SYSTEM SET DQL_STRICT_MODE = falsequeries (this is now the v5 default); if your app used the v4 default (strict mode = true), addALTER SYSTEM SET DQL_STRICT_MODE = trueBEFORESync.Start()to maintain v4 behavior
Update Sync API
- Replace
ditto.StartSync()βditto.Sync.Start() - Replace
ditto.StopSync()βditto.Sync.Stop() - Remove
DisableSyncWithV3()calls
Update Authentication
- Replace
IDittoAuthenticationDelegateimplementations withDittoAuthenticationExpirationHandlerasync delegate - Replace
LoginWithToken()/Login()/LoginWithCredentials()βLoginAsync() - Set
ExpirationHandlerdelegate after initialization - Use
DittoAuthenticationProvider.Developmentfor playground tokens - Remove authentication from identity configuration
Observers
- Replace
DittoLiveQuery/.ObserveLocal()withDittoStoreObserver/Store.RegisterObserver() - Observer callbacks receive
DittoQueryResultwith.Itemsproperty - Use
observer.Cancel()for cleanup (replacesliveQuery.Stop()) - If processing is expensive, use
Action<DittoQueryResult, Action>signalNext overload for back-pressure control
Breaking Changes
- Set
PersistenceDirectoryif maintaining v4 directory structure - Update
PeerKeyStringβPeerKey - Update
IsDittoCloudConnectedβIsConnectedToDittoServer - Replace
DittoStore.Write()βDittoStore.TransactionAsync()(callback usesDittoTransaction, notDittoWriteTransaction) - Add
using DittoSDK.Auth;,using DittoSDK.Exceptions;as needed -
UpdateTransportConfig()callback parameter changed fromAction<DittoTransportConfig>toAction<DittoTransportConfigBuilder>(builder pattern)
Update Renamed Types
-
Ditto.SiteIdβ Removed (use peer identity from presence graph) -
Ditto.SDKVersionβDitto.Version(static) -
DittoDiskUsageChildβDittoDiskUsageItem -
DittoDiskUsage.Exec()βDittoDiskUsage.Item(property) -
DittoSubscriptionβDittoSyncSubscription -
DittoLiveQueryβDittoStoreObserver
Verification
- Build compiles with zero errors
- No deprecated API warnings
- DQL strict mode set correctly for your data model
- Auth handler fires before first sync
- Observers update UI immediately
- No memory leaks on navigation
Common Pitfalls
-
DQL_STRICT_MODEsilent change: Default flipped. Objects merge field-by-field instead of replacing whole. SetDQL_STRICT_MODE=trueBEFORESync.Start()if migrating existing data. -
Missing
awaitonExecuteAsync:ExecuteAsyncreturnsTask<DittoQueryResult>. Forgettingawaitcauses silent no-op. -
DittoSortDirectionremoved:Sort("field", DittoSortDirection.Ascending)wonβt compile. UseORDER BY field ASCin DQL. -
DittoIdentityTypeenum: Removed entirely. Code checking identity type wonβt compile. -
LoginWithToken()removed: Must useLoginAsync(). The sync overloads are gone. -
Write()vsTransactionAsync(): The callback signature changed completely β the callback now receivesDittoTransaction(notDittoWriteTransaction) and uses async DQL, not collection-based mutations. -
ExecuteAsyncargument type:ExecuteAsynctakesDictionary<string, object>β anonymous objects likenew { color = "blue" }are NOT accepted. Usenew Dictionary<string, object> { ["color"] = "blue" }. Note:RegisterObserverandRegisterSubscriptionDO have overloads accepting anonymousobjectarguments. -
ObserveLocalWithNextSignal()removed from collection API: Back-pressure IS supported in v5 via theRegisterObserveroverload that takesAction<DittoQueryResult, Action>where the secondActionis thesignalNextcallback.