Deeplinkly

Glossary/Implementation artifacts

Bundle Identifier

Definition

A bundle identifier is the reverse-DNS string, such as com.example.shop, that uniquely identifies an iOS app to the operating system and the App Store.

Android's equivalent is the package name, and the two are close enough that teams treat them as one field and then spend an afternoon working out why a deep link or an install postback matches nothing. They are set in different places, allowed to differ from each other, and appear under different names in every attribution payload you will read.

The three identifiers people call the same thing

Bundle identifier, package name, and App ID — set in different places, used for different things.
Bundle identifier (iOS)Package name (Android)App ID (iOS)
Examplecom.example.shopcom.example.shopA1B2C3D4E5.com.example.shop
Set inPRODUCT_BUNDLE_IDENTIFIER in XcodeapplicationId in build.gradleApple Developer → Identifiers
Also appears asCFBundleIdentifier in Info.plistpackage in the built manifestapplication-identifier entitlement
Used by deep linking forNothing directlyassetlinks.json package_nameAASA appIDs
Unique acrossThe App StoreGoogle PlayOne developer account
Changeable after releaseNoNoNo

The column that catches people is the fourth. iOS authorises by App ID — prefix included — while Android authorises by package name plus certificate fingerprint. Neither platform accepts the other's identifier, and a config file that carries the wrong one is well-formed and inert.

Where each one is configured

On iOS the bundle identifier is a build setting, which means it can vary per configuration. On Android there are two similar-looking Gradle properties, and only one of them is the app's identity.

app/build.gradle.kts
android {
    // The Java/Kotlin package for generated sources (R, BuildConfig).
    // NOT the app's identity. Renaming it changes nothing publicly.
    namespace = "com.example.shop"

    defaultConfig {
        // The app's identity on the device and on Play.
        // This is the value assetlinks.json must contain.
        applicationId = "com.example.shop"
    }

    buildTypes {
        debug {
            // Debug installs alongside release as a SEPARATE app:
            // com.example.shop.debug
            applicationIdSuffix = ".debug"
        }
    }
}

applicationIdSuffix silently breaks App Links on debug builds

With applicationIdSuffix = ".debug", the installed package is com.example.shop.debug. Your assetlinks.json names com.example.shop, so verification fails on every debug build and succeeds on release — which reads exactly like "deep links are broken on my machine". Add a second statement for the debug package, with the debug signing key's fingerprint.

The iOS equivalent is a per-configuration bundle identifier, usually com.example.shop.dev for a Debug build. It needs its own App ID, its own Associated Domains capability, and its own entry in the appIDs array — which is why that array so often has three entries.

How each appears in attribution data

Every attribution system keys on one of these strings, and almost none of them agree on what to call the field. When an install postback matches no app, this table is usually where the answer is.

The same identifier, under the names different systems give it.
ContextField nameValue
App Store Connect / StoreKitbundleIdcom.example.shop
SKAdNetwork postbackapp-idThe numeric App Store ID, not the bundle ID
Google Play Install Referrerpackage namecom.example.shop
Firebase / GA4app_idBundle ID on iOS, package name on Android
Meta Install Referrerpackage namecom.example.shop
Smart App Banner meta tagapp-idNumeric App Store ID
assetlinks.jsonpackage_namecom.example.shop
apple-app-site-associationappIDsA1B2C3D4E5.com.example.shop

iOS has two identifiers and they are not interchangeable

The bundle identifier is a reverse-DNS string; the App Store ID is a number such as 123456789. SKAdNetwork postbacks, StoreKit product references and the smart app banner meta tag all use the numeric ID. Reporting that shows zero installs for an iOS app is frequently a bundle ID sitting in a field that wanted the number.

Read the identifier out of a real artifact
# iOS: from an .ipa
unzip -p Shop.ipa "Payload/*.app/Info.plist" | \
  plutil -extract CFBundleIdentifier raw -

# Android: from an APK or AAB
aapt2 dump badging app-release.apk | head -1

# Android: what is actually installed on the device
adb shell pm list packages | grep example

Choosing one, and living with it

The rules are short, and the cost of breaking them is a new App Store listing with no reviews and no ranking.

  • Reverse-DNS from a domain you control: com.example.shop, not shop or MyApp.
  • Letters, digits, hyphens and dots on iOS; Android additionally requires each dot-separated segment to start with a letter and forbids hyphens.
  • Keep iOS and Android identical unless you have a reason not to — every dashboard, export and support conversation is easier when there is one string.
  • Never reuse an identifier from a deleted app; on both stores it stays claimed.
  • Once an app is published, it is permanent. Changing it publishes a new app, and existing users are not migrated.

If you have inherited a mismatch — say com.example.shop on iOS and com.example.shopapp on Android — do not rename. Record both in your attribution configuration and in both association files, and move on. The rename costs more than the inconsistency.

AASA & assetlinks.json generator

Enter the bundle identifier and App ID Prefix for iOS and the package name and SHA-256 fingerprint for Android, and get both association files with the right identifier in the right field on each side. It runs entirely in the browser, so nothing is uploaded.

Open the aasa & assetlinks.json generator

Frequently asked questions

What is the difference between a bundle identifier and a package name?
They are the platform-specific names for the same idea: a reverse-DNS string that uniquely identifies an app. iOS calls it the bundle identifier and stores it as CFBundleIdentifier; Android calls it the package name and sets it as applicationId in Gradle. They are configured independently and are allowed to differ, so an attribution setup has to be told both rather than assuming one value.
Is applicationId the same as namespace in Gradle?
No. namespace is the Kotlin or Java package used for generated sources such as R and BuildConfig, and changing it is an internal refactor. applicationId is the app's public identity on the device and on Google Play, and it is the value that must appear as package_name in assetlinks.json. They often hold the same string, which is why the distinction is easy to miss.
Can I change my bundle identifier after the app is published?
No. On both the App Store and Google Play the identifier is permanent once an app is published. Shipping a different one creates a separate listing with no ratings, no reviews and no install history, and existing users are not migrated to it. Treat the identifier as a one-time decision made before first submission.
Why do my Android App Links fail only on debug builds?
Because applicationIdSuffix changes the installed package name — typically to com.example.shop.debug — while assetlinks.json names com.example.shop. Verification compares package name and signing fingerprint, so the debug build matches no statement and its links open the browser. Add a second statement to the file for the debug package with the debug keystore's SHA-256 fingerprint.
What is an App ID, and how is it different from a bundle identifier?
An App ID is the fully qualified iOS identifier: the App ID Prefix, a dot, then the bundle identifier, for example A1B2C3D4E5.com.example.shop. The apple-app-site-association file requires the full App ID, not the bundle identifier alone. Supplying only the bundle identifier produces a file that parses cleanly and authorises nothing.

Related terms

  • Apple Team IDAn Apple Team ID is the ten-character alphanumeric identifier Apple assigns to a developer account, used to namespace that account's App IDs, certificates, and entitlements.
  • assetlinks.jsonassetlinks.json is a Digital Asset Links statement file hosted at a domain's /.well-known/ path that authorises a named Android app, identified by package name and signing certificate fingerprint, to handle that domain's URLs.
  • SHA-256 Certificate FingerprintA SHA-256 certificate fingerprint is the SHA-256 hash of an app signing certificate, written as 32 colon-separated hexadecimal bytes, used to prove that a specific build was signed by a specific key.
  • Apple App Site Association (AASA)The apple-app-site-association file is a JSON document hosted at a domain's /.well-known/ path that tells iOS which app is allowed to handle which URLs on that domain.