Consent Docs

Record consent in a mobile app

Use the iOS or Android SDK to fetch the form, show your own consent screen, record the answer and gate your app's features on it.

How-to2 min readFor web, backend and mobile developers

The iOS and Android SDKs record consent from native apps as the same evidence the website produces. They do not draw a banner. Your app shows its own consent screen from the form the server sends, and the SDK records the answer and keeps it on the device.

Before you start#

  • A key that lists the app. Under Allowed origins, add app: followed by the bundle id (iOS) or the application id (Android), such as app:in.example.kalpa. A key cannot be edited afterwards.
  • A region rule that sends your users to a published form.
  • The SDK added to your project. See iOS SDK or Android SDK.

A test key answers any app. A live key answers only the apps on its list, so an unlisted app gets 401.

Ask and record#

  1. Create one client with your publishable key and keep it for the life of the app.

    iOSSwift
    import PiedapConsent
    
    let consent = try PiedapConsent(
        apiUrl: "https://console.dpconsent.com/consent",
        publishableKey: "pk_…",
        applicationId: Bundle.main.bundleIdentifier!,
        store: UserDefaultsConsentStore()
    )
    Androidjava
    import com.piedap.consent.PiedapConsent;
    
    PiedapConsent consent = PiedapConsent.builder()
        .apiUrl("https://console.dpconsent.com/consent")
        .publishableKey("pk_…")
        .applicationId(context.getPackageName())
        .store(new PreferencesStore(context))
        .build();
  2. Fetch the form in the user's language.

    iOSSwift
    let locale = Locale.preferredLanguages.first ?? "en"
    let form = try await consent.loadForm(locale: locale)
    Androidjava
    // Network calls block. Run them off the main thread.
    String locale = Locale.getDefault().toLanguageTag();
    ConsentForm form = consent.loadForm(locale, null);
  3. Show your consent screen when shouldAsk is true.

    iOSSwift
    if consent.shouldAsk(form) {
        presentConsentScreen(form)
    }
    Androidjava
    if (consent.shouldAsk(form)) {
      presentConsentScreen(form);
    }
  4. On the screen, show the form's title and body, then every purpose with its name, its description and a switch.

  5. Record the answer with the call that matches the button the user pressed.

    iOSSwift
    try await consent.acceptAll(form)
    try await consent.rejectAll(form)
    try await consent.savePreferences(
        form,
        choices: ["analytics": true, "marketing": false]
    )
    Androidjava
    consent.acceptAll(form);
    consent.rejectAll(form);
    
    Map<String, Boolean> choices = new HashMap<>();
    choices.put("analytics", true);
    choices.put("marketing", false);
    consent.savePreferences(form, choices);
  6. Gate your own SDKs on granted.

    iOSSwift
    if consent.granted("analytics") {
        Analytics.enable()
    }
    Androidjava
    if (consent.granted("analytics")) {
      analytics.enable();
    }

The first answer creates a record, kept under a random id made for this install with the type DEVICE_ID. consent.consentId on iOS, or consent.consentId() on Android, returns its id. With a test key the record shows a Test badge under Records in the console.

Three rules for the screen and the store:

  • Start each switch from alwaysOn || defaultGranted, and disable the switch of an always-on purpose.
  • Offer "accept all" and "reject all" with equal weight, and a way to save a custom choice.
  • Give the client a store that lasts. Without one, the SDK forgets everything when the app exits. iOS has UserDefaultsConsentStore built in. On Android, add the class below.
The Android store class
PreferencesStore.javajava
import android.content.Context;
import android.content.SharedPreferences;
import com.piedap.consent.ConsentStore;

final class PreferencesStore implements ConsentStore {
  private final SharedPreferences preferences;

  PreferencesStore(Context context) {
    preferences = context.getSharedPreferences(
        "piedap_consent", Context.MODE_PRIVATE);
  }

  @Override public String get(String key) {
    return preferences.getString(key, null);
  }

  @Override public void put(String key, String value) {
    preferences.edit().putString(key, value).apply();
  }

  @Override public void remove(String key) {
    preferences.edit().remove(key).apply();
  }
}

Record under a signed-in user#

Your server computes an identity proof and sends it to the app with the session. See Record consent for signed-in visitors. The app passes it on.

iOSSwift
try consent.identify(
    subjectId: session.userId,
    subjectType: .userId,
    proof: session.consentProof
)

consent.clearIdentity() // on sign-out
Androidjava
consent.identify(session.userId, "USER_ID", session.consentProof);

consent.clearIdentity(); // on sign-out

The identity is kept in memory only, so call identify after sign-in on every launch. It applies to records created afterwards.

Limits to plan for#

  • Only a publishable key belongs in an app. Both SDKs refuse a key that starts with sk_ before they make any request.
  • There is no offline queue and no retry. If a save fails, the call throws and the answer is not stored. Keep the choice on screen and let the user try again.
  • The SDKs do not send linked identifiers. Link an email address or phone number from your server with linkIdentifiers.
  • The SDKs answer per purpose only, not partner by partner.

↑↓ to move, Enter to open