Platform Permissions - In Depth

Ditto attempts to use all the available networking capabilities of your device to locate and sync with other users of your app. This includes standard Wifi, as well as peer-to-peer functionality such as AWDL and Bluetooth Low Energy.

The iOS and Android operating systems restrict access to some of this functionality for the sake of user control and privacy. To unlock the full capabilities of Ditto it is important to configure your app so that it requests all the permissions that it needs.

For example, four conditions must be met for Ditto to use Bluetooth LE on Android:

  • A Bluetooth hardware adapter must be present. This may be absent on some devices or in an Android simulator.
  • The app must declare the correct permissions in its manifest. These are reviewed and approved by the end user at the time they install the app.
  • The app must request the correct runtime permissions. On Android, the location permission is required to scan for Bluetooth LE peripherals, since detecting a certain peripheral might associate the user with a real-world location.
  • The hardware must be turned on. If the user has selected flight mode or disabled Bluetooth in their settings, it will not work, even if all the permissions are granted.

App Developer Responsibilities

Ditto takes care of itself. Once started, it continuously monitors when hardware and permissions are available and activates new modes of communication when it is able to do so. As an app developer you should include the required permissions/capabilities and request the appropriate runtime permissions. This is described below. You can also subscribe to receive realtime updates from Ditto about the health condition of network transports. For example, you could use this to warn the user when they have Bluetooth LE disabled.

iOS Requirements

Since iOS 13 and Xcode 11 an app must ask the user's permission to use Bluetooth. Ditto will activate Bluetooth by default, which means the user will receive a permission prompt automatically. In addition, since iOS 14 an app must ask the user's permission to use the Local Area Network to discover devices.

You must include several keys in the Info.plist file your app

  • Privacy - Local Network Usage Description
  • Privacy - Bluetooth Peripheral Usage Description
  • Privacy - Bluetooth Always Usage Description
  • A Bonjour service _http-alt._tcp.

These can be configured through Xcode's Info project settings.

Alternatively, add the keys directly to Info.plist. Right click on the Info.plist and hover to Open as and then click Source Code

<string>Uses Bluetooth to connect and sync with nearby devices</string>
<string>Uses Bluetooth to connect and sync with nearby devices</string>
<string>Uses WiFi to connect and sync with nearby devices</string>

Android Requirements

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />

At an appropriate place in your app's workflow you should check for and request the Location permission. The following code is from the example ToDo app. For more information about requesting permissions in a user-friendly way refer to Android's documentation: Request App Permissions.

fun checkLocationPermission() {
    // On Android, parts of Bluetooth LE and WiFi Direct require location permission
    // Ditto will operate without it but data sync may be impossible in certain scenarios
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
            != PackageManager.PERMISSION_GRANTED) {
        // For this app we will prompt the user for this permission every time if it is missing
        // We ignore the result - Ditto will automatically notice when the permission is granted
        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 0);

On Android there may be a noticeable delay between when the user grants location access and when Ditto notices the new permission. For this reason it is recommended to call refreshPermissions() whenever a relevant permission might have changed. This will force an immediate check. If a permission has become available your app can begin syncing as quickly as possible.

override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    // Regardless of the outcome, tell Ditto that permissions maybe changed