Transport Configuration
There are instances where you may want to configure additional connections configurations for the current Ditto instance. This section will teach you how to configure your Ditto instance to listen for connections on a port explicitly and to connect to remote instances via a host (IP) and port. To do this, before you call startSync()
, construct a DittoTransportConfig
value and set ditto.SetTransportConfig
.
Ditto will automatically attempt to connect to other ditto instances on
the Local Area Network, Bluetooth, and AWDL. However, if you supply a
DittoTransportConfig
, this will not automatically be enabled. You'll need to
remember to enable peer to peer connnections with EnableAllPeerToPeer()
.
Enabling/Disabling Transports
You can enable all peer to peer connections with EnableAllPeerToPeer()
. You can also customize which transports are used by enabling/disabling each transport separately.
// Create a new DittoTransportConfig()var config = DittoTransportConfig()
//Enable all peer to peer transportsconfig.enableAllPeerToPeer()
//Or enable/disable each transport separately//BluetoothLeconfig.peerToPeer.bluetoothLE.isEnabled = true//Local Area Networkconfig.peerToPeer.lan.isEnabled = true//Awdlconfig.peerToPeer.awdl.isEnabled = true
ditto.transportConfig = config
do { try ditto.startSync()} catch (let err) { print(err.localizedDescription)}
Sync to Ditto Cloud
The big peer generates certificates automatically for each small peer. A certificate involves an expiration date and AppID, among other credentials. Read more here about how it works.
In order to sync with other devices, a peer must connect to the Big Peer at least once. To do this, you must use either OnlinePlayground or OnlineWithAuthentication.
We recommend using OnlinePlayground for development and OnlineWithAuthentication for production apps. If you only want to use the Big Peer for authentication without syncing to the cloud, you can use enableDittoCloudSync=false
. enableDittoCloudSync
is set to true by default.
let ditto = Ditto(identity: .onlinePlayground( appID: "REPLACE_ME_WITH_YOUR_APP_ID", token: "REPLACE_ME_WITH_YOUR_PLAYGROUND_TOKEN", // Set to false to disable syncing with the cloud enableDittoCloudSync: true))do { try ditto.startSync()} catch (let err) { print(err.localizedDescription)}
Observing Peers
Ditto always monitors the mesh network and can report the device names of peers it is connected to. You can manually set the device name of those peers and observe those peers in the network.
info
The device name must be set before startSync()
is called.
ditto.deviceName = "Susan B."let observer = ditto.presence.observe { presence in if !presence.remotePeers.isEmpty { // render peers }}
do { try ditto.startSync()} catch (let err) { print(err.localizedDescription)}
If you want to observe changes made by a particular device, include the device name as a field in the document and query for that field in your ditto live query
"status == 'open' && edited_by == 'deviceC'"
Sync Groups
When peer-to-peer transports are enabled, all devices with the same App ID will form an interconnected mesh network. If you have knowledge ahead of time that devices are in distinct groups that should only sync with each other, you can optimize performance by restricting them from syncronizing with each other.
The syncGroup
parameter provides this functionality. A device can only ever be
in one sync group, which by default is group 0. Up to 2^32 distinct group
numbers can be used in an app. Using a sync group is more performant, and is
recommended in any case where this knowledge is known ahead of time.
info
For example, imagine an application that supports two different restaurant
locations: 1234 and 7890. Before calling startSync()
, you can use a different sync group for each location. Only
devices in the same sync group will sync with eachother. This will make queries faster when sent to the same big peer.
struct User { var id: String var restaurantID: UInt32}let authenticatedUser = User(id: "abc123", restaurantID: 323234)
var config = DittoTransportConfig()// 1. Enable All Peer to Peer Connections config.enableAllPeerToPeer()// 2. Set sync group to an integer between 0 and 2^32config.global.syncGroup = authenticatedUser.restaurantIDditto.transportConfig = config
do { try ditto.startSync()} catch (let error) { print(error.localizedDescription)}
info
Note: sync groups are not a security mechanism
Sync groups are an optimization, not a security control. If a connection is created
manually, such as by specifying a connect
transport, then devices from
different sync groups will still sync as normal. If two groups of devices are
intended to have access to different data sets, this must be enforced using
Ditto's permissions system.
Connecting to a Remote Ditto Peer
If you know the host and port of another remote Ditto peer and would like to connect to it, construct a DittoTransportConfig
object and add the host and port to the DittoTransportConfig.Connect.TcpServers
property as a string
. The string format should be host:port
, separated by a colon.
In the example below, we know of two other Ditto peers located on:
- Host IP
135.1.5.5
at port12345
- Host IP
185.1.5.5
at port4567
var config = DittoTransportConfig()// Connect explicitly to a remote devicesconfig.connect.tcpServers.insert("135.1.5.5:12345")config.connect.tcpServers.insert("185.1.5.5:12345")
ditto.transportConfig = config
do { try ditto.startSync()} catch (let err) { print(err.localizedDescription)}
Feel free to add as many known remote host:port
strings.
You can also configure your Ditto instance to connect to a websocket, which is useful to connect to one or more big peers or authentication instances.
Listening for Connections on a Specific Port
You can enable the Ditto instance to listen for incoming connections from other remotes Ditto peers on a specific port. You can think of this as setting up your Ditto as a "server" that can listen to connections from other peers.
In this example, we would like our Ditto instance to listen to incoming connections on port 4000
on 0.0.0.0
.
info
To be safe, please do not use localhost
when setting the IP interface. Use "0.0.0.0"
instead.
var config = DittoTransportConfig()
// Listen for incoming connections on port 4000config.listen.tcp.isEnabled = trueconfig.listen.tcp.interfaceIP = "0.0.0.0"config.listen.tcp.port = 4000
ditto.transportConfig = config
do { try ditto.startSync()} catch (let err) { print(err.localizedDescription)}
Incoming connections from other Ditto peers will be able to connect only if the port is accessible. Depending on your deployment be sure to check that external connections can reach the port that you have specified in your configuration. You may need to set up port forwarding if external ports map differently to your host.
Combining Multiple Transports
You can specify several modes of transport configuration within DittoTransportConfig
. The following snippet shows you a ditto instance that can:
- Connect to local area network devices
- Listen for incoming remote connections
- Connect to remote devices with a known host and port.
var config = DittoTransportConfig()// 1. Enable All Peer to Peer Connectionsconfig.enableAllPeerToPeer()
// 2. Listen for incoming connections on port 4000config.listen.tcp.isEnabled = trueconfig.listen.tcp.interfaceIP = "0.0.0.0"config.listen.tcp.port = 4000
// 3. Connect explicitly to remote devicesconfig.connect.tcpServers.insert("135.1.5.5:12345")config.connect.tcpServers.insert("185.1.5.5:12345")
ditto.transportConfig = config
do { try ditto.startSync()} catch (let err) { print(err.localizedDescription)}
Monitoring Transport Conditions
If syncing over Bluetooth LE is a critical part of your application you may want to warn the user if they are missing the permission or if the hardware is disabled. Ditto will help you by reporting conditions via a delegate or callback object.
First, while configuring Ditto, assign a delegate or a callback to receive notifications.
// Setting up inside a ViewControllerlet ditto = Ditto(identity: DittoIdentity.onlinePlayground(appID: "REPLACE_ME_WITH_YOUR_APP_ID", token: "REPLACE_ME_WITH_YOUR_PLAYGROUND_TOKEN"))ditto.delegate = selftry! ditto.startSync()
// Now you can observe real time changes to the transport conditions:extension ViewController: DittoDelegate { func transportConditionDidChange(transportID: Int64, condition: TransportCondition) { if condition == .BleDisabled { print("BLE disabled") } else if condition == .NoBleCentralPermission { print("Permission missing for BLE") } else if condition == .NoBlePeripheralPermission { print("Permission missing for BLE") } }