vasSessionBegin method

Future<void> vasSessionBegin({
  1. required List<VasCommandConfiguration> configurations,
  2. required void onResponse(
    1. List<VasResponse> responses
    ),
  3. Future<void> onError(
    1. NfcError error
    )?,
  4. void onBecameActive()?,
  5. String? alertMessage,
})

Starts a Value Added Services session, which reads Apple Wallet passes -- loyalty cards, membership cards -- rather than NFC tags.

Requires com.apple.developer.nfc.readersession.formats to include VAS in the app's entitlements file, which is not part of Xcode's Near Field Communication Tag Reading capability -- the App ID has to be provisioned for VAS separately -- plus NFCReaderUsageDescription in Info.plist. Nothing else: the pass type identifiers are supplied per command through VasCommandConfiguration.passTypeIdentifier, not by any plist key. A missing entitlement surfaces asynchronously through onError as a security violation. See the README's iOS setup list.

Implementation

Future<void> vasSessionBegin({
  required List<VasCommandConfiguration> configurations,
  required void Function(List<VasResponse> responses) onResponse,
  Future<void> Function(NfcError error)? onError,
  void Function()? onBecameActive,
  String? alertMessage,
}) async {
  // The VAS slots are separate from the reader session's: iOS runs the two as independent
  // session objects, and sharing the slots meant stopping one unregistered the other's
  // callbacks while it was still up.
  final restore = NfcCallbacks.instance.armVasSession(
    response: (responses) => onResponse([
      for (final response in responses)
        VasResponse(
          status: _statusFromWire(response.status),
          vasData: response.vasData,
          mobileToken: response.mobileToken,
        ),
    ]),
    error: onError,
    active: onBecameActive,
  );

  try {
    await iosApi.vasSessionBegin([
      for (final configuration in configurations)
        VasCommandConfigurationPigeon(
          mode: switch (configuration.mode) {
            VasMode.normal => VasModePigeon.normal,
            VasMode.urlOnly => VasModePigeon.urlOnly,
          },
          passTypeIdentifier: configuration.passTypeIdentifier,
          url: configuration.url,
        ),
    ], alertMessage);
  } on Object {
    // Only the VAS slots, and only back to what they were: an empty configuration list or
    // a device without VAS fails here without any session having been disturbed.
    restore();
    rethrow;
  }
}