How to Enable Consent Management in Mobile SDKs
5 minute read
This guide walks you through enabling consent management in the RudderStack Android (Kotlin) and iOS (Swift) SDKs and updating the user’s choices at runtime.
Configure destination consent settings
Consent management works from the consent settings you configure in the RudderStack dashboard, so set those up first:
- Open the destination you want to gate, and go to its Consent settings section.
- Choose Custom as the provider.
- Enter the consent category IDs that apply to this destination.
- Choose the consent logic —
AND(the user must consent to every listed category) orOR(consenting to at least one is enough).
For the full walkthrough, see Custom Consent Management and the Consent Management Overview.
Consent category IDs are case-sensitive. The IDs you pass to the SDK must match the ones you entered in the dashboard exactly, or the destination will not be gated the way you expect.
Enable consent management
Consent management is disabled by default. Turn it on when you initialize the SDK, and supply the user’s current choices at the same time.
Enabling consent management is a load-time decision. You cannot turn it on later in the session — so if your app can ever need it, enable it at initialization and let the consent values reflect what the user has chosen.
import com.rudderstack.sdk.kotlin.android.*
import com.rudderstack.sdk.kotlin.android.consent.*
val analytics = Analytics(
configuration = Configuration(
application = application,
writeKey = WRITE_KEY,
dataPlaneUrl = DATA_PLANE_URL,
consentManagement = ConsentManagementConfiguration(
enabled = true,
provider = ConsentManagementProvider.CUSTOM,
allowedConsentIds = listOf("marketing", "analytics"),
deniedConsentIds = listOf("advertising"),
),
)
)The corresponding Java snippet is shown below:
import com.rudderstack.sdk.kotlin.android.consent.*;
import com.rudderstack.sdk.kotlin.android.javacompat.*;
import java.util.Arrays;
ConsentManagementConfiguration consentManagement = new ConsentManagementConfigurationBuilder()
.setEnabled(true)
.setProvider(ConsentManagementProvider.CUSTOM)
.setAllowedConsentIds(Arrays.asList("marketing", "analytics"))
.setDeniedConsentIds(Arrays.asList("advertising"))
.build();
JavaAnalytics analytics = new JavaAnalytics(
new ConfigurationBuilder(application, WRITE_KEY, DATA_PLANE_URL)
.setConsentManagement(consentManagement)
.build()
);import RudderStackAnalytics
let configuration = Configuration(
writeKey: WRITE_KEY,
dataPlaneUrl: DATA_PLANE_URL,
consentManagement: ConsentManagementConfiguration(
enabled: true,
provider: .custom,
allowedConsentIds: ["marketing", "analytics"],
deniedConsentIds: ["advertising"]
)
)
let analytics = Analytics(configuration: configuration)The corresponding Objective-C snippet is shown below:
@import RudderStackAnalytics;
RSSConfigurationBuilder *builder = [[RSSConfigurationBuilder alloc]
initWithWriteKey:WRITE_KEY
dataPlaneUrl:DATA_PLANE_URL];
RSSConsentManagementConfigurationBuilder *consentBuilder =
[RSSConsentManagementConfigurationBuilder new];
[consentBuilder setEnabled:YES];
[consentBuilder setProvider:RSSConsentManagementProviderCustom];
[consentBuilder setAllowedConsentIds:@[@"marketing", @"analytics"]];
[consentBuilder setDeniedConsentIds:@[@"advertising"]];
[builder setConsentManagement:[consentBuilder build]];
RSSAnalytics *analytics = [[RSSAnalytics alloc]
initWithConfiguration:[builder build]];Configuration parameters
| Parameter | Type | Description |
|---|---|---|
enabled | Boolean | Turns consent management on for the session. Defaults to false. |
provider | Enum | The consent provider. Only the custom provider is supported — ConsentManagementProvider.CUSTOM (Kotlin) / .custom (Swift). This is the default. |
allowedConsentIds | List of strings | The consent category IDs the user has allowed. Defaults to empty. |
deniedConsentIds | List of strings | The consent category IDs the user has denied. Defaults to empty. |
Supply at least one consent ID. Enabling consent management with both lists empty leaves it inactive for the whole session — see Handle empty consent IDs.
Update consent at runtime
When the user changes their choices, pass the new state to setConsent. The values you supply fully replace the previous state, so always pass the user’s complete choices, not just what changed.
analytics.setConsent(
ConsentManagementOptions(
allowedConsentIds = listOf("marketing"),
deniedConsentIds = listOf("analytics", "advertising"),
)
)The corresponding Java snippet is shown below:
ConsentManagementOptions options = new ConsentManagementOptionsBuilder()
.setAllowedConsentIds(Arrays.asList("marketing"))
.setDeniedConsentIds(Arrays.asList("analytics", "advertising"))
.build();
analytics.setConsent(options);let options = ConsentManagementOptions(
allowedConsentIds: ["marketing"],
deniedConsentIds: ["analytics", "advertising"]
)
analytics.setConsent(options)The corresponding Objective-C snippet is shown below:
RSSConsentManagementOptions *options =
[[RSSConsentManagementOptions alloc]
initWithAllowedConsentIds:@[@"marketing"]
deniedConsentIds:@[@"analytics", @"advertising"]];
[analytics setConsent:options];Calling setConsent takes effect immediately:
- Destinations the user has newly allowed are initialized, without restarting the app.
- Destinations the user has newly denied stop receiving events. The destination’s own SDK is left in place — most third-party SDKs have no reliable teardown — but RudderStack sends it nothing further.
- Events tracked while a destination was denied are not delivered to it when the user allows it later.
Handle empty consent IDs
Enabling consent management requires at least one consent category ID. How the SDK handles an empty pair depends on when it happens:
| When | What happens |
|---|---|
| At initialization, with both lists empty | Accepted, but consent management stays inactive for the session — exactly as if you had left it disabled. Events carry no consent block and no destination is gated. The SDK logs this at info. |
At runtime, setConsent with both lists empty | Ignored, with a warning. Your current consent state is left unchanged. |
To record that the user refused everything, pass those categories in deniedConsentIds — do not call setConsent with two empty lists:
// Correct: the user rejected everything
analytics.setConsent(
ConsentManagementOptions(
allowedConsentIds = emptyList(),
deniedConsentIds = listOf("marketing", "analytics", "advertising"),
)
)// Correct: the user rejected everything
analytics.setConsent(
ConsentManagementOptions(
allowedConsentIds: [],
deniedConsentIds: ["marketing", "analytics", "advertising"]
)
)Only
allowedConsentIdsdecides whether a destination is gated.deniedConsentIdsis stamped onto the event for your records and is never consulted when resolving a destination.To block a destination, ensure its consent category IDs are absent from
allowedConsentIds— adding them todeniedConsentIdsalone does not block it.
An empty call is never how a refusal is expressed — that is why the SDK treats it as a mistake rather than as “deny everything”.