This content is for SDK V4. For the latest version, see the V5 documentation.
This API is in preview and will become the standard way to initialize Ditto instances in v5, replacing the legacy
DittoIdentity-based initialization.DittoConfig structure and the Ditto.open() method.
Overview
The new initialization approach provides:- Cleaner, more intuitive API design
- Better separation of concerns between identity and connectivity
- Improved type safety and error handling
- Support for both async and sync initialization patterns
- Renamed terminology: “App ID” is now “Database ID” to better reflect its purpose
Terminology Changes in v5
App → Database: As part of v5, we’re renaming “App” to “Database” throughout the SDK to better reflect what these identifiers represent. Your Ditto instance connects to a specific database, not an application.
- App ID → Database ID
- App (in portal) → Database
- This change will be rolled out platform-wide Q4 2025.
Creating a new Ditto instance
Config and Open
DittoConfig encapsulates all the parameters required to configure a Ditto
instance. Once you have a configuration, you can then pass that configuration to
the Ditto.open() method to initialize a Ditto instance.
The new Ditto.open(config) method is both asynchronous and fallible, meaning:
- Asynchronous: The method returns a Promise/Future that must be awaited, allowing Ditto to perform initialization tasks without blocking your application
- Fallible: Initialization can fail for various reasons, requiring proper error handling
public struct DittoConfig {
/// The unique identifier for the Ditto database (previously "App ID")
public var databaseID: String // Should be a UUID
/// The connectivity configuration for this Ditto instance
public var connect: DittoConfigConnect
/// The persistence directory used by Ditto to persist data
public var persistenceDirectory: URL?
/// Configuration for experimental features
public var experimental: DittoConfigExperimental
}
interface DittoConfig {
// The unique identifier for the Ditto database (previously "App ID")
databaseID: string;
// The connectivity configuration for this Ditto instance
connect: DittoConfigConnect;
// The persistence directory used by Ditto to persist data
persistenceDirectory?: string;
// Configuration for experimental features
experimental?: DittoConfigExperimental;
}
data class DittoAndroidConfig(
val context: Context,
// The unique identifier for the Ditto database (previously "App ID")
val databaseId: String,
// The connectivity configuration for this Ditto instance
val connect: DittoConfigConnect,
// The persistence directory used by Ditto to persist data
val persistenceDirectory: String? = null,
// Configuration for experimental features
val experimental: DittoConfigExperimental = DittoConfigExperimental()
)
public class DittoAndroidConfig {
private final Context context;
// The unique identifier for the Ditto database (previously "App ID")
private final String databaseId;
// The connectivity configuration for this Ditto instance
private final DittoConfigConnect connect;
// The persistence directory used by Ditto to persist data
private final String persistenceDirectory;
// Configuration for experimental features
private final DittoConfigExperimental experimental;
}
public class DittoConfig
{
/// <summary>
/// The unique identifier for the Ditto database (previously "App ID")
/// </summary>
public string DatabaseId { get; set; }
/// <summary>
/// The connectivity configuration for this Ditto instance
/// </summary>
public DittoConfigConnect Connect { get; set; }
/// <summary>
/// The persistence directory used by Ditto to persist data
/// </summary>
public string PersistenceDirectory { get; set; }
/// <summary>
/// Configuration for experimental features
/// </summary>
public DittoConfigExperimental Experimental { get; set; }
}
class DittoConfig {
public:
// The unique identifier for the Ditto database (previously "App ID")
std::string database_id;
// The connectivity configuration for this Ditto instance
Connect connect;
// The persistence directory used by Ditto to persist data
std::string persistence_directory;
// Configuration for experimental features
Experimental experimental;
// Builder methods
DittoConfig& set_database_id(const std::string& id);
DittoConfig& set_connect(const Connect& conn);
DittoConfig& set_persistence_directory(const std::string& dir);
DittoConfig& set_experimental(const Experimental& exp);
};
pub struct DittoConfig {
// The unique identifier for the Ditto database (previously "App ID")
pub database_id: String,
// The connectivity configuration for this Ditto instance
pub connect: DittoConfigConnect,
// The persistence directory used by Ditto to persist data
pub persistence_directory: Option<PathBuf>,
// Configuration for experimental features
pub experimental: DittoConfigExperimental,
}
impl DittoConfig {
// Constructor
pub fn new(database_id: impl Into<String>, connect: DittoConfigConnect) -> Self;
// Builder method for persistence directory
pub fn with_persistence_directory(self, path: impl Into<PathBuf>) -> Self;
}
// ⚠️ Flutter SDK does not yet support the new DittoConfig-based initialization.
// Continue using the legacy DittoIdentity-based API for Flutter applications.
type DittoConfig struct {
// DatabaseID is the unique identifier for the Ditto database.
DatabaseID string `cbor:"database_id" json:"databaseId"`
// Connect specifies how this instance discovers and connects to peers.
Connect DittoConfigConnectUnion `cbor:"connect" json:"connect"`
// PersistenceDirectory specifies where Ditto should persist data.
PersistenceDirectory string `cbor:"persistence_directory,omitempty" json:"persistenceDirectory,omitempty"`
// Experimental allows setting configuration for experimental features.
Experimental map[string]any `cbor:"experimental" json:"experimental"`
}
Connect
TheDittoConfigConnect enum specifies how your Ditto instance discovers and
connects to peers. There are two connection modes:
- Server: Formerly known as OnlinePlayground or OnlineWithAuthentication
- SmallPeersOnly: Formerly known as SharedKey or OfflinePlayground Authentication
Server Connection
Server Connection enables you to sync with all peers, both servers and other SDKs, using your HTTP Cloud URL Endpoint. This connection mode was formerly known as OnlinePlayground or OnlineWithAuthentication. These two authentication types have now been unified under one Server Connection, where you supply a token and a provider. Your database can be in development or authenticated mode. When in development mode, use thedevelopment provider. Otherwise, you can use your own customer
authentication webhook provider.
// Your HTTP Cloud URL Endpoint
let config = DittoConfig(
databaseID: "REPLACE_ME_WITH_YOUR_DATABASE_ID", // This was "appID" in v4
connect: .server(url: URL(string: "REPLACE_ME_WITH_YOUR_URL")), // This was "Custom Auth URL" in v4
)
let ditto = try await Ditto.open(config: config)
// Set up authentication expiration handler (required for server connections)
ditto.auth?.expirationHandler = { [weak self] ditto, secondsRemaining in
// Authenticate when token is expiring
ditto.auth?.login(
// Your development token, replace with your actual token
token: "REPLACE_ME_WITH_YOUR_DEVELOPMENT_TOKEN",
// Use .development or your actual provider name
provider: .development
) { clientInfo, error in
if let error = error {
print("Authentication failed: \(error)")
} else {
print("Authentication successful")
}
}
}
try ditto.sync.start()
// Your HTTP Cloud URL Endpoint
const authURL = "REPLACE_ME_WITH_YOUR_URL";
const databaseId = "REPLACE_ME_WITH_YOUR_DATABASE_ID";
const config = new DittoConfig(
databaseId, // This was "appID" in v4
{
mode: 'server',
url: authURL
}
);
const ditto = await Ditto.open(config);
// Set up authentication expiration handler (required for server connections)
await ditto.auth.setExpirationHandler(async (ditto, secondsRemaining) => {
// Authenticate when token is expiring. Any errors will be logged in the Ditto logger.
await ditto.auth.login(
// Your development token, replace with your actual token
"REPLACE_ME_WITH_YOUR_DEVELOPMENT_TOKEN",
// Use Authenticator.DEVELOPMENT_PROVIDER for playground, or your actual provider name
Authenticator.DEVELOPMENT_PROVIDER
);
console.log("Authentication successful");
});
ditto.sync.start();
// Your HTTP Cloud URL Endpoint
val endpoint = "REPLACE_ME_WITH_YOUR_URL"
val id = "REPLACE_ME_WITH_YOUR_DATABASE_ID"
val config = DittoAndroidConfig(
context,
databaseId = id, // This was "appID" in v4
connect = DittoConfigConnect.Server(url = URL(endpoint))
)
val ditto = Ditto.open(config)
// Set up authentication expiration handler (required for server connections)
ditto.auth.expirationHandler = { ditto, secondsRemaining ->
// Authenticate when token is expiring
ditto.auth.login(
// Your development token, replace with your actual token
token = "REPLACE_ME_WITH_YOUR_DEVELOPMENT_TOKEN",
// Use DittoAuthenticationProvider.development() for playground
provider = DittoAuthenticationProvider.development()
) { clientInfo, error ->
if (error != null) {
println("Authentication failed: $error")
} else {
println("Authentication successful")
}
}
}
ditto.startSync()
import com.ditto.java.Ditto;
import com.ditto.java.DittoFactory;
import com.ditto.java.DittoConfig;
import com.ditto.java.DittoAuthenticationProvider;
// Your HTTP Cloud URL Endpoint
String endpoint = "REPLACE_ME_WITH_YOUR_URL";
String databaseId = "REPLACE_ME_WITH_YOUR_DATABASE_ID";
DittoConfig config = new DittoConfig.Builder(databaseId)
.connect(new DittoConfig.Connect.Server(endpoint))
.build();
Ditto ditto = DittoFactory.create(config);
// Set up authentication expiration handler (required for server connections)
ditto.getAuth().setExpirationHandler((expiringDitto, timeUntilExpiration) -> {
// Authenticate when token is expiring
expiringDitto.getAuth().login(
"REPLACE_ME_WITH_YOUR_DEVELOPMENT_TOKEN",
DittoAuthenticationProvider.development()
).thenAccept(clientInfo -> {
System.out.println("Authentication successful");
}).exceptionally(error -> {
System.out.println("Authentication failed: " + error);
return null;
});
});
ditto.startSync();
// Your HTTP Cloud URL Endpoint
var endpoint = "REPLACE_ME_WITH_YOUR_URL";
var id = "REPLACE_ME_WITH_YOUR_DATABASE_ID";
var config = new DittoConfig(
databaseId: id, // This was "appID" in v4
connect: new DittoConfigConnect.Server(new Uri(endpoint))
);
var ditto = await Ditto.OpenAsync(config);
// Set up authentication expiration handler (required for server connections)
ditto.Auth.ExpirationHandler = async (ditto, secondsRemaining) =>
{
// Authenticate when token is expiring
try
{
await ditto.Auth.LoginAsync(
// Your development token, replace with your actual token
"REPLACE_ME_WITH_YOUR_DEVELOPMENT_TOKEN",
// Use DittoAuthenticationProvider.Development for playground, or your actual provider
DittoAuthenticationProvider.Development
);
Console.WriteLine("Authentication successful");
}
catch (Exception error)
{
Console.WriteLine($"Authentication failed: {error}");
}
};
ditto.StartSync();
// Your HTTP Cloud URL Endpoint
std::string endpoint = "REPLACE_ME_WITH_YOUR_URL";
std::string id = "REPLACE_ME_WITH_YOUR_DATABASE_ID";
auto config = DittoConfig::default_config()
.set_database_id(id) // This was "appID" in v4
.set_connect(DittoConfig::Connect::server(endpoint));
auto ditto = Ditto::open(config);
// Set up authentication expiration handler (required for server connections)
ditto->auth().set_expiration_handler([](auto& ditto, int64_t seconds_remaining) {
// Authenticate when token is expiring
ditto.auth().login(
// Your development token, replace with your actual token
"REPLACE_ME_WITH_YOUR_DEVELOPMENT_TOKEN",
// Use Authenticator::get_development_provider() for playground
Authenticator::get_development_provider(),
[](auto client_info, auto error) {
if (error) {
std::cout << "Authentication failed: " << error->message() << std::endl;
} else {
std::cout << "Authentication successful" << std::endl;
}
}
);
});
ditto->start_sync();
use dittolive_ditto::prelude::*;
use dittolive_ditto::identity::get_development_provider;
use std::time::Duration;
// Your HTTP Cloud URL Endpoint and Database ID
let config = DittoConfig::new(
"REPLACE_ME_WITH_YOUR_DATABASE_ID", // This was "app_id" in v4
DittoConfigConnect::Server {
url: "REPLACE_ME_WITH_YOUR_URL".parse().unwrap(),
},
);
// Initialize Ditto
let ditto = Ditto::open_sync(config)?;
// Set up authentication expiration handler (required for Server connections)
let auth = ditto.auth().expect("Auth is available in Server mode");
auth.set_expiration_handler(async |ditto: &Ditto, _duration: Duration| {
if let Some(auth) = ditto.auth() {
// Fetch your token from your auth server
let token = "REPLACE_ME_WITH_YOUR_DEVELOPMENT_TOKEN";
let provider = get_development_provider();
match auth.login(token, &provider) {
Ok(_) => println!("Authentication successful"),
Err(e) => println!("Authentication failed: {}", e),
}
} else {
eprintln!("Ditto authentication not configured");
}
});
ditto.sync().start()?;
// ⚠️ Flutter SDK does not yet support the new DittoConfig-based initialization.
// Continue using the legacy `DittoIdentity`-based API for Flutter applications.
const endpoint = "REPLACE_ME_WITH_YOUR_URL"
const id = "REPLACE_ME_WITH_YOUR_DATABASE_ID"
const token = "REPLACE_ME_WITH_YOUR_DEVELOPMENT_TOKEN"
config := ditto.DefaultDittoConfig().
WithDatabaseID(id).
WithConnect(&ditto.DittoConfigConnectServer{URL: endpoint})
dit, err := ditto.Open(config)
if err != nil {
log.Fatalf("error: failed to open Ditto: %v", err)
}
defer dit.Close()
// Set up authentication expiration handler (required for server connections)
dit.Auth().SetExpirationHandler(
func(d *ditto.Ditto, timeUntilExpiration time.Duration) {
_, err := d.Auth().Login(token, ditto.DevelopmentAuthenticationProvider())
if err != nil {
log.Errorf("Authentication failed: %v", err)
} else {
log.Printf("Authentication succeeded")
}
})
if err := dit.Sync().Start(); err != nil {
log.Errorf("error: failed to start sync: %v", err)
}
Small Peers Only
Restrict connectivity to small peers only. Formerly known as SharedKey Authentication. The mechanism is no different, but the terminology has changed to better reflect its purpose.// Without encryption
let config = DittoConfig(
databaseID: "38d4b612-e6ea-42f2-ae3e-f4cba92c918",
connect: .smallPeersOnly(privateKey: nil) // add your OpenSSL key in production
)
// Without encryption
const config = new DittoConfig(
"38d4b612-e6ea-42f2-ae3e-f4cba92c918",
{
type: 'smallPeersOnly',
privateKey: undefined // add your OpenSSL key in production
}
);
// Without encryption
val config = DittoAndroidConfig(
context,
databaseId = "38d4b612-e6ea-42f2-ae3e-f4cba92c918",
connect = DittoConfigConnect.SmallPeersOnly(
privateKey = null // add your OpenSSL key for e2e encryption
)
)
// Without encryption
DittoAndroidConfig config = new DittoAndroidConfig(
context,
"38d4b612-e6ea-42f2-ae3e-f4cba92c918",
new DittoConfigConnect.SmallPeersOnly(
null // add your OpenSSL key in production
)
);
// Without encryption
var config = new DittoConfig(
databaseId: "38d4b612-e6ea-42f2-ae3e-f4cba92c918",
connect: new DittoConfigConnect.SmallPeersOnly(
privateKey: null // add your OpenSSL key in production
)
);
// Without encryption
auto config = DittoConfig::default_config()
.set_database_id("38d4b612-e6ea-42f2-ae3e-f4cba92c918")
.set_connect(DittoConfig::Connect::small_peers_only(
std::nullopt // add your OpenSSL key in production
));
// Without encryption
let config = DittoConfig::new(
"38d4b612-e6ea-42f2-ae3e-f4cba92c918",
DittoConfigConnect::SmallPeersOnly {
private_key: None, // add your OpenSSL key in production
},
);
// ⚠️ Flutter SDK does not yet support the new DittoConfig-based initialization.
// Continue using the legacy DittoIdentity-based API for Flutter applications.
Persistence Directory
ThepersistenceDirectory property accepts three types of values:
- Absolute path: A URL with an absolute file path
- Relative path: A URL with a relative file path, resolved relative to
Ditto.defaultRootDirectory - nil: Uses a default directory named
ditto-{database-id}, placed inDitto.defaultRootDirectory
Breaking Change: In v5, the default persistence directory name includes the
database ID. This is different from v4, which used a generic
ditto directory
for all apps.Ditto initialization Behavior
// All apps share the same default directory name
let ditto = Ditto(identity: .onlinePlayground(
appID: "f232511c-256b-4b69-8d28-90283bf715d2",
token: "your-token"
))
// Creates directory: /app/sandbox/ditto/
Ditto.open() Behavior
// Directory name includes the database ID
let config = DittoConfig(
databaseID: "f232511c-256b-4b69-8d28-90283bf715d2",
connect: .server(url: URL(string: "REPLACE_ME_WITH_YOUR_URL")!)
)
// Creates directory: /app/sandbox/ditto-f232511c-256b-4b69-8d28-90283bf715d2/
// For compatibility between the two methods, specify the old directory path
let config = DittoConfig(
databaseID: "f232511c-256b-4b69-8d28-90283bf715d2",
connect: .server(url: URL(string: "REPLACE_ME_WITH_YOUR_URL")!),
persistenceDirectory: URL(string: "/app/sandbox/ditto")! // old path
)
// Absolute path
let config1 = DittoConfig(
databaseID: "f232511c-256b-4b69-8d28-90283bf715d2",
connect: .smallPeersOnly(),
persistenceDirectory: URL(string: "/Users/me/DittoData")!
)
// Relative path
let config2 = DittoConfig(
databaseID: "f232511c-256b-4b69-8d28-90283bf715d2",
connect: .smallPeersOnly(),
persistenceDirectory: URL(string: "MyApp/Data")!
)
// Default (nil) - NEW: includes database ID
let config3 = DittoConfig(
databaseID: "f232511c-256b-4b69-8d28-90283bf715d2",
connect: .smallPeersOnly(),
persistenceDirectory: nil // Will use "ditto-f232511c-256b-4b69-8d28-90283bf715d2" as default directory
)
// Absolute path
const config1 = new DittoConfig(
"f232511c-256b-4b69-8d28-90283bf715d2",
{ type: 'smallPeersOnly' },
"/Users/me/DittoData"
);
// Relative path
const config2 = new DittoConfig(
"f232511c-256b-4b69-8d28-90283bf715d2",
{ type: 'smallPeersOnly' },
"MyApp/Data"
);
// Default (undefined) - NEW: includes database ID
const config3 = new DittoConfig(
"f232511c-256b-4b69-8d28-90283bf715d2",
{ type: 'smallPeersOnly' }
// Will use "ditto-f232511c-256b-4b69-8d28-90283bf715d2" as default directory
);
// Absolute path
val config1 = DittoAndroidConfig(
context,
databaseId = "f232511c-256b-4b69-8d28-90283bf715d2",
connect = DittoConfigConnect.SmallPeersOnly(),
persistenceDirectory = "/data/data/com.myapp/DittoData"
)
// Relative path
val config2 = DittoAndroidConfig(
context,
databaseId = "f232511c-256b-4b69-8d28-90283bf715d2",
connect = DittoConfigConnect.SmallPeersOnly(),
persistenceDirectory = "MyApp/Data"
)
// Default (null) - NEW: includes database ID
val config3 = DittoAndroidConfig(
context,
databaseId = "f232511c-256b-4b69-8d28-90283bf715d2",
connect = DittoConfigConnect.SmallPeersOnly()
// Will use "ditto-f232511c-256b-4b69-8d28-90283bf715d2" as default directory
)
// Absolute path
DittoAndroidConfig config1 = new DittoAndroidConfig(
context,
"f232511c-256b-4b69-8d28-90283bf715d2",
new DittoConfigConnect.SmallPeersOnly(),
"/data/data/com.myapp/DittoData"
);
// Relative path
DittoAndroidConfig config2 = new DittoAndroidConfig(
context,
"f232511c-256b-4b69-8d28-90283bf715d2",
new DittoConfigConnect.SmallPeersOnly(),
"MyApp/Data"
);
// Default (null) - NEW: includes database ID
DittoAndroidConfig config3 = new DittoAndroidConfig(
context,
"f232511c-256b-4b69-8d28-90283bf715d2",
new DittoConfigConnect.SmallPeersOnly()
// Will use "ditto-f232511c-256b-4b69-8d28-90283bf715d2" as default directory
);
// Absolute path
var config1 = new DittoConfig(
databaseId: "f232511c-256b-4b69-8d28-90283bf715d2",
connect: new DittoConfigConnect.SmallPeersOnly(),
persistenceDirectory: "/Users/me/DittoData"
);
// Relative path
var config2 = new DittoConfig(
databaseId: "f232511c-256b-4b69-8d28-90283bf715d2",
connect: new DittoConfigConnect.SmallPeersOnly(),
persistenceDirectory: "MyApp/Data"
);
// Default (null) - NEW: includes database ID
var config3 = new DittoConfig(
databaseId: "f232511c-256b-4b69-8d28-90283bf715d2",
connect: new DittoConfigConnect.SmallPeersOnly()
// Will use "ditto-f232511c-256b-4b69-8d28-90283bf715d2" as default directory
);
// Absolute path
auto config1 = DittoConfig::default_config()
.set_database_id("f232511c-256b-4b69-8d28-90283bf715d2")
.set_connect(DittoConfig::Connect::small_peers_only())
.set_persistence_directory("/Users/me/DittoData");
// Relative path
auto config2 = DittoConfig::default_config()
.set_database_id("f232511c-256b-4b69-8d28-90283bf715d2")
.set_connect(DittoConfig::Connect::small_peers_only())
.set_persistence_directory("MyApp/Data");
// Default (empty string) - NEW: includes database ID
auto config3 = DittoConfig::default_config()
.set_database_id("f232511c-256b-4b69-8d28-90283bf715d2")
.set_connect(DittoConfig::Connect::small_peers_only());
// Will use "ditto-f232511c-256b-4b69-8d28-90283bf715d2" as default directory
// Absolute path
let config1 = DittoConfig::new(
"f232511c-256b-4b69-8d28-90283bf715d2",
DittoConfigConnect::SmallPeersOnly { private_key: None },
)
.with_persistence_directory("/Users/me/DittoData");
// Relative path
let config2 = DittoConfig::new(
"f232511c-256b-4b69-8d28-90283bf715d2",
DittoConfigConnect::SmallPeersOnly { private_key: None },
)
.with_persistence_directory("MyApp/Data");
// Default (None) - NEW: includes database ID
let config3 = DittoConfig::new(
"f232511c-256b-4b69-8d28-90283bf715d2",
DittoConfigConnect::SmallPeersOnly { private_key: None },
);
// Will use "ditto-f232511c-256b-4b69-8d28-90283bf715d2" as default directory
// ⚠️ Flutter SDK does not yet support the new DittoConfig-based initialization.
// Continue using the legacy DittoIdentity-based API for Flutter applications.
The new directory naming scheme allows multiple Ditto instances with different
database IDs to coexist without conflicts, which wasn’t possible with the previous
shared
ditto directory approach.API Usage
Async
Use the async initialization methods for non-blocking operations:do {
let ditto = try await Ditto.open(config: config)
// Ditto instance is ready to use
} catch {
print("Failed to initialize Ditto: \(error)")
}
try {
const ditto = await Ditto.open(config);
// Ditto instance is ready to use
} catch (error) {
console.error("Failed to initialize Ditto:", error);
}
try {
val ditto = Ditto.open(config)
// Ditto instance is ready to use
} catch (error: DittoError) {
println("Failed to initialize Ditto: $error")
}
try {
Ditto ditto = Ditto.openSync(config);
// Ditto instance is ready to use
} catch (DittoError error) {
System.err.println("Failed to initialize Ditto: " + error);
}
try
{
// Note: In .NET, OpenAsync() is the async variant, Open() is synchronous
var ditto = await Ditto.OpenAsync(config);
// Ditto instance is ready to use
}
catch (Exception error)
{
Console.WriteLine($"Failed to initialize Ditto: {error}");
}
auto future = Ditto::open_async(config);
future.then([](auto ditto) {
// Ditto instance is ready to use
}).on_error([](auto error) {
std::cerr << "Failed to initialize Ditto: " << error.message() << std::endl;
});
match Ditto::open(config).await {
Ok(ditto) => {
// Ditto instance is ready to use
}
Err(error) => {
eprintln!("Failed to initialize Ditto: {}", error);
}
}
// ⚠️ Flutter SDK does not yet support the new DittoConfig-based initialization.
// Continue using the legacy DittoIdentity-based API for Flutter applications.
// The Go SDK does not provide a non-blocking Open(). For asynchronous
// initialization, run the blocking Open() in a goroutine and get the
// result via a channel or other mechanism.
type openResult struct {
ditto *ditto.Ditto
err error
}
resultCh := make(chan, openResult, 1)
go func() {
dit, err := ditto.Open(config)
resultCh <- openResult{ditto: dit, err: err}
}()
result := <-resultCh
if result.err != nil {
log.Errorf("error: Failed to initialize Ditto: %v", result.err)
return result.err
}
dit := result.ditto
Sync
Use the synchronous variant when async is not available or practical:The synchronous variant blocks for a potentially long time during initialization and should be avoided when possible. Only use it in cases where longer blocking times are tolerable, such as command-line tools or initialization routines where blocking is acceptable.
do {
let ditto = try Ditto.openSync(config: config)
// Ditto instance is ready to use
} catch {
print("Failed to initialize Ditto: \(error)")
}
// JavaScript doesn't have a sync version - use await in an async function
try {
val ditto = Ditto.openSync(config)
// Ditto instance is ready to use
} catch (error: DittoError) {
println("Failed to initialize Ditto: $e")
}
try {
Ditto ditto = Ditto.openSync(config);
// Ditto instance is ready to use
} catch (DittoError error) {
System.err.println("Failed to initialize Ditto: " + error);
}
try
{
// Note: In .NET, Open() is the synchronous variant, OpenAsync() is async
var ditto = Ditto.Open(config);
// Ditto instance is ready to use
}
catch (Exception error)
{
Console.WriteLine($"Failed to initialize Ditto: {error}");
}
try {
auto ditto = Ditto::open(config);
// Ditto instance is ready to use
} catch (const std::exception& error) {
std::cerr << "Failed to initialize Ditto: " << error.what() << std::endl;
}
match Ditto::open_sync(config) {
Ok(ditto) => {
// Ditto instance is ready to use
}
Err(error) => {
eprintln!("Failed to initialize Ditto: {}", error);
}
}
// ⚠️ Flutter SDK does not yet support the new DittoConfig-based initialization.
// Continue using the legacy DittoIdentity-based API for Flutter applications.
dit, err := ditto.Open(config)
if err != nil {
log.Errorf("error: failed to open Ditto: %v", err)
}
Default Configuration
The default config by itself will not be able to synchronize data with other peers, but it is useful for testing. You can always use it as a starting place as a base configuration and modify it based on incoming parameters. The default configuration is the same as callingDitto() in v4, but with the new DittoConfig structure.
// Use the default configuration as a starting point
let defaultConfig = DittoConfig.default
// Modify as needed
defaultConfig.databaseID = "38d4b612-e6ea-42f2-ae3e-f4cba92c918d", // This was "appID" in v4
defaultConfig.connect = .server(url: URL(string: "REPLACE_ME_WITH_YOUR_URL")!)
let ditto = try await Ditto.open(config: defaultConfig)
// Use the default configuration as a starting point
const defaultConfig = DittoConfig.default;
// Create a new config with modifications
const customConfig = new DittoConfig(
"38d4b612-e6ea-42f2-ae3e-f4cba92c918d", // This was "appID" in v4
{
type: 'server',
url: 'REPLACE_ME_WITH_YOUR_URL'
},
defaultConfig.persistenceDirectory
);
const ditto = await Ditto.open(customConfig);
// Android doesn't have a default config - must provide context
val config = DittoAndroidConfig(
context,
databaseId = "38d4b612-e6ea-42f2-ae3e-f4cba92c918d", // This was "appID" in v4
connect = DittoConfigConnect.Server(
url = URI.create("REPLACE_ME_WITH_YOUR_URL")
)
)
val ditto = Ditto.open(config)
// Android doesn't have a default config - must provide context
DittoAndroidConfig config = new DittoAndroidConfig(
context,
"38d4b612-e6ea-42f2-ae3e-f4cba92c918d", // This was "appID" in v4
new DittoConfigConnect.Server(
URI.create("REPLACE_ME_WITH_YOUR_URL")
)
);
Ditto ditto = Ditto.open(config);
// Use the default configuration as a starting point
var defaultConfig = DittoConfig.Default;
// Create a new config with modifications
var customConfig = new DittoConfig(
databaseId: "38d4b612-e6ea-42f2-ae3e-f4cba92c918d", // This was "appID" in v4
connect: new DittoConfigConnect.Server(
new Uri("REPLACE_ME_WITH_YOUR_URL")
),
persistenceDirectory: defaultConfig.PersistenceDirectory
);
var ditto = await Ditto.OpenAsync(customConfig);
// Use the default configuration as a starting point
auto customConfig = DittoConfig::default_config()
.set_database_id("38d4b612-e6ea-42f2-ae3e-f4cba92c918d") // This was "appID" in v4
.set_connect(DittoConfig::Connect::server(
"REPLACE_ME_WITH_YOUR_URL"
));
auto ditto = Ditto::open(customConfig);
// Create a new configuration
let custom_config = DittoConfig::new(
"38d4b612-e6ea-42f2-ae3e-f4cba92c918d", // This was "appID" in v4
DittoConfigConnect::Server {
url: "REPLACE_ME_WITH_YOUR_URL".parse().unwrap(),
},
);
let ditto = Ditto::open_sync(custom_config)?;
// ⚠️ Flutter SDK does not yet support the new DittoConfig-based initialization.
// Continue using the legacy DittoIdentity-based API for Flutter applications.
config := ditto.DefaultDittoConfig().
WithDatabaseID("38d4b612-e6ea-42f2-ae3e-f4cba92c918d").
WithConnect(&ditto.DittoConfigConnectServer{URL: "REPLACE_ME_WITH_YOUR_URL"})
Error Handling
Common initialization failures include:- Persistence directory locked: Another Ditto instance is already using the specified directory
- Invalid configuration: The provided
DittoConfigcontains invalid values - Missing required fields: Required configuration properties are not provided
do {
let ditto = try await Ditto.open(config: config)
} catch DittoSwiftError.storeError(.persistenceDirectoryLocked) {
// Another Ditto instance is using this directory
} catch DittoSwiftError.validationError(.invalidDittoConfig) {
// Configuration validation failed
} catch {
// Other initialization failures
}
try {
const ditto = await Ditto.open(config);
} catch (error) {
if (error.code === 'store/persistence-directory-locked') {
// Another Ditto instance is using this directory
} else if (error.code === 'validation/invalid-ditto-config') {
// Configuration validation failed
} else {
// Other initialization failures
}
}
try {
val ditto = Ditto.open(config)
} catch (e: DittoError.StoreError) {
when (e.reason) {
is DittoError.StoreErrorReason.PersistenceDirectoryLocked -> {
// Another Ditto instance is using this directory
}
else -> {
// Other store errors
}
}
} catch (e: DittoError.ValidationError) {
when (e.reason) {
is DittoError.ValidationErrorReason.InvalidDittoConfig -> {
// Configuration validation failed
}
else -> {
// Other validation errors
}
}
} catch (e: Exception) {
// Other initialization failures
}
try (Ditto ditto = Ditto.openSync(config)) {
// Ditto is auto-closed by try-with-resources block
} catch (DittoError.StoreError storeError) {
if (storeError.getReason() instanceof DittoError.StoreErrorReason.PersistenceDirectoryLocked) {
// Another Ditto instance is using this directory
}
} catch (DittoError.ValidationError validationError) {
if (validationError.getReason() instanceof DittoError.ValidationErrorReason.InvalidDittoConfig) {
// Configuration validation failed
}
} catch (DittoError error) {
// Other initialization failures
}
try
{
var ditto = await Ditto.OpenAsync(config);
}
catch (DittoStoreException ex) when (ex.Reason is DittoStoreException.PersistenceDirectoryLocked)
{
// Another Ditto instance is using this directory
}
catch (DittoValidationException ex) when (ex.Reason is DittoValidationException.InvalidDittoConfig)
{
// Configuration validation failed
}
catch (Exception ex)
{
// Other initialization failures
}
try {
auto ditto = Ditto::open(config);
} catch (const StoreError& e) {
if (e.reason() == StoreError::Reason::persistence_directory_locked) {
// Another Ditto instance is using this directory
}
} catch (const ValidationError& e) {
if (e.reason() == ValidationError::Reason::invalid_ditto_config) {
// Configuration validation failed
}
} catch (const std::exception& e) {
// Other initialization failures
}
match Ditto::open_sync(config) {
Ok(ditto) => {
// Success - Ditto instance is ready to use
}
Err(error) => {
// Handle error based on error message or type
eprintln!("Failed to initialize Ditto: {}", error);
// You can check specific error conditions:
// - Persistence directory locked
// - Invalid configuration
// - Other initialization failures
}
}
// ⚠️ Flutter SDK does not yet support the new DittoConfig-based initialization.
// Continue using the legacy DittoIdentity-based API for Flutter applications.
dit, err := ditto.Open(config)
if err != nil {
log.Errorf("error: failed to open Ditto: %v", err)
} else {
// Success - Ditto instance is ready to use
}
Best Practices
- Use async initialization when possible for better performance
- Create the Ditto instance once and keep it for the app’s lifetime rather than recreating it repeatedly
- Set authentication handlers immediately after initialization for server connections
- Handle errors gracefully and provide user feedback when initialization fails
- Use unique database IDs for different apps to avoid conflicts
Migration from Ditto Identity
When migrating from Ditto Identity to the new Ditto Config API, consider the following changes:- Initialization: Update your initialization code to use the new
DittoConfigclass. - Error Handling: Review and update error handling logic to accommodate new error types and structures.
- Directory Structure: Ensure you set the
persistenceDirectoryto maintain compatibility. - Authentication Method: Uses a closure-based pattern instead of delegate pattern.
Online Playground
Instead of OnlinePlayground, now useDittoConfig in server connection mode.
- Use
DittoConfigwith.server(url:)for server connections - Set the
databaseIDto your app’s unique identifier (UUID) (previously “App ID”) - Create an authentication handler closure to handle authentication events
- Playground token is now called Development token
- Websocket URL is no longer required
- Remove
updateTransportConfigmethod calls
let config = DittoConfig(
databaseID: "REPLACE_ME_WITH_YOUR_DATABASE_ID", // This was "appID" in v4
connect: .server(url: URL(string: "REPLACE_ME_WITH_YOUR_URL")!)
)
let ditto = try await Ditto.open(config: config)
// Set up authentication expiration handler (required for server connections)
ditto.auth?.expirationHandler = { [weak self] ditto, secondsRemaining in
// Authenticate when token is expiring
ditto.auth?.login(
// Your development token, replace with your actual token
token: "REPLACE_ME_WITH_YOUR_PLAYGROUND_TOKEN",
// Use .development or your actual provider name
provider: .development
) { clientInfo, error in
if let error = error {
print("Authentication failed: \(error)")
} else {
print("Authentication successful")
}
}
}
do {
try ditto.sync.start()
} catch {
print(error.localizedDescription)
}
ditto = Ditto(
identity: DittoIdentity.onlinePlayground(
appID: "REPLACE_ME_WITH_YOUR_APP_ID",
token: "REPLACE_ME_WITH_YOUR_PLAYGROUND_TOKEN",
enableDittoCloudSync: false, // This is required to be set to false to use the correct URLs
customAuthURL: URL(string: "REPLACE_ME_WITH_YOUR_AUTH_URL")
)
)
ditto.updateTransportConfig { transportConfig in
// Set the Ditto Websocket URL
transportConfig.connect.webSocketURLs.insert("wss://REPLACE_ME_WITH_YOUR_WEBSOCKET_URL")
}
// Disable DQL strict mode so that collection definitions are not required in DQL queries
try await ditto.store.execute("ALTER SYSTEM SET DQL_STRICT_MODE = false")
do {
try ditto.startSync()
} catch {
print(error.localizedDescription)
}
Authentication Lifecycle
All authentication methods are backwards compatible with the new Ditto Config API. To simplify code, you no longer need to implement an extra authentication object. For authentication lifecycle hooks, you can now collapse authenticationRequired and authenticationExpiringSoon into a single closure.ditto.auth?.expirationHandler = { [weak self] ditto, secondsRemaining in
ditto.auth?.login(token: myJWT, provider: "my-auth-provider") { clientInfo, error in
if let error = error {
print("Authentication failed: \(error)")
} else {
print("Authentication successful")
}
}
}
ditto.auth.setExpirationHandler(async (ditto, secondsRemaining) => {
try {
await ditto.auth.login(myJWT, "my-auth-provider");
console.log("Authentication successful");
} catch (error) {
console.error("Authentication failed:", error);
}
});
ditto.auth?.expirationHandler = { ditto, secondsRemaining ->
ditto.auth?.login(
token = myJWT,
provider = "my-auth-provider"
) { clientInfo, error ->
if (error != null) {
println("Authentication failed: $error")
} else {
println("Authentication successful")
}
}
}
ditto.auth.setExpirationHandler((dit, secondsRemaining) -> {
dit.auth.login(
myJWT,
"my-auth-provider",
new DittoLoginCallback() {
public void callback(String clientInfo, DittoError error) {
if (error != null) {
System.out.println("Authentication failed: " + error);
} else {
System.out.println("Authentication successful");
}
}
}
);
});
ditto.Auth.ExpirationHandler = async (ditto, secondsRemaining) =>
{
try
{
await ditto.Auth.LoginAsync(myJWT, "my-auth-provider");
Console.WriteLine("Authentication successful");
}
catch (Exception error)
{
Console.WriteLine($"Authentication failed: {error}");
}
};
ditto->auth().set_expiration_handler([](auto& ditto, int64_t seconds_remaining) {
ditto.auth().login(
myJWT,
"my-auth-provider",
[](auto client_info, auto error) {
if (error) {
std::cout << "Authentication failed: " << error->message() << std::endl;
} else {
std::cout << "Authentication successful" << std::endl;
}
}
);
});
ditto.auth().set_expiration_handler(|ditto, seconds_remaining| {
ditto.auth().login(
my_jwt,
"my-auth-provider",
|client_info, error| {
match error {
Some(e) => println!("Authentication failed: {}", e),
None => println!("Authentication successful"),
}
},
);
});
// ⚠️ Flutter SDK does not yet support the new DittoConfig-based initialization.
// Continue using the legacy DittoIdentity-based API for Flutter applications.
ditto.auth?.expirationHandler = { [weak self] ditto, secondsRemaining in
if secondsRemaining == 0 {
// Do what you'd do in `authenticationRequired`
} else {
// Do what you'd do in `authenticationExpiringSoon`
}
}
ditto.auth.setExpirationHandler(async (ditto, secondsRemaining) => {
if (secondsRemaining === 0) {
// Do what you'd do in `authenticationRequired`
} else {
// Do what you'd do in `authenticationExpiringSoon`
}
});
ditto.auth?.expirationHandler = { ditto, secondsRemaining ->
if (secondsRemaining <= 0) {
// Do what you'd do in `authenticationRequired`
} else {
// Do what you'd do in `authenticationExpiringSoon`
}
}
ditto.auth.setExpirationHandler((dit, secondsRemaining) -> {
if (secondsRemaining <= 0) {
// Do what you'd do in `authenticationRequired`
} else {
// Do what you'd do in `authenticationExpiringSoon`
}
});
ditto.Auth.ExpirationHandler = async (ditto, secondsRemaining) =>
{
if (secondsRemaining == 0)
{
// Do what you'd do in `authenticationRequired`
}
else
{
// Do what you'd do in `authenticationExpiringSoon`
}
};
ditto->auth().set_expiration_handler([](auto& ditto, int64_t seconds_remaining) {
if (seconds_remaining == 0) {
// Do what you'd do in `authenticationRequired`
} else {
// Do what you'd do in `authenticationExpiringSoon`
}
});
ditto.auth().set_expiration_handler(|ditto, seconds_remaining| {
if seconds_remaining == 0 {
// Do what you'd do in `authenticationRequired`
} else {
// Do what you'd do in `authenticationExpiringSoon`
}
});
// ⚠️ Flutter SDK does not yet support the new DittoConfig-based initialization.
// Continue using the legacy DittoIdentity-based API for Flutter applications.
SharedKey & Offline Playground
The migration is straightforward - these are renamed:- SharedKey becomes
SmallPeersOnly(privateKey: "MY_PRIVATE_KEY") - OfflinePlayground becomes
SmallPeersOnly(privateKey: nil) Ditto()becomesDittoConfig.default