# How to Enable Consent Management in Mobile SDKs

<p>This guide walks you through enabling consent management in the RudderStack <a href="https://www.rudderstack.com/docs/sources/event-streams/sdks/kotlin-sdk/" >Android (Kotlin)</a> and <a href="https://www.rudderstack.com/docs/sources/event-streams/sdks/swift-sdk/" >iOS (Swift)</a> SDKs and updating the user&rsquo;s choices at runtime.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="configure-destination-consent-settings">Configure destination consent settings</h2><p>Consent management works from the consent settings you configure in the RudderStack dashboard, so set those up first:</p>
<ol>
<li>Open the destination you want to gate, and go to its <strong>Consent settings</strong> section.</li>
<li>Choose <strong>Custom</strong> as the provider.</li>
<li>Enter the <strong>consent category IDs</strong> that apply to this destination.</li>
<li>Choose the <strong>consent logic</strong> — <code>AND</code> (the user must consent to every listed category) or <code>OR</code> (consenting to at least one is enough).</li>
</ol>
<p>For the full walkthrough, see <a href="https://www.rudderstack.com/docs/data-governance/consent-management/custom-consent-manager/" >Custom Consent Management</a> and the <a href="https://www.rudderstack.com/docs/data-governance/consent-management/overview/" >Consent Management Overview</a>.</p>

<blockquote class="warning">
  <div class="tip-quote">
    
    <div class="tip-text">Consent category IDs are <strong>case-sensitive</strong>. 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.</div>
  </div>
</blockquote>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="enable-consent-management">Enable consent management</h2><p>Consent management is <strong>disabled by default</strong>. Turn it on when you initialize the SDK, and supply the user&rsquo;s current choices at the same time.</p>

<blockquote class="warning">
  <div class="tip-quote">
    
    <div class="tip-text">Enabling consent management is a <strong>load-time decision</strong>. 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.</div>
  </div>
</blockquote>
<div class="rs-tabs">
	<div class="rs-tabs__list">
		<button type="button" class="rs-tabs__tab"
			id="tab-caebdf"
		
		>Android (Kotlin)</button>
		<button type="button" class="rs-tabs__tab"
			id="tab-cadfeb"
		
		>iOS (Swift)</button>
	</div>
	
<div class="rs-tabs__panel" id="panel-caebdf">

<div class="rs-code">
  <div class="rs-code__head">kotlin<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-kotlin" data-lang="kotlin">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(&#34;marketing&#34;, &#34;analytics&#34;),
            deniedConsentIds = listOf(&#34;advertising&#34;),
        ),
    )
)</code></pre></div>
</div>
<p>The corresponding Java snippet is shown below:</p>
<div class="rs-code">
  <div class="rs-code__head">java<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java">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(&#34;marketing&#34;, &#34;analytics&#34;))
        .setDeniedConsentIds(Arrays.asList(&#34;advertising&#34;))
        .build();

JavaAnalytics analytics = new JavaAnalytics(
        new ConfigurationBuilder(application, WRITE_KEY, DATA_PLANE_URL)
                .setConsentManagement(consentManagement)
                .build()
);</code></pre></div>
</div>

</div>

<div class="rs-tabs__panel" id="panel-cadfeb" hidden>

<div class="rs-code">
  <div class="rs-code__head">swift<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-swift" data-lang="swift">import RudderStackAnalytics

let configuration = Configuration(
    writeKey: WRITE_KEY,
    dataPlaneUrl: DATA_PLANE_URL,
    consentManagement: ConsentManagementConfiguration(
        enabled: true,
        provider: .custom,
        allowedConsentIds: [&#34;marketing&#34;, &#34;analytics&#34;],
        deniedConsentIds: [&#34;advertising&#34;]
    )
)

let analytics = Analytics(configuration: configuration)</code></pre></div>
</div>
<p>The corresponding Objective-C snippet is shown below:</p>
<div class="rs-code">
  <div class="rs-code__head">objectivec<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-objectivec" data-lang="objectivec">@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:@[@&#34;marketing&#34;, @&#34;analytics&#34;]];
[consentBuilder setDeniedConsentIds:@[@&#34;advertising&#34;]];

[builder setConsentManagement:[consentBuilder build]];

RSSAnalytics *analytics = [[RSSAnalytics alloc]
    initWithConfiguration:[builder build]];</code></pre></div>
</div>

</div>

</div>

<!-- end-chunk -->
<!-- begin-chunk -->
<h3 id="configuration-parameters">Configuration parameters</h3><table>
<thead>
<tr>
<th>Parameter</th>
<th>Type</th>
<th><div>Description</div></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>enabled</code></td>
<td>Boolean</td>
<td>Turns consent management on for the session. Defaults to <code>false</code>.</td>
</tr>
<tr>
<td><code>provider</code></td>
<td>Enum</td>
<td>The consent provider. Only the custom provider is supported — <code>ConsentManagementProvider.CUSTOM</code> (Kotlin) / <code>.custom</code> (Swift). This is the default.</td>
</tr>
<tr>
<td><code>allowedConsentIds</code></td>
<td>List of strings</td>
<td>The consent category IDs the user has <strong>allowed</strong>. Defaults to empty.</td>
</tr>
<tr>
<td><code>deniedConsentIds</code></td>
<td>List of strings</td>
<td>The consent category IDs the user has <strong>denied</strong>. Defaults to empty.</td>
</tr>
</tbody>
</table>

<html lang="en">
<blockquote class="info">
  <div class="tip-quote">
    
    <div class="tip-text">Supply at least one consent ID. Enabling consent management with both lists empty leaves it <strong>inactive</strong> for the whole session — see <a href="#handle-empty-consent-ids" >Handle empty consent IDs</a>.</div>
  </div>
</blockquote>

</html>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="update-consent-at-runtime">Update consent at runtime</h2><p>When the user changes their choices, pass the new state to <code>setConsent</code>. The values you supply <strong>fully replace</strong> the previous state, so always pass the user&rsquo;s complete choices, not just what changed.</p>
<div class="rs-tabs">
	<div class="rs-tabs__list">
		<button type="button" class="rs-tabs__tab"
			id="tab-ebadcf"
		
		>Android (Kotlin)</button>
		<button type="button" class="rs-tabs__tab"
			id="tab-dfaceb"
		
		>iOS (Swift)</button>
	</div>
	
<div class="rs-tabs__panel" id="panel-ebadcf">

<div class="rs-code">
  <div class="rs-code__head">kotlin<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-kotlin" data-lang="kotlin">analytics.setConsent(
    ConsentManagementOptions(
        allowedConsentIds = listOf(&#34;marketing&#34;),
        deniedConsentIds = listOf(&#34;analytics&#34;, &#34;advertising&#34;),
    )
)</code></pre></div>
</div>
<p>The corresponding Java snippet is shown below:</p>
<div class="rs-code">
  <div class="rs-code__head">java<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java">ConsentManagementOptions options = new ConsentManagementOptionsBuilder()
        .setAllowedConsentIds(Arrays.asList(&#34;marketing&#34;))
        .setDeniedConsentIds(Arrays.asList(&#34;analytics&#34;, &#34;advertising&#34;))
        .build();

analytics.setConsent(options);</code></pre></div>
</div>

</div>

<div class="rs-tabs__panel" id="panel-dfaceb" hidden>

<div class="rs-code">
  <div class="rs-code__head">swift<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-swift" data-lang="swift">let options = ConsentManagementOptions(
    allowedConsentIds: [&#34;marketing&#34;],
    deniedConsentIds: [&#34;analytics&#34;, &#34;advertising&#34;]
)

analytics.setConsent(options)</code></pre></div>
</div>
<p>The corresponding Objective-C snippet is shown below:</p>
<div class="rs-code">
  <div class="rs-code__head">objectivec<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-objectivec" data-lang="objectivec">RSSConsentManagementOptions *options =
    [[RSSConsentManagementOptions alloc]
        initWithAllowedConsentIds:@[@&#34;marketing&#34;]
        deniedConsentIds:@[@&#34;analytics&#34;, @&#34;advertising&#34;]];

[analytics setConsent:options];</code></pre></div>
</div>

</div>

</div>

<p>Calling <code>setConsent</code> takes effect immediately:</p>
<ul>
<li>Destinations the user has <strong>newly allowed</strong> are initialized, without restarting the app.</li>
<li>Destinations the user has <strong>newly denied</strong> stop receiving events. The destination&rsquo;s own SDK is left in place — most third-party SDKs have no reliable teardown — but RudderStack sends it nothing further.</li>
<li>Events tracked while a destination was denied are <strong>not</strong> delivered to it when the user allows it later.</li>
</ul>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="handle-empty-consent-ids">Handle empty consent IDs</h2><p>Enabling consent management requires <strong>at least one</strong> consent category ID. How the SDK handles an empty pair depends on when it happens:</p>
<table>
<thead>
<tr>
<th>When</th>
<th>What happens</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>At initialization</strong>, with both lists empty</td>
<td>Accepted, but consent management stays <strong>inactive</strong> 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 <code>info</code>.</td>
</tr>
<tr>
<td><strong>At runtime</strong>, <code>setConsent</code> with both lists empty</td>
<td><strong>Ignored</strong>, with a warning. Your current consent state is left unchanged.</td>
</tr>
</tbody>
</table>
<p>To record that the user refused everything, pass those categories in <code>deniedConsentIds</code> — do not call <code>setConsent</code> with two empty lists:</p>
<div class="rs-tabs">
	<div class="rs-tabs__list">
		<button type="button" class="rs-tabs__tab"
			id="tab-dcafeb"
		
		>Android (Kotlin)</button>
		<button type="button" class="rs-tabs__tab"
			id="tab-efadcb"
		
		>iOS (Swift)</button>
	</div>
	
<div class="rs-tabs__panel" id="panel-dcafeb">

<div class="rs-code">
  <div class="rs-code__head">kotlin<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-kotlin" data-lang="kotlin">// Correct: the user rejected everything
analytics.setConsent(
    ConsentManagementOptions(
        allowedConsentIds = emptyList(),
        deniedConsentIds = listOf(&#34;marketing&#34;, &#34;analytics&#34;, &#34;advertising&#34;),
    )
)</code></pre></div>
</div>

</div>

<div class="rs-tabs__panel" id="panel-efadcb" hidden>

<div class="rs-code">
  <div class="rs-code__head">swift<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-swift" data-lang="swift">// Correct: the user rejected everything
analytics.setConsent(
    ConsentManagementOptions(
        allowedConsentIds: [],
        deniedConsentIds: [&#34;marketing&#34;, &#34;analytics&#34;, &#34;advertising&#34;]
    )
)</code></pre></div>
</div>

</div>

</div>

<blockquote class="warning">
  <div class="tip-quote">
    
    <div class="tip-text"><p>Only <code>allowedConsentIds</code> decides whether a destination is gated. <code>deniedConsentIds</code> is stamped onto the event for your records and is never consulted when resolving a destination.</p>
<p>To block a destination, ensure its consent category IDs are absent from <code>allowedConsentIds</code> — adding them to <code>deniedConsentIds</code> alone does not block it.</p>
</div>
  </div>
</blockquote>
<p>An empty call is never how a refusal is expressed — that is why the SDK treats it as a mistake rather than as &ldquo;deny everything&rdquo;.</p>
<!-- end-chunk -->
<!-- begin-chunk -->
<h2 id="see-more">See more</h2><ul>
<li><a href="https://www.rudderstack.com/docs/sources/event-streams/sdks/client-side-features/consent-management/" >Consent Management in Mobile SDKs</a></li>
<li><a href="https://www.rudderstack.com/docs/sources/event-streams/sdks/client-side-features/consent-management/migrate-from-legacy-sdks/" >How to Migrate Consent Filters from the Legacy SDKs</a></li>
<li><a href="https://www.rudderstack.com/docs/data-governance/consent-management/custom-consent-manager/" >Custom Consent Management</a></li>
<li><a href="https://www.rudderstack.com/docs/data-governance/consent-management/overview/" >Consent Management Overview</a></li>
</ul>

