Skip to main content

iOS Push Provisioning v2.0.0

· 8 min read
warning

This version is a major update over v1 that includes breaking changes in the public interface of the SDK.

Make sure you address these changes by following the instructions in the migration guide at the end of the release notes for this version.

Supported versions:

  • iOS: 15.1 and onwards
  • Xcode: 16.4 and 26
  • Swift: 5.9 and onwards

Added

  • Added WPPComponents.initialize(environment: , uiKey:).
  • Added WPPComponents.makePassEntriesForCardsWith(authenticationToken:, entries:, imageFallback:).
  • Added an async version of WPPComponents.addPaymentPass(authenticationToken: ,clientPaymentCardId: ,certificates: ,nonce: ,nonceSignature:) in case you prefer to avoid callbacks.
  • Added extra properties reason, domain, underlyingErrorCode, and errorCode to WPPError to provide better context into what went wrong.
  • Added WeavrProvisioningErrorCode and WeavrProvisioningErrorDomain to better represent errors. The end of this document contains appendixes with their values.
  • Added more cases to the AddCardToWalletStatus enum to better reflect the potential states a card can be in within Apple Pay.
  • Added a DeviceType enum to let you query the card status in a specific device between phone, or wallet. Also included an either value for convenience. The potential values and how to use them are listed in the Appendix.
  • Added WeavrProvisioningExtensionHandler to simplify the non-ui wallet extension integration.

Breaking change

  • Changed the completion block of WPPComponents.addPaymentPass(authenticationToken: ,clientPaymentCardId: ,certificates: ,nonce: ,nonceSignature: , completionHandler:) so that the PKAddPaymentPassRequest becomes nullable (PKAddPaymentPassRequest?). This allows to report failures to create the request, and is consistent with Apple's requirements in the PKIssuerProvisioningExtensionHandler.generateAddPaymentPassRequestForPassEntryWithIdentifier method.

Removed

  • Removed WPPComponents.makePassEntryForCardWith.
  • Removed WPPError.message in favour of newly added properties.

Deprecated

  • Deprecated WPPComponents.canAddCardToWallet(panLastFour:) in favour of WPPComponents.getCardStatusInWallet(forCardWithLastFourDigits:, deviceType:).

Migration guide

Mandatory : adding provisioning_config file

For the correct functioning of our SDK, we require a configuration file necessary to initialise one of our dependencies.

To do so, you need to add the provisioning_config file to your iOS bundle. For instance, it can be located in an assets folder, and added to your app's and extension targets.

Get in touch with our support team at support@weavr.io to obtain this file.

warning

This file is sensitive, make sure you store and distribute it in a secure manner.

Our recommendation is not to commit this file to your repository, and distribute it via a secure file sharing software amongst your team.

Finally, use the secrets feature of your CI software of choice to avoid impacting your CI/CD pipelines.

Mandatory breaking changes

The following changes must be implemented to be able to provision correctly.

Initialising the SDK

Starting from version 2.0.0, you need to initialise the SDK during your app's start up using the following call:

WPPComponents.initialize(environment: .Sandbox, uiKey: "<MY-UI-KEY>")

Notice that the UI key that is expected as a parameter can be obtained from the Weavr portal.

Provisioning a card via Wallet Extension

Option 1: using Weavr provisioning extension handler

WeavrProvisioningExtensionHandler has been introduced as a simplified way to implement the non-UI Wallet Extension. This class implements PKIssuerProvisioningExtensionHandler and implements the logic required to satisfy Apple's needs.

All you have to do to use it is to change your Extension handler to implement WeavrProvisioningExtensionHandler, and implement the following getters:

IntentHandler.swift
import WeavrPushProvisioning
import UIKit
import Foundation

class IntentHandler: WeavrProvisioningExtensionHandler {

override func loadCardsFromDisk() -> [ManagedCard] {
// TODO Load the managed cards from disk.
}

override func loadTokenFromDisk() -> Token? {
// TODO Load the user's token from Disk
}

override func loadCardsFromNetwork() async throws -> [ManagedCard] {
// TODO Load the cards from the network
}

override func uiKey() -> String {
// TODO Return the UI key of the project to use.
}

override func defaultCardImage() -> CGImage {
// TODO Return a CGImage for the card image you want to default to if we can't load the image URL
// or no URL is provided.
}
}

Option 2: using Apple's extension handler

Although we recommend using WeavrProvisioningExtensionHandler to re-use well tested and Apple certified code, you can still implement PKIssuerProvisioningExtensionHandler on your own if you prefer to.

To migrate to v2.0.0 using PKIssuerProvisioningExtensionHandler you must change the following calls:

Making pass entries

We have migrated from makePassEntryForCardWith, which needs to be called once for each card available to provision, to WPPComponents.makePassEntriesForCardsWith(authenticationToken:, entries:, imageFallback:), which takes an array of entries, allowing the workload that the device has to perform to be reduced before obtaining a result.

The following snippet showcases how to call the new method:

let entries: [PassEntryInfo] = cards.compactMap { card in
// Check if the card state is notAdded.
let canAddCard = .notAdded == WPPComponents.getCardStatusInWallet(
forCardWithLastFourDigits: card.cardNumberLastFour,
// Note that the device type will depend on whether you are responding to `passEntries` or
// `remotePassEntries` in the non-UI extension.
deviceType: .phone
)

guard canAddCard else {
return nil
}

// PassEntryInfo is a struct that allows us to encapsulate all data needed for the pass entry.
return PassEntryInfo(
cardId: card.id,
cardholderName: card.cardholder,
panLastFour: card.cardNumberLastFour,
cardDescription: card.friendlyName,
cardImageURL: card.digitalArtworkUrl.flatMap { URL(string: $0) }
)
}

// The new method returns a list of pass entries to provide to Apple.
let passEntries = await WPPComponents.makePassEntriesForCardsWith(
authenticationToken: authenticationToken,
entries: entries.compactMap { $0 },
// TODO Provide default image
imageFallback: myDefaultCardImage
)

Adding the payment pass

The completion block in WPPComponents.addPaymentPass(authenticationToken: ,clientPaymentCardId: ,certificates: ,nonce: ,nonceSignature: , completionHandler:) has been updated to receive a nullable PKAddPaymentPassRequest?.

Therefore, if your method is expecting a non optional value, or explicitly defines the PKAddPaymentPassRequest type received, you need to update your code to handle the nullable value.

More commonly, we expect you to just pass through the same completion block that Apple provides you, and therefore no change would be needed.

Additionally, a coroutine based method has been introduced in case you prefer it over callbacks.

Overall, generateAddPaymentPassRequestForPassEntryWithIdentifier should be implemented in a way similar to the next snippet:

override func generateAddPaymentPassRequestForPassEntryWithIdentifier(
_ identifier: String,
configuration: PKAddPaymentPassRequestConfiguration,
certificateChain certificates: [Data],
nonce: Data,
nonceSignature: Data
) async -> PKAddPaymentPassRequest? {
// Load the token from the storage shared with your app and UI extension
guard let token = loadToken() else {
// If the token is not available, return nil to signal Apple
// that something went wrong
return nil
}

// Otherwise, call `WPPComponents.addPaymentPass` and pass the result back to Apple
return await WPPComponents.addPaymentPass(
authenticationToken: token,
clientPaymentCardId: identifier,
certificates: certificates,
nonce: nonce,
nonceSignature: nonceSignature
)
}

The following changes are not mandatory for this version, but we encourage you accommodate them now in order to avoid breaking changes in the future.

Checking the status of the card

A new method has been added to obtain the card's status, allowing you to specify on which device you want to check. Additionally a new device type either is included allowing to check the status for either the phone or the watch.

// Check the state of the card ending in 1234 on the phone:
WPPComponents.getCardStatusInWallet(forCardWithLastFourDigits: "1234", deviceType: .phone)

// Check the state of the card ending in 1234 on the watch:
WPPComponents.getCardStatusInWallet(forCardWithLastFourDigits: "1234", deviceType: .watch)

// Check the state of the card ending in 1234 on either the phone, or watch:
WPPComponents.getCardStatusInWallet(forCardWithLastFourDigits: "1234", deviceType: .either)

Finally, new states that the card can be in have been added, to better reflect what Apple reports to us. You can observe these potential new states on the Card Status section.

Appendices

Error codes

WeavrProvisioningErrorCodeDescriptionInteger Code
cardNotEligibleForProvisioningThe card is not eligible for provisioning.1003
cantFindProviderToUseCouldn't find the provider to use. This can occur when there's a problem obtaining the provider from the API, e.g. a client error or server error.1004
emptyEntriesNo card entries provided by the user.1005
userCancelledThe user cancelled the flow.1006
unauthorizedThe token is not present or is rejected during provisioning.2002
missingCardIdThe card Id provided is empty.2003
missingCardLastFourDigitsThe last four digits of the card provided is empty.2005
missingCardholderNameThe card holder name of the card provided is empty.2006
missingCardDescriptionThe card description of the card provided is empty.2007
cantBeAddedToAppleWalletCouldn't add the card to Apple Wallet.3001
cantFindViewControllerCouldn't find the top most view controller to display the provisioning view controllers.5001
cantObtainPaymentPassRequestConfigurationThe payment pass request configuration needed by Apple Wallet to provision the card couldn't be created.5002
cantCreateProvisioningExtensionPaymentPassEntryThe SDK couldn't create the extension payment pass entry needed by Apple Wallet.5003
cantCreateProvisioningExtensionPaymentPassRequestThe SDK couldn't create the payment pass request to add the card selected by the user to Apple Wallet.5004
unknownAn unknown error occurred.-1

Card status

AddCardToWalletStatusDescription
unavailableApple Pay is not available on the device, or there is no Apple Watch connected when querying for card status on the watch.
alreadyAddedThe card has already been added to Apple Pay. Equivalent to PKSecureElementPass.PassActivationState.activated.
notAddedThe card has not been added yet to Apple Pay, but it can be.
requiresActivationThe card is present in the Wallet but has not yet been activated.
activatingThe card isn't ready to use, but will be soon.
suspendedThe card has been suspended and is not available to use.
deactivatedThe card has been deactivated.
unknownAn unknown state. Shouldn't occur.

Device types

DeviceTypeDescription
phoneThe card state query will be performed on the phone or tablet itself.
watchThe card state query will be performed on the user's Apple Watch.
eitherThe card state query will be performed on both the phone and the watch. The result of the query will be notAdded if the card hasn't been added to the watch yet. In all other cases, the result will match that of the phone device type.