New configuration-based initialization for Ditto instances
This API is in preview and will become the standard way to initialize Ditto instances in v5, replacing the legacy DittoIdentity-based initialization.
The Initialization Revamp introduces a new, more flexible way to configure and initialize Ditto instances using the DittoConfig structure and the Ditto.open() method.
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.
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
Copy
Ask AI
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}
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 the development provider. Otherwise, you can use your own customer
authentication webhook provider.
Copy
Ask AI
// Your HTTP Cloud URL Endpointlet 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()
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.
Copy
Ask AI
// Without encryptionlet config = DittoConfig( databaseID: "38d4b612-e6ea-42f2-ae3e-f4cba92c918", connect: .smallPeersOnly(privateKey: nil) // add your OpenSSL key in production)
The persistenceDirectory 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 in Ditto.defaultRootDirectory
Important Change in v5: Directory Names Now Include Database ID
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.
Legacy Ditto initialization Behavior
Copy
Ask AI
// All apps share the same default directory namelet ditto = Ditto(identity: .onlinePlayground( appID: "f232511c-256b-4b69-8d28-90283bf715d2", token: "your-token"))// Creates directory: /app/sandbox/ditto/
Newly introduced Ditto.open() Behavior
Copy
Ask AI
// Directory name includes the database IDlet config = DittoConfig( databaseID: "f232511c-256b-4b69-8d28-90283bf715d2", connect: .server(url: URL(string: "https://your-server.ditto.live")!))// Creates directory: /app/sandbox/ditto-f232511c-256b-4b69-8d28-90283bf715d2/
Maintaining CompatibilityTo maintain compatibility with directory structure, explicitly set the persistence directory:
Copy
Ask AI
// For compatibility between the two methods, specify the old directory pathlet config = DittoConfig( databaseID: "f232511c-256b-4b69-8d28-90283bf715d2", connect: .server(url: URL(string: "https://your-server.ditto.live")!), persistenceDirectory: URL(string: "/app/sandbox/ditto")! // old path)
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.
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.
Copy
Ask AI
do { let ditto = try Ditto.openSync(config: config) // Ditto instance is ready to use} catch { print("Failed to initialize Ditto: \(error)")}
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 calling Ditto() in v4, but with the new DittoConfig structure.
Copy
Ask AI
// Use the default configuration as a starting pointlet defaultConfig = DittoConfig.default// Modify as neededdefaultConfig.databaseID = "38d4b612-e6ea-42f2-ae3e-f4cba92c918d", // This was "appID" in v4defaultConfig.connect = .server(url: URL(string: "https://my-server.ditto.live")!)let ditto = try await Ditto.open(config: defaultConfig)
Persistence directory locked: Another Ditto instance is already using the specified directory
Invalid configuration: The provided DittoConfig contains invalid values
Missing required fields: Required configuration properties are not provided
The new initialization can throw several types of errors:
Copy
Ask AI
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}
Instead of OnlinePlayground, now use DittoConfig in server connection mode.
Use DittoConfig with .server(url:) for server connections
Set the databaseID to 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 updateTransportConfig method calls
Copy
Ask AI
let config = DittoConfig( databaseID: "REPLACE_ME_WITH_YOUR_DATABASE_ID", // This was "appID" in v4 connect: .server(url: "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)}
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.
Copy
Ask AI
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") } }}
If you need different behavior based on timing of the request, you can condition on
secondsRemaining == 0 in the closure-based handler. This is optional.
Copy
Ask AI
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` }}