Ultraligh¶
Ultralight enables Beamsync, a proximity-based data transmission mechanism that broadcasts passenger data to nearby airport touchpoints (for example, e-gates or self-service kiosks). This allows passengers to pass through airport processes without re-scanning their documents at each step.
In the current SDK architecture, Ultralight is integrated through the Enrolment SDK facade.
You provide your own UltralightProvider during initialization, and Enrolment exposes two methods to
control the sharing lifecycle: share() (sets passengers and starts broadcasting) and stopSharing().
Prerequisites¶
Before integrating Ultralight, ensure you have:
- Ultralight API key — Contact your Amadeus liaison to obtain one
- Ultralight provider dependency — Added to your project (see import instructions below)
- Minimum SDK level 26 (same as Enrolment SDK)
- Required permissions (brought transitively by the Ultralight provider dependency):
BLUETOOTH_SCAN,BLUETOOTH_CONNECT(Bluetooth Low Energy)ACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION(required for BLE scanning)
- Runtime requirements:
- Bluetooth must be enabled on the device
- Location services must be enabled on the device
Note
The SDK validates that Bluetooth and Location are enabled before starting Beamsync
and returns a descriptive FeatureError if either is disabled.
- Minimum iOS Version: 15 (same as Enrolment SDK)
- Required permissions on Info.plist (brought transitively by the Ultralight provider dependency):
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Bluetooth is used to communicate with nearby UltraLight devices</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Location is required to detect nearby Bluetooth devices.</string>
How to Import¶
Ultralight is exposed through the Enrolment SDK integration flow.
Add the Ultralight provider dependency to your app's build.gradle:
dependencies {
implementation "com.amadeus.mdi.mob.sdk:ama-ultralight:<1.0.0>"
// ... other dependencies
}
Ultralight is distributed for iOS via Swift Package Manager (SPM).
Install using Xcode
- Open your project in Xcode
- Go to File ▸ Add Packages…
- Enter the package repository URL:
https://github.com/vbmobile/AmaShareUltralight
- Select the desired version (recommended: exact or up to next major)
- Add the AmaShareUltralight product to your app target
Install using Package.swift
If you are managing dependencies manually, add Ultralight to your Package.swift:
dependencies: [
.package(
url: "https://github.com/vbmobile/AmaShareUltralight",
exact: "1.0.0-rc24"
)
],
Replace
`1.0.0-rc24is the pretended version.
Then include it in your target dependencies:
.target(
name: "YourAppTarget",
dependencies: [
.product(name: "AmaShareUltralight", package: "AmaShareUltralight")
]
)
Replace
YourAppTargetwith the intended app target you wish to use.
Once added, Ultralight APIs are available to your application through the Enrolment SDK integration flow.
Configure Ultralight in Enrolment Initialization¶
Step 1: Create and Initialize UltralightProvider¶
Before initializing Enrolment, create and configure your UltralightProvider instance:
private fun initializeUltralight(): UltralightProvider? {
val ultralightApiKey = "<your-ultralight-api-key>"
// Initialize UltralightSdk
UltralightSdk.initialize(context = requireContext())
UltralightSdk.getInstance().initialiseBeamSync(ultralightApiKey)
return UltralightSdk.getInstance()
}
If you don't want to enable Ultralight, simply return null:
private fun initializeUltralight(): UltralightProvider? {
val enabled = false // Your configuration flag
if (!enabled) return null
// ... initialization code
}
func ultralightProvider() -> UltralightProtocol? {
let ultralightProvider: AMAShareUltralight.Ultralight = .init()
ultralightProvider.initialiseBeamSync(apiKey: "<your-ultralight-api-key>")
return ultralightProvider
}
Step 2: Pass Provider to Enrolment Initialization¶
Pass the UltralightProvider to Enrolment.initialize():
val ultralightProvider = initializeUltralight()
Enrolment.initialize(
context = requireContext().applicationContext,
enrolmentConfig = enrolmentConfig,
enrolmentCustomViews = enrolmentCustomViews,
documentReaderProvider = documentReaderProvider,
ultralightProvider = ultralightProvider, // Pass the provider
rfidReaderProvider = null,
enrolmentInitializerCallback = callback
)
func initializeEnrolment(provider: UltralightProtocol?) async -> EnrolmentProtocol {
let ultralightProvider = provider ?? ultralightProvider()
return await withCheckedContinuation { continuation in
Enrolment.shared.initWith(enrolmentConfig: nil,
documentScanProvider: nil,
documentRFIDProvider: nil,
ultralightProvider: ultralightProvider,
viewRegister: nil,
completionHandler: { result in
switch result {
case .success:
continuation.resume(returning: Enrolment.shared)
case let .failure(featureError):
print(featureError.description)
continuation.resume(returning: Enrolment.shared)
}
})
}
Share Passenger Data¶
The share() method sets the passenger list and starts sharing in a single call.
It returns a Pair<Boolean, FeatureError>
Passenger model:
| Field | Type | Description |
|---|---|---|
language |
String |
Language code (e.g., "en", "fr") |
mrz |
String |
MRZ string (\n separating lines) |
boardingPasses |
List<String> |
Raw BCBP barcode strings |
docPhotoBase64 |
String |
Base64-encoded document holder photo |
selfieBase64 |
String |
Base64-encoded selfie |
ePassport |
Boolean |
Whether the document is an e-Passport |
eBagTagId |
String? |
Optional electronic bag tag ID |
tag |
String? |
Optional custom tag |
val passengers = listOf(
Passenger(
language = "en",
mrz = "<mrz-line-1>\n<mrz-line-2>",
boardingPasses = listOf("<bcbp-barcode-string>"),
docPhotoBase64 = "<base64-encoded-document-photo>",
selfieBase64 = "<base64-encoded-selfie>",
ePassport = true,
eBagTagId = null,
tag = null
)
)
val (success, featureError) = Enrolment.getInstance().share(passengers)
if (success) {
// Passengers set and Beamsync started successfully
} else {
// Check featureError.description for details
Log.e("Ultralight", "Error: ${featureError.description}")
}
func sampleShare(enrolment: EnrolmentProtocol?) async {
let passenger = Passenger(language: "en",
mrz: "<mrz-line-1>\n<mrz-line-2>",
boardingPasses: ["<bcbp-barcode-string>"],
docPhotoBase64: "<base64-encoded-document-photo>",
selfieBase64: "<base64-encoded-selfie>",
ePassport: true,
tag: nil,
ebagtagId: nil)
guard let shareResult = await enrolment?.share(passengers: [passenger]) else {
print("Precondition failed: nil shareResult")
return
}
if shareResult.result ?? false {
// Passengers set and Beamsync started successfully
} else {
// Check featureError.description for details
print(shareResult.error ?? "")
}
}
Stop Beamsync¶
Stop Beamsync when the flow ends (for example, when leaving the screen or destroying the view):
Enrolment.getInstance().stopSharing()
It's recommended to call stopSharing() in your fragment/activity lifecycle:
override fun onDestroyView() {
super.onDestroyView()
Enrolment.getInstance().stopSharing()
}
It's recommended to call stopSharing() in your view lifecycle:
deinit {
presenter?.shouldStopSharing() // MVP Design Pattern
}
or call it ha hoc
enrolment?.stopSharing()
Complete Example¶
Here's a complete example integrating Ultralight with the Enrolment SDK:
class UltralightFragment : Fragment() {
// Initialize Ultralight provider
private fun initializeUltralight(): UltralightProvider? {
val ultralightApiKey = "<your-api-key>"
UltralightSdk.initialize(context = requireContext())
UltralightSdk.getInstance().initialiseBeamSync(ultralightApiKey)
return UltralightSdk.getInstance()
}
// Initialize Enrolment with Ultralight
private fun initializeEnrolment() {
val ultralightProvider = initializeUltralight()
Enrolment.initialize(
context = requireContext().applicationContext,
enrolmentConfig = enrolmentConfig,
enrolmentCustomViews = enrolmentCustomViews,
documentReaderProvider = documentReaderProvider,
ultralightProvider = ultralightProvider,
rfidReaderProvider = null,
enrolmentInitializerCallback = object : EnrolmentInitializerCallback {
override fun onEnrolmentInitialized() {
Log.d(TAG, "Enrolment initialized successfully")
}
override fun onEnrolmentInitializationError(error: FeatureError) {
Log.e(TAG, "Initialization error: ${error.description}")
}
}
)
}
// Prepare and share passenger data
private fun prepareAndSharePassenger(): Pair<Boolean, String?> {
try {
val idDocument = EnrolmentData.documentReaderReport?.idDocument
?: return false to "No ID document"
val boardingPass = EnrolmentData.boardingPass
val selfieBitmap = readPhotoFromInternalStorage(
requireContext(),
EnrolmentData.faceCapturePhotoFilename ?: ""
)
// Build passenger from collected enrolment data
val passenger = Passenger(
language = "en",
mrz = idDocument.mrz,
boardingPasses = listOf(boardingPass.rawBoardingPass),
docPhotoBase64 = documentPhoto,
selfieBase64 = selfiePhoto,
ePassport = idDocument.isElectronic
)
val (success, featureError) = Enrolment.getInstance()
.share(listOf(passenger))
return if (success) {
true to null
} else {
false to featureError.description.ifBlank {
featureError.publicMessage.ifBlank { "Unknown error" }
}
}
} catch (e: Exception) {
Log.e(TAG, "Error preparing passenger", e)
return false to e.message
}
}
// Stop Beamsync
private fun stopBeamsync() {
Enrolment.getInstance().stopSharing()
Log.d(TAG, "Beamsync stopped")
}
override fun onDestroyView() {
super.onDestroyView()
Enrolment.getInstance().stopSharing()
}
}
Notes¶
- The
UltralightProvidermust be initialized before passing it toEnrolment.initialize() - Ultralight is not available in offline mode (
initializeOffline) share()is a blocking call — always invoke it from a background thread -share()both sets the passenger data and starts Beamsync (there is no separatestartSharing()step)- The SDK performs pre-flight checks for Bluetooth and Location before starting Beamsync
- Always call
stopSharing()when cleaning up (e.g., inonDestroyView())
import AMADocModeliOS
import AMAShareUltralight
import MobileIdSDKiOS
class UltralightProviderSample {
func ultralightProvider() -> UltralightProtocol? {
let ultralightProvider: AMAShareUltralight.Ultralight = .init()
ultralightProvider.initialiseBeamSync(apiKey: "<your-ultralight-api-key>")
return ultralightProvider
}
func initializeEnrolment(provider: UltralightProtocol?) async -> EnrolmentProtocol {
let ultralightProvider = provider ?? ultralightProvider()
return await withCheckedContinuation { continuation in
Enrolment.shared.initWith(enrolmentConfig: nil,
documentScanProvider: nil,
documentRFIDProvider: nil,
ultralightProvider: ultralightProvider,
viewRegister: nil,
completionHandler: { result in
switch result {
case .success:
continuation.resume(returning: Enrolment.shared)
case let .failure(featureError):
print(featureError.description)
continuation.resume(returning: Enrolment.shared)
}
})
}
}
func sampleShare(enrolment: EnrolmentProtocol?) async {
let passenger = Passenger(language: "en",
mrz: "<mrz-line-1>\n<mrz-line-2>",
boardingPasses: ["<bcbp-barcode-string>"],
docPhotoBase64: "<base64-encoded-document-photo>",
selfieBase64: "<base64-encoded-selfie>",
ePassport: true,
tag: nil,
ebagtagId: nil)
guard let shareResult = await enrolment?.share(passengers: [passenger]) else {
print("Precondition failed: nil shareResult")
return
}
if shareResult.result ?? false {
// Passengers set and Beamsync started successfully
} else {
// Check featureError.description for details
print(shareResult.error ?? "")
}
}
func stopSharing(enrolment: EnrolmentProtocol?) {
enrolment?.stopSharing()
}
func prepareAndSharePassenger(enrolment: EnrolmentProtocol?) {
guard EnrolmentData.idDocument != nil else {
print("Precondition failed: Have not read document")
return
}
guard let faceCapture = EnrolmentData.biometricFaceCaptureReport?.photo else {
print("Precondition failed: Have not read face")
return
}
guard EnrolmentData.boardingPass != nil else {
print("Precondition failed: Have not read boarding pass")
return
}
guard let idDocument = EnrolmentData.idDocument else {
print("Precondition failed: idDocument missing")
return
}
let boardPass = EnrolmentData.boardingPass?.raw ?? ""
let passenger = idDocument.mapToPassenger(faceCapture: faceCapture, boardingPasses: [boardPass])
Task {
guard let shareResult = await enrolment?.share(passengers: [passenger]) else {
print("Precondition failed: nil shareResult")
return
}
if shareResult.result ?? false {
// Passengers set and Beamsync started successfully
} else {
// Check featureError.description for details
print(shareResult.error ?? "")
}
}
}
}
Notes¶
- The
UltralightProvidermust be initialized before passing it toEnrolment.initialize() - Ultralight is not available in offline mode (
initializeOffline) share()both sets the passenger data and starts Beamsync (there is no separatestartSharing()step)- The SDK performs pre-flight checks for Bluetooth and Location before starting Beamsync
- Always call
stopSharing()when cleaning up (e.g., indeinit())