> For the complete documentation index, see [llms.txt](https://docs.payments.thalescloud.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.payments.thalescloud.io/push-provisioning/implement-push-provisioning/implement-push-to-digital-wallets/apple-wallet-extension.md).

# Apple wallet extension

## Apple wallet extension

Use Apple Wallet extensions to let end users discover and provision cards directly from the Apple Wallet application, in addition to the issuer application.

Through application extensions, you can expose custom functionality and content outside the issuer application and make it available while the end user interacts with Apple Wallet or the system.

Provisioning cards within the Apple Wallet application improves the in-app experience by avoiding manual entry of payment card details.

Refer to the [Apple extension overview](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html) for general information about iOS extensions.

### User experience

The end-to-end Apple Wallet extension flow has two main phases:

1. Apple Wallet discovers that the issuer application has cards available for provisioning.
2. The end user authenticates and selects which cards to add; Apple Wallet then requests the provisioning payload from the issuer application via the D1 SDK.

#### Discovery and authentication

<figure><img src="/files/gapT5Wykb4VjxkVcCov6" alt="Apple Wallet showing issuer application option for adding a card"><figcaption><p>Apple Wallet shows the issuer application as an option when cards are available for provisioning.</p></figcaption></figure>

{% stepper %}
{% step %}

#### Discover issuer application in Apple Wallet

Apple Wallet checks with the issuer application to determine whether cards are available. If cards are available, the issuer application appears in the list of options.
{% endstep %}

{% step %}

#### The end user selects the issuer application

When the end user selects the issuer application, Apple Wallet triggers the issuer application UI extension to authenticate the end user.
{% endstep %}

{% step %}

#### Authenticate the end user

The issuer application’s UI extension authenticates the end user using the same credentials and methods (for example, manual login and biometrics) as the main issuer application.
{% endstep %}
{% endstepper %}

#### Selecting and provisioning cards

<figure><img src="/files/fedaSBQ2uBi3Vd3m1CAI" alt="Apple Wallet showing available cards from the issuer application"><figcaption><p>Apple Wallet fetches and displays the cards available from the issuer application.</p></figcaption></figure>

{% stepper %}
{% step %}

#### Apple Wallet fetches available cards

Apple Wallet requests the list of available cards from the issuer application through the non-UI extension. The end user sees a list of cards to add.
{% endstep %}

{% step %}

#### View card details

The end user can review card details (for example, card art, product title, and last four digits) before adding the card.
{% endstep %}

{% step %}

#### Accept terms and conditions

The end user reviews and accepts the issuer’s and payment network’s terms and conditions.
{% endstep %}

{% step %}

#### Provisioning payload and tokenization

For each selected card, Apple Wallet requests a provisioning payload from the issuer application and then initiates the tokenization flow with the payment network TSP.
{% endstep %}
{% endstepper %}

### Extensions

The Apple Wallet integration requires two types of extensions from the issuer application:

* **Non-UI extension**\
  Reports the status of the extension flow and card availability. This covers the card discovery and provisioning payload steps. It is implemented by extending the D1 SDK base class `D1IssuerProvisioningExtensionHandler`.
* **UI extension**\
  Authenticates the end user. This is a separate screen that uses the same login credentials as the issuer application. It implements the `D1IssuerProvisioningExtensionAuthorizationProviding` protocol.

{% hint style="info" %}
Apple Wallet extensions are installed at the same time as the issuer application. For Apple Wallet to detect that there are passes to add and to show the extension, the end user must launch the issuer application at least once.
{% endhint %}

D1 SDK provides a base implementation for the non-UI extension so that you do not need to implement the complete logic yourself. The issuer application calls `D1Task.configure()` with `WalletExtensionConfig` during application launch to share card details with the extension. While this configuration is required only once after installation, we recommend calling it on every application launch to keep card information up to date.

<figure><img src="/files/Y91oobQTVHJcIsr6kuFA" alt="High-level flow for configuring the Apple Wallet extension via D1 SDK"><figcaption><p>High-level flow: the issuer application configures the D1 SDK so Apple Wallet can discover cards.</p></figcaption></figure>

After a successful authentication in the UI extension, the issuer application must call `D1IssuerProvisioningExtensionAuthorizationProviding.login()` so that the SDK can continue with the non-UI extension steps.

### Sequence diagrams

#### 1. Apple Wallet shows the issuer application button

<figure><img src="/files/03DWV5hS8jO6eTo7r8am" alt="Sequence diagram showing how Apple Wallet discovers the issuer application"><figcaption><p>Apple Wallet discovers that the issuer application has cards available for provisioning.</p></figcaption></figure>

#### 2. End user authentication

<figure><img src="/files/MPmEXhIUTAdjY33WrxAD" alt="Sequence diagram showing Apple Wallet triggering issuer UI extension for authentication"><figcaption><p>Apple Wallet launches the issuer application UI extension for authentication.</p></figcaption></figure>

#### 3. Available cards are displayed

<figure><img src="/files/uXZeZUejQ8JFotErRC9f" alt="Sequence diagram showing Apple Wallet requesting card list from issuer"><figcaption><p>Apple Wallet requests the list of available cards and shows them to the end user.</p></figcaption></figure>

#### 4. Tokenization

<figure><img src="/files/WHvJ3sTDY5aW7DJmSif4" alt="Sequence diagram showing Apple Wallet requesting provisioning payload and tokenizing"><figcaption><p>Apple Wallet requests provisioning payloads and tokenizes the selected cards.</p></figcaption></figure>

### Integrate Apple Wallet via the D1 SDK

#### SDK configuration

When you perform [iOS SDK initialization](/push-provisioning/integrate-the-d1-sdk/getting-started/configuration/3.-initialization/ios-initialization.md), the issuer application must also call `configure(ConfigParams.walletExtensionConfig(...))` and provide:

**`cardParamsList`**

Use this variant when you have a single `consumerId` and a single `issuerId`:

* `cardParamsList` – List of card IDs, card art, product titles, and last four digits. Card art and product title are displayed in Apple Wallet.
* `appGroupID` – The shared container ID so the D1 SDK can share data between the issuer application and its extensions.

```swift
let coreConfig = ConfigParams.coreConfig(consumerID: "<obtained from server>")
let cardConfig = ConfigParams.cardConfig()

let card = ConfigParams.CardParams(cardID: "<obtained from server>",
        cardArt: UIImage(named: "<image_name>")!, productTitle: "<product title>", last4: "<last4>")
let walletExtensionConfig = ConfigParams.walletExtensionConfig(cardParamsList: [card],
        appGroupID: "<App Group ID defined in app capabilities>")

d1Task.configure([coreConfig, cardConfig, walletExtensionConfig]) { errors in
    if let errors = errors {
        for error in errors {
            // Inspect errors to identify which configuration failed
            // Refer to the D1 SDK integration error management section
        }
    }
}

// Save consumerId to authenticate the same end user in the wallet extension flow
saveToKeychain(key: "D1ConsumerID", value: "<consumer_ID>")

func saveToKeychain(key: String, value: String) {
    guard let valueData = value.data(using: .utf8) else {
        return
    }
    let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
        kSecAttrService as String: "<service associated, e.g. IssuerAppService>",
        kSecAttrAccessGroup as String: "<App Group ID defined in app capabilities>",
        kSecAttrAccount as String: key]
    var addQuery = query
    addQuery[kSecValueData as String] = valueData
    let status = SecItemAdd(addQuery as CFDictionary, nil)

    if status == errSecDuplicateItem  {
        let updatedData: [String: Any] = [kSecValueData as String: valueData]
        SecItemUpdate(query as CFDictionary, updatedData as CFDictionary)
    }
}
```

**`issuerParamsList`**

Use this variant when you support multiple combinations of `issuerId` and `consumerId`:

* `issuerParamsList` – List of `cardParamsList` with the corresponding `issuerId` and `consumerId`.
* `appGroupID` – The shared container ID so the D1 SDK can share data between the issuer application and its extensions.

```swift
let coreConfig = ConfigParams.coreConfig(consumerID: "<obtained from server>")
let cardConfig = ConfigParams.cardConfig()

var issuerParamsList: [ConfigParams.IssuerParams] = []
let card1 = ConfigParams.CardParams(cardID: "<obtained from server>",
        cardArt: UIImage(named: "<image_name>")!, productTitle: "<product title>", last4: "<last4>")
let card2 = ConfigParams.CardParams(cardID: "<obtained from server>",
        cardArt: UIImage(named: "<image_name>")!, productTitle: "<product title>", last4: "<last4>")
let card3 = ConfigParams.CardParams(cardID: "<obtained from server>",
        cardArt: UIImage(named: "<image_name>")!, productTitle: "<product title>", last4: "<last4>")
let issuerParam1 = ConfigParams.IssuerParams(cardParams: card1, issuerID: "<issuerId 1>", consumerID: "<consumerId 1>")
let issuerParam2 = ConfigParams.IssuerParams(cardParams: card2, issuerID: "<issuerId 2>", consumerID: "<consumerId 2>")
let issuerParam3 = ConfigParams.IssuerParams(cardParams: card3, issuerID: "<issuerId 2>", consumerID: "<consumerId 3>")
issuerParamsList.append(issuerParam1)
issuerParamsList.append(issuerParam2)
issuerParamsList.append(issuerParam3)

let walletExtensionConfig = ConfigParams.walletExtensionConfig(issuerParamsList: issuerParamsList,
        appGroupID: "<App Group ID defined in app capabilities>")

d1Task.configure([coreConfig, cardConfig, walletExtensionConfig]) { errors in
    if let errors = errors {
        for error in errors {
            // Inspect errors to identify which configuration failed
            // Refer to the D1 SDK integration error management section
        }
    }
}

// Save issuerId values for use during wallet extension authentication
saveToKeychain(key: "issuerID1", value: "<issuerId 1>")
saveToKeychain(key: "issuerID2", value: "<issuerId 2>")

func saveToKeychain(key: String, value: String) {
    guard let valueData = value.data(using: .utf8) else {
        return
    }
    let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
        kSecAttrService as String: "<service associated, e.g. IssuerAppService>",
        kSecAttrAccessGroup as String: "<App Group ID defined in app capabilities>",
        kSecAttrAccount as String: key]
    var addQuery = query
    addQuery[kSecValueData as String] = valueData
    let status = SecItemAdd(addQuery as CFDictionary, nil)

    if status == errSecDuplicateItem  {
        let updatedData: [String: Any] = [kSecValueData as String: valueData]
        SecItemUpdate(query as CFDictionary, updatedData as CFDictionary)
    }
}
```

{% hint style="info" %}
We strongly recommend using the `last4` parameter.

Without `last4`, the SDK may misinterpret tokenization status in shared-device scenarios. For example, if User A tokenizes a card and then User B logs into the same issuer application on the same device, the SDK might incorrectly assume tokenization is already complete for User B.
{% endhint %}

#### App Group ID

By default, the issuer application and its extensions do not share storage. Use app groups to share storage between them. See the [Apple extension overview](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html) for more details.

`appGroupID` identifies the shared container between the issuer application and its extensions. You configure it in Xcode under **Capabilities**.

<figure><img src="/files/pKsoOhJQgeWX6K2xWIUl" alt="Xcode configuration for app groups used by issuer application and extensions"><figcaption><p>Example of configuring an app group in Xcode for the issuer application and its extensions.</p></figcaption></figure>

{% hint style="info" %}
Ensure that the App Group ID is correctly added to Xcode before using it in `configure(ConfigParams.walletExtensionConfig(...))`. Otherwise, the D1 SDK returns the `invalidAppGroupID` error.
{% endhint %}

#### Apple Pay entitlement

Similar to in-app Apple Pay provisioning, you must add the entitlement `com.apple.developer.payment-pass-provisioning` to both the UI and non-UI extension targets. For onboarding details, refer to your Apple Wallet provisioning documentation or **Push provisioning onboarding for wallets**.

#### UI extension

**Create a UI extension**

<figure><img src="/files/975D1HBMxMPJKDdkE9kx" alt="Xcode dialog for creating an intents UI extension"><figcaption><p>Create an Intents UI Extension target in Xcode for the Apple Wallet UI extension.</p></figcaption></figure>

{% stepper %}
{% step %}
Add a new target in Xcode of type **Intents UI Extension**.
{% endstep %}

{% step %}
In the new target, enable the same App Group ID and entitlement values as in the issuer application. If the App Group ID differs, the `login` API returns the `walletExtensionAppGroupIDNotFound` error.
{% endstep %}

{% step %}
Update the extension `Info.plist`:

* Set `NSExtensionPointIdentifier` to `com.apple.PassKit.issuer-provisioning.authorization`.
* Set `NSExtensionPrincipalClass` to a class that conforms to `D1IssuerProvisioningExtensionAuthorizationProviding`.
  {% endstep %}
  {% endstepper %}

Example entitlements and extension configuration:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.developer.payment-pass-provisioning</key>
    <true/>
    <key>com.apple.security.application-groups</key>
    <array>
        <string>group.com.example.IssuerApp</string>
    </array>
</dict>
</plist>
```

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSExtension</key>
    <dict>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.PassKit.issuer-provisioning.authorization</string>
        <key>NSExtensionPrincipalClass</key>
        <string>$(PRODUCT_MODULE_NAME).IntentViewController</string>
    </dict>
</dict>
</plist>
```

**Integrate the SDK for the UI extension**

The D1 SDK provides the `D1IssuerProvisioningExtensionAuthorizationProviding` protocol, which extends Apple’s [`PKIssuerProvisioningExtensionAuthorizationProviding`](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionauthorizationproviding) and adds a `login` API.

In the UI extension, implement a view controller that conforms to `D1IssuerProvisioningExtensionAuthorizationProviding`, authenticates the end user, and calls `login` with a D1 access token (for example, a JWT from your backend). See your **SDK login** documentation for token generation.

**Login with a single issuerId**

```swift
import UIKit
import PassKit
import D1

class IntentViewController: UIViewController, D1IssuerProvisioningExtensionAuthorizationProviding {
    var completionHandler: ((PKIssuerProvisioningExtensionAuthorizationResult) -> Void)?
    private let authButton = UIButton(type: .system)

    override func viewDidLoad() {
        super.viewDidLoad()
        // Configure UI layout
        authButton.addTarget(self, action: #selector(authButtonTouched), for: .touchUpInside)
    }

    @objc func authButtonTouched() {
        // Load authentication-related data from Keychain (at minimum consumerID)
        let consumerID = loadFromKeychain(key: "D1ConsumerID")

        // Provide both manual and biometric authentication methods as required by Apple

        // After successful authentication, retrieve the D1 access token for this consumer
        var issuerToken = getAccessToken(sub: consumerID!)

        // Use the access token to log in to the D1 solution
        login(&issuerToken) { [weak self] error in
            if let _ = error {
                self?.completionHandler?(.canceled)
            } else {
                self?.completionHandler?(.authorized)
            }
        }
    }

    func loadFromKeychain(key: String) -> String? {
        let getQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: "<service associated, e.g. IssuerAppService>",
            kSecAttrAccessGroup as String: "<App Group ID defined in app capabilities>",
            kSecAttrAccount as String: key,
            kSecReturnData as String: true]
        var item: AnyObject?
        let status = SecItemCopyMatching(getQuery as CFDictionary, &item)
        if status == errSecSuccess,
           let data = item as? Data {
            return String(data: data, encoding: .utf8)
        }
        return nil
    }

    func getAccessToken(sub: String) -> Data? {
        // Implement retrieval of the D1 access token for the given subject
        return Data()
    }
}
```

**Login with multiple issuerIds**

```swift
import UIKit
import PassKit
import D1

class IntentViewController: UIViewController, D1IssuerProvisioningExtensionAuthorizationProviding {
    var completionHandler: ((PKIssuerProvisioningExtensionAuthorizationResult) -> Void)?
    private let authButton = UIButton(type: .system)

    override func viewDidLoad() {
        super.viewDidLoad()
        // Configure UI layout
        authButton.addTarget(self, action: #selector(authMultiIssuerButtonTouched), for: .touchUpInside)
    }

    @objc func authMultiIssuerButtonTouched() {
        // Load issuer IDs from Keychain
        let issuerId1 = loadFromKeychain(key: "issuerID1")
        let issuerId2 = loadFromKeychain(key: "issuerID2")

        // Provide both manual and biometric authentication methods as required by Apple

        // After successful authentication, retrieve D1 access tokens for each issuer
        let issuerToken1 = getAccessToken(iss: issuerId1!)
        let issuerToken2 = getAccessToken(iss: issuerId2!)
        var issuerTokens = [issuerToken1, issuerToken2]

        // Use the access tokens to log in to the D1 solution
        login(&issuerTokens) { [weak self] error in
            if let _ = error {
                self?.completionHandler?(.canceled)
            } else {
                self?.completionHandler?(.authorized)
            }
        }
    }

    func loadFromKeychain(key: String) -> String? {
        let getQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: "<service associated, e.g. IssuerAppService>",
            kSecAttrAccessGroup as String: "<App Group ID defined in app capabilities>",
            kSecAttrAccount as String: key,
            kSecReturnData as String: true]
        var item: AnyObject?
        let status = SecItemCopyMatching(getQuery as CFDictionary, &item)
        if status == errSecSuccess,
           let data = item as? Data {
            return String(data: data, encoding: .utf8)
        }
        return nil
    }

    func getAccessToken(iss: String) -> Data {
        // Implement retrieval of the D1 access token for the given issuer
        return Data()
    }
}
```

{% hint style="info" %}
If cards share the same `issuerId` but have different `consumerId` values, generate a single `issuerToken` for that `issuerId`.
{% endhint %}

#### Non-UI extension

**Create a non-UI extension**

<figure><img src="/files/lmOlgw6FWEnPaSPCqNFm" alt="Xcode dialog for creating an intents extension without UI"><figcaption><p>Create an Intents Extension target in Xcode for the Apple Wallet non-UI extension.</p></figcaption></figure>

{% stepper %}
{% step %}
Add a new target in Xcode of type **Intents Extension**. Clear **Include UI Extension** because the UI extension was created previously.
{% endstep %}

{% step %}
In the new target, enable the same App Group ID and entitlement values as in the issuer application. If the App Group ID differs, D1 SDK operations return the `walletExtensionAppGroupIDNotFound` error.
{% endstep %}

{% step %}
Update the extension `Info.plist`:

* Set `NSExtensionPointIdentifier` to `com.apple.PassKit.issuer-provisioning`.
* Set `NSExtensionPrincipalClass` to a class that extends `D1IssuerProvisioningExtensionHandler`.
  {% endstep %}
  {% endstepper %}

Example entitlements and extension configuration:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.developer.payment-pass-provisioning</key>
    <true/>
    <key>com.apple.security.application-groups</key>
    <array>
        <string>group.com.example.IssuerApp</string>
    </array>
</dict>
</plist>
```

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSExtension</key>
    <dict>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.PassKit.issuer-provisioning</string>
        <key>NSExtensionPrincipalClass</key>
        <string>$(PRODUCT_MODULE_NAME).IntentHandler</string>
    </dict>
</dict>
</plist>
```

**Integrate the SDK for the non-UI extension**

The D1 SDK provides the base class `D1IssuerProvisioningExtensionHandler`, which extends Apple’s [`PKIssuerProvisioningExtensionHandler`](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler) and implements the required APIs. In most cases, you only need to extend this class.

```swift
import D1

class IntentHandler: D1IssuerProvisioningExtensionHandler {
}
```

#### Error handling

Optionally, override `errorEncountered(_:)` in the non-UI extension to log or track errors received during the extension flow.

```swift
import D1

class IntentHandler: D1IssuerProvisioningExtensionHandler {

    override func errorEncountered(_ error: D1Error) {
        print("Error = \(error.localizedDescription)")
    }
}
```

Typical error cases include:

* The `appGroupID` is invalid or not found.
* The D1 backend returns an error.

```console
# Example: error in status API
SDK Error = Error in status API. Cause: [2](-25300) Failed storage access. 
Error: [20009]The appGroupID is not found in Apple Pay Wallet UI and Non-UI extension. 
It could be due to several reasons: In the main application, there is no wallet extension 
configuration by calling `D1Task.configure(_:completion:)` with 
`ConfigParams.walletExtensionConfig(cardParamsList:appGroupID:)` OR 
the appGroupID defined in the main application capabilities is not defined in the UI 
and non-UI extension targets capabilities.

# Example: error in passEntries API
SDK Error = Error in passEntries API. Cause: [3]Received a server error with 
HTTP error code 400 and message 'Server error'. Error: [40001]Errors encountered 
whilst executing card operations.

# Example: error in remotePassEntries API
SDK Error = Error in remotePassEntries API. Cause: [3]Received a server error 
with HTTP error code 400 and message 'Server error'. Error: [40001]Errors encountered 
whilst executing card operations.

# Example: error in generate API
SDK Error = Error in generate API. Cause: [3]Received a server error with 
HTTP error code 400 and message '911. Operation failed .1'. Error: [40001]Errors encountered 
whilst executing card operations.
```

#### Apple functional requirements

To align with Apple’s functional requirements, the D1 SDK `D1IssuerProvisioningExtensionHandler` and the issuer application adopt the following practices:

* Display card art and cardholder name.
* Determine whether cards are available (eligibility) within 100 ms of Apple invoking the API.
* Provide card art and app icons with squared edges.
* Support both manual and biometric login methods for authentication in the UI extension.

### FAQ

<details>

<summary><strong>How can the issuer application provide card availability to Apple Wallet before the end user logs in?</strong></summary>

The end user must log in to the issuer application at least once so that the extension can be updated with card status. The issuer application calls `configure(ConfigParams.walletExtensionConfig())` to provide the list of cards to the D1 SDK.

</details>

<details>

<summary><strong>If a login fails, where is the error shown?</strong></summary>

We recommend showing login errors directly in the UI extension’s login screen. The UI extension should follow the same UX guidelines as the issuer application’s login.

</details>

<details>

<summary><strong>What are the size and resolution requirements for card images?</strong></summary>

The digital card image should follow Apple’s functional requirements:

* Provide images as PNG (recommended) or vector PDF.
  * Wallet extensions have stricter memory limits than main apps.
  * PDF files increase memory usage because the OS converts them to PNG.
* Use a resolution of 1536 × 969.
* Ensure the image size is less than 4 MB.
* Use squared (not rounded) corners.
* Exclude physical-card-only elements (for example, PAN, embossed characters, hologram, chip contacts).
* Use landscape orientation. If the physical card is portrait, reorient it to landscape.
* Optionally add the contactless indicator when NFC payments are supported.

</details>

<details>

<summary><strong>Do extensions require new bundle identifiers?</strong></summary>

Yes. The new bundle identifiers must be included in the `associatedApplicationIdentifier` of the payment network operator (PNO). iOS supports wildcard bundle identifiers.

Issuers must also update existing passes in Apple Wallet using PNO APIs. If the bundle ID is not included, cards added through the Apple Wallet extension cannot be accessed by the issuer application (for example, for digitization state checks).

</details>

<details>

<summary><strong>What could be wrong if the issuer application icon does not appear in Apple Wallet?</strong></summary>

Check the following:

* The issuer application is installed and has been opened at least once.
* The issuer application calls `configure(ConfigParams.walletExtensionConfig())` to update the card list.
* The issuer application and both extensions use the same `appGroupID` in `configure(ConfigParams.walletExtensionConfig())` and in Xcode capabilities.

</details>

<details>

<summary><strong>Is in-app provisioning required for Apple Wallet extensions to work?</strong></summary>

Yes. The issuer application must first implement in-app push provisioning to wallets before adding Apple Wallet extensions.

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.payments.thalescloud.io/push-provisioning/implement-push-provisioning/implement-push-to-digital-wallets/apple-wallet-extension.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
