A Swift framework for parsing airline boarding pass barcodes and QR codes that conform to the IATA Bar Coded Boarding Pass (BCBP) standard.
Compliance: IATA Resolution 792 - BCBP Version 8 (Effective June 1, 2020) β
- BoardingPassKit
- π« Parse IATA BCBP compliant boarding pass barcodes and QR codes
- π± Generate QR codes from boarding pass data (iOS only)
- π Comprehensive debugging and logging capabilities
- π Support for single and multi-leg itineraries
- π·οΈ Extract bag tags, frequent flyer information, and security data
- βοΈ Configurable data processing options (trim whitespace, remove leading zeros, convert empty strings to nil)
- π― Built-in demo data for testing and development
- π¦ Swift Package Manager and CocoaPods support
- iOS 15.0+ / macOS 10.15+
- Swift 5.7+
- Xcode 14.0+
Add BoardingPassKit to your project using Swift Package Manager:
- In Xcode, go to File β Add Package Dependencies
- Enter the repository URL:
https://github.com/anomaddev/BoardingPassKit.git - Select the version and add to your target
Or add it to your Package.swift:
dependencies: [
.package(url: "https://github.com/anomaddev/BoardingPassKit.git", from: "2.1.2")
]Add BoardingPassKit to your project using CocoaPods:
- Add this line to your
Podfile:
pod 'BoardingPassKit'- Run
pod installin your terminal - Use the
.xcworkspacefile that was created
Or add it directly to your Podfile:
target 'YourAppTarget' do
pod 'BoardingPassKit'
endimport BoardingPassKit
// Using a barcode string
let barcodeString = "M1ACKERMANN/JUSTIN DAVEJKLEAJ MSYPHXAA 2819 014S008F0059 14A>318 0014BAA 00000000000002900174844256573 AA AA 76UXK84 223"
do {
let decoder = BoardingPassDecoder()
let boardingPass = try decoder.decode(code: barcodeString)
print("Passenger: \(boardingPass.passengerName)")
print("Flight: \(boardingPass.boardingPassLegs.first?.flightno ?? "N/A")")
print("From: \(boardingPass.boardingPassLegs.first?.origin ?? "N/A")")
print("To: \(boardingPass.boardingPassLegs.first?.destination ?? "N/A")")
} catch {
print("Error decoding boarding pass: \(error)")
}The main class for decoding boarding pass barcodes.
let decoder = BoardingPassDecoder()
// Decode from string
let boardingPass = try decoder.decode(code: barcodeString)
// Decode from Data
let boardingPass = try decoder.decode(barcodeData)The BoardingPassDecoder provides several configuration options to customize parsing behavior:
let decoder = BoardingPassDecoder()
// Configuration options (all default to true)
decoder.debug = true // Enable detailed console logging during parsing
decoder.trimLeadingZeroes = true // Remove leading zeros from numeric fields
decoder.trimWhitespace = true // Remove whitespace from string fields
decoder.emptyStringIsNil = true // Convert empty strings to nil for optional fieldsDefault behavior examples:
// trimLeadingZeroes = true (default)
// Flight number "00234" becomes "234"
// Seat number "005A" becomes "5A"
// trimWhitespace = true (default)
// Field " ABC " becomes "ABC"
// emptyStringIsNil = true (default)
// Optional field with "" becomes nil
// Bag tags with empty strings are filtered outComparison with disabled settings:
let decoder = BoardingPassDecoder()
decoder.trimLeadingZeroes = false
decoder.trimWhitespace = false
decoder.emptyStringIsNil = false
// Results in:
// Flight number "00234" remains "00234"
// Field " ABC " remains " ABC "
// Optional field with "" remains ""
// All bag tags preserved: ["ABC123", "", "DEF456"]For most use cases, keep the default settings:
let decoder = BoardingPassDecoder()
// All defaults are optimal for typical boarding pass parsingFor data analysis or debugging, you might want to preserve original formatting:
let decoder = BoardingPassDecoder()
decoder.trimWhitespace = false
decoder.trimLeadingZeroes = false
decoder.emptyStringIsNil = false
// Preserves original field formatting for analysisFor user-facing applications, use defaults to ensure clean data:
let decoder = BoardingPassDecoder()
// Default settings ensure clean, user-friendly data
// Empty strings become nil (easier to handle in UI)
// Whitespace is trimmed (cleaner display)
// Leading zeros are removed (more readable numbers)The main structure representing a decoded boarding pass.
public struct BoardingPass: Codable {
/// The IATA BCBP format (M or S)
public var format: String
/// Number of legs in the boarding pass
public var numberOfLegs: Int
/// Passenger name (20 characters)
public var passengerName: String
/// Electronic ticket indicator (E or blank)
public var ticketIndicator: String
/// Array of flight legs
public var boardingPassLegs: [BoardingPassLeg]
/// Boarding pass information
public var passInfo: BoardingPassInfo
/// Security data (if present)
public var securityData: BoardingPassSecurityData?
/// Airline-specific blob data
public var airlineBlob: String?
/// Original barcode string
public let code: String
}Represents a single flight segment.
public struct BoardingPassLeg: Codable {
/// Leg index (0 for first leg)
public let legIndex: Int
/// Record locator (PNR code)
public let pnrCode: String
/// Origin airport (IATA code)
public let origin: String
/// Destination airport (IATA code)
public let destination: String
/// Operating carrier (IATA code)
public let operatingCarrier: String
/// Flight number
public let flightno: String
/// Julian date of flight
public let julianDate: Int
/// Compartment code (Y, C, F, etc.)
public let compartment: String
/// Seat number
public let seatno: String
/// Check-in sequence number
public let checkIn: Int
/// Passenger status
public let passengerStatus: String
/// Size of conditional data
public let conditionalSize: Int
/// Conditional data for this leg
public var conditionalData: BoardingPassLegData?
}Contains boarding pass metadata and bag tags.
public struct BoardingPassInfo: Codable {
/// IATA BCBP version
let version: String
/// Passenger description/gender code
var passengerDescription: String?
/// Check-in source
var checkInSource: String?
/// Pass issuance source
var passSource: String?
/// Issue date
var issueDate: String?
/// Document type
var documentType: String?
/// Issuing airline (IATA code)
let issuingAirline: String
/// Bag tags
var bagTags: [String]
}Contains conditional data for a specific leg.
public struct BoardingPassLegData: Codable {
/// Segment size
public let segmentSize: Int
/// Airline code
public var airlineCode: String
/// Ticket number
public var ticketNumber: String
/// Security selectee flag
public var selectee: String
/// International documentation indicator
public var internationalDoc: String
/// Ticketing carrier
public var ticketingCarrier: String
/// Frequent flyer airline
public var ffAirline: String
/// Frequent flyer number
public var ffNumber: String
/// ID/AD indicator
public var idAdIndicator: String?
/// Free baggage allowance
public var freeBags: String?
/// Fast track indicator
public var fastTrack: String?
/// Airline-specific data
public var airlineUse: String?
}Generate QR codes from boarding pass data:
#if os(iOS)
do {
let qrCodeImage = try boardingPass.qrCode()
// Display the QR code image
} catch {
print("Failed to generate QR code: \(error)")
}
#endifThe framework includes built-in demo data for testing:
// Simple single-leg boarding pass
let simplePass = try decoder.decode(code: BoardingPass.DemoData.Simple.string)
// Historical boarding pass example
let historicalPass = try decoder.decode(code: BoardingPass.DemoData.Historical.string)
// Multi-leg boarding pass
let multiLegPass = try decoder.decode(code: BoardingPass.DemoData.MultiLeg.string)Enable detailed logging to see the parsing process:
let decoder = BoardingPassDecoder()
decoder.debug = true
let boardingPass = try decoder.decode(code: barcodeString)
boardingPass.printout() // Prints detailed information to consoleThe framework provides comprehensive error handling:
do {
let boardingPass = try decoder.decode(code: barcodeString)
} catch BoardingPassError.InvalidPassFormat(let format) {
print("Invalid format: \(format)")
} catch BoardingPassError.DataIsNotBoardingPass(let error) {
print("Not a valid boarding pass: \(error)")
} catch BoardingPassError.MandatoryItemNotFound(let index) {
print("Missing mandatory field at index: \(index)")
} catch {
print("Other error: \(error)")
}The framework fully supports multi-leg itineraries:
let boardingPass = try decoder.decode(code: multiLegBarcodeString)
print("Number of legs: \(boardingPass.numberOfLegs)")
for leg in boardingPass.boardingPassLegs {
print("Leg \(leg.legIndex): \(leg.origin) β \(leg.destination)")
print("Flight: \(leg.operatingCarrier)\(leg.flightno)")
print("Seat: \(leg.seatno)")
}Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
Justin Ackermann
Version 2.x includes critical parsing fixes and significant improvements over v1.x. If you're upgrading from v1.x, please read this section carefully.
Version 2.0 is a major release with critical bug fixes. While we've maintained API compatibility where possible, the internal parsing logic has been significantly improved to fix critical issues with multi-leg boarding passes and conditional data parsing.
1. Critical Parsing Bug Fixes
- Multi-leg boarding passes: v1.x incorrectly parsed subsequent flight segments using mandatory parsing instead of conditional parsing, causing failures on multi-leg itineraries
- Check-in sequence numbers: Fixed incorrect parsing logic that could cause data misalignment
- Conditional data handling: Improved parsing of optional fields to match IATA Resolution 792 specification
2. IATA Resolution 792 Version 8 Compliance
- Added support for new gender codes: "X" (Unspecified) and "U" (Undisclosed)
- Updated passenger description field to handle Version 8 specifications
- See
IATA_COMPLIANCE.mdfor full compliance details
3. New Configuration Options
The BoardingPassDecoder now includes three configuration properties for better control over data processing:
// NEW in v2.x: These properties are now available (all default to true)
decoder.trimLeadingZeroes = true // Remove leading zeros from numeric fields
decoder.trimWhitespace = true // Remove whitespace from string fields
decoder.emptyStringIsNil = true // Convert empty strings to nil for optional fields4. Enhanced Error Handling
- More descriptive error messages for parsing failures
- Better validation of conditional data boundaries
- Improved debugging output
While the public API remains largely compatible, the parsing behavior has changed:
- Multi-leg boarding passes: v2.x correctly parses multi-leg itineraries that may have failed or parsed incorrectly in v1.x
- Field values: With default settings, fields are now trimmed and cleaned (leading zeros removed, whitespace trimmed, empty strings converted to nil)
- Data validation: Stricter validation may cause some malformed boarding passes that parsed in v1.x to now throw errors (this is correct behavior)
If you only parse single-leg (M1 format) boarding passes, your code should work with minimal changes:
// v1.x code
let decoder = BoardingPassDecoder()
let boardingPass = try decoder.decode(code: barcodeString)
// v2.x code - same API, improved parsing
let decoder = BoardingPassDecoder()
let boardingPass = try decoder.decode(code: barcodeString)Expected differences:
- Numeric fields will have leading zeros removed (
"00234"β"234") - String fields will have whitespace trimmed
- Empty optional fields will be
nilinstead of""
IMPORTANT: If you use multi-leg boarding passes, v2.x fixes critical bugs that likely caused parsing failures in v1.x.
// v2.x correctly parses multi-leg boarding passes
let decoder = BoardingPassDecoder()
let boardingPass = try decoder.decode(code: multiLegBarcodeString)
// Now you can reliably access all legs
for leg in boardingPass.boardingPassLegs {
print("Leg \(leg.legIndex): \(leg.origin) β \(leg.destination)")
}If you need to preserve the exact field formatting from v1.x (not recommended for new code):
let decoder = BoardingPassDecoder()
// Disable data cleaning features (not recommended)
decoder.trimLeadingZeroes = false
decoder.trimWhitespace = false
decoder.emptyStringIsNil = false
// This will produce output closer to v1.x behavior
// WARNING: This does NOT restore v1.x parsing bugsNote: Even with these settings disabled, v2.x uses the corrected parsing logic. You cannot restore v1.x's parsing bugs.
- Correct Multi-Leg Parsing: Properly handles M2, M3, M4 format boarding passes
- IATA Compliance: Full compliance with IATA Resolution 792 Version 8
- Better Data Quality: Automatic cleaning of leading zeros and whitespace
- Cleaner Optional Fields: Empty strings converted to
nilfor better Swift integration - Enhanced Debugging: Improved logging and error messages
- Configurable Processing: Choose how data should be cleaned
| Feature | v1.0.x | v2.0.x | v2.1.x (Latest) |
|---|---|---|---|
| Single-leg parsing | β | β | β |
| Multi-leg parsing | β Fixed | β | |
| IATA Version 8 compliance | β | β | β |
| Trim leading zeros | β | β (default) | β (default) |
| Trim whitespace | β | β (default) | β (default) |
| Empty string to nil | β | β (default) | β (default) |
| Configurable options | β | β | β |
| "0000" field parsing | β Fixed in v2.1.2 | ||
| Conditional data parsing | β Fixed | β |
- Fixed critical crash with Variable Size field of 0: Resolved
EXC_BREAKPOINTerror when processing boarding passes with no conditional data - Fixed "0000" integer parsing issue: Resolved bug where fields containing "0000" failed to parse
- Full backward compatibility with v2.1.1
- Fixed "0000" integer parsing issue: Resolved bug where fields containing "0000" failed to parse
- Full backward compatibility with v2.1.0
- Enhanced configuration options
- Improved data processing and validation
- Better debugging capabilities
- CocoaPods naming update (BoardingPassKit β BoardingPassParser for pods)
- No functional changes
- Patch release with improved configuration options
- Critical parsing fixes for multi-leg boarding passes
- IATA Resolution 792 Version 8 compliance
- New configuration options (trimLeadingZeroes, trimWhitespace, emptyStringIsNil)
- Enhanced error handling and validation
- Initial IATA BCBP implementation
β οΈ Known issues with multi-leg boarding passesβ οΈ Incorrect conditional data parsing
We strongly recommend upgrading to v2.1.2 for all users:
- Critical bug fixes for multi-leg boarding passes
- Full IATA Resolution 792 Version 8 compliance
- Better data quality and error handling
- Minimal migration effort for most use cases
- IATA Resolution 792 - BCBP Version 8 specification
- SwiftDate library for date handling
- Flight Historian by Paul Bogard for boarding pass parsing inspiration