PassKit enables you to create passes and integrate with Apple Wallet. Learn to add loyalty cards, tickets, and more.
Pass Types
Pass Structure
swift
import PassKit
enum PassType {
case boardingPass
case coupon
case eventTicket
case generic
case storeCard
}Creating Passes
Pass Configuration
swift
// pass.json (Pass JSON configuration)
{
"formatVersion": 1,
"passTypeIdentifier": "pass.com.yourapp.boarding",
"serialNumber": "123456",
"teamIdentifier": "YOUR_TEAM_ID",
"webServiceURL": "https://yourapp.com/passes",
"authenticationToken": "your_auth_token",
"backgroundColor": "rgb(255,255,255)",
"logoText": "Your Airline",
"description": "Boarding Pass",
"organizationName": "Your Airline",
"foregroundColor": "rgb(0,0,0)",
"labelColor": "rgb(255,255,255)",
"boardingPass": {
"primaryFields": [
{
"key": "origin",
"label": "San Francisco",
"value": "SFO"
},
{
"key": "destination",
"label": "New York",
"value": "JFK"
}
],
"secondaryFields": [
{
"key": "flight",
"label": "Flight",
"value": "123"
},
{
"key": "date",
"label": "Date",
"value": "2025-03-27"
}
],
"backFields": [
{
"key": "passenger",
"label": "Passenger",
"value": "John Doe"
},
{
"key": "seat",
"label": "Seat",
"value": "12A"
}
],
"transitType": "PKTransitTypeAir"
},
"barcode": {
"format": "PKBarcodeFormatQR",
"message": "FLIGHT123*SFOJFK*2025-03-27",
"messageEncoding": "iso-8859-1"
}
}Generate Pass
Pass Creation
swift
import Foundation
class PassGenerator {
func generatePass() throws -> Data {
// Create pass.json
let passJSON = """
{
"formatVersion": 1,
"passTypeIdentifier": "pass.com.yourapp.generic",
"serialNumber": "\(UUID().uuidString)",
"teamIdentifier": "YOUR_TEAM_ID",
"organizationName": "Your Company",
"description": "Loyalty Card",
"logoText": "Your Brand",
"foregroundColor": "rgb(0,0,0)",
"backgroundColor": "rgb(255,255,255)",
"generic": {
"primaryFields": [
{
"key": "name",
"label": "Name",
"value": "John Doe"
}
],
"secondaryFields": [
{
"key": "balance",
"label": "Balance",
"value": "100 points"
}
]
},
"barcode": {
"format": "PKBarcodeFormatQR",
"message": "MEMBER123",
"messageEncoding": "iso-8859-1"
}
}
"""
let passData = passJSON.data(using: .utf8)!
// Sign pass (requires certificate)
let signedPass = try signPass(passData: passData)
// Create .pkpass file
return createPKPass(signedPass: signedPass)
}
private func signPass(passData: Data) throws -> Data {
// Load certificate
let certificateURL = Bundle.main.url(forResource: "passCertificate", withExtension: "pem")!
let certificate = try String(contentsOf: certificateURL, encoding: .utf8)
// Create manifest
let manifest = [
"pass.json": base64Encode(data: passData)
]
let manifestData = try JSONSerialization.data(withJSONObject: manifest)
let manifestSignature = signManifest(manifest: manifestData)
// Create signature
let signature = base64Encode(data: manifestSignature)
let passDictionary: [String: Any] = [
"manifest": manifest,
"signature": signature
]
return try JSONSerialization.data(withJSONObject: passDictionary)
}
private func createPKPass(signedPass: Data) -> Data {
// Create .pkpass archive
return signedPass
}
private func base64Encode(data: Data) -> String {
return data.base64EncodedString()
}
private func signManifest(manifest: Data) -> Data {
// Sign manifest with private key
return manifest // Implementation depends on your crypto setup
}
}Add Pass to Wallet
PassKit Integration
swift
import PassKit
struct AddPassView: View {
@State private var showAddPassSheet = false
@State private var passURL: URL?
var body: some View {
VStack {
Button("Add to Wallet") {
addPassToWallet()
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.sheet(isPresented: $showAddPassSheet) {
if let url = passURL {
AddPassView(url: url)
}
}
}
func addPassToWallet() {
// Generate pass and create URL
guard let passURL = generatePassURL() else {
return
}
self.passURL = passURL
showAddPassSheet = true
}
func generatePassURL() -> URL? {
// Generate pass and serve from your server
// or embed in app
return URL(string: "https://yourapp.com/pass.pkpass")
}
}
struct AddPassView: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context: Context) -> PKAddPassesViewController {
return PKAddPassesViewController(passURL: url)
}
func updateUIViewController(_ uiViewController: PKAddPassesViewController, context: Context) {}
}Apple Pay
Payment Integration
swift
import PassKit
struct ApplePayView: View {
@State private var isProcessingPayment = false
var body: some View {
VStack {
Button("Pay with Apple Pay") {
processApplePay()
}
.disabled(isProcessingPayment)
.padding()
.background(Color.black)
.foregroundColor(.white)
.cornerRadius(10)
}
}
func processApplePay() {
let request = PKPaymentRequest()
request.merchantIdentifier = "merchant.com.yourapp"
request.supportedNetworks = [.visa, .masterCard, .amex]
request.countryCode = "US"
request.currencyCode = "USD"
request.paymentSummaryItems = [
PKPaymentSummaryItem(
label: "Product",
amount: NSDecimalNumber(value: 19.99),
type: .final
)
]
if let controller = PKPaymentAuthorizationController(paymentRequest: request) {
controller.delegate = self
controller.present(completion: nil)
}
}
}
extension ApplePayView: PKPaymentAuthorizationControllerDelegate {
func paymentAuthorizationController(
_ controller: PKPaymentAuthorizationController,
didAuthorizePayment payment: PKPayment,
completion: @escaping (PKPaymentAuthorizationStatus) -> Void
) {
// Process payment
completion(.success)
}
func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
controller.dismiss(completion: nil)
}
}Pass Updates
Update Pass Content
swift
class PassUpdateService {
func updatePass(serialNumber: String, newBalance: Int) async throws {
let url = URL(string: "https://yourapp.com/passes/\(serialNumber)")!
var request = URLRequest(url: url)
request.httpMethod = "PUT"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let updateData: [String: Any] = [
"balance": newBalance
]
request.httpBody = try JSONSerialization.data(withJSONObject: updateData)
let (_, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw PassUpdateError.updateFailed
}
}
enum PassUpdateError: Error {
case updateFailed
}
}Pass Sharing
Share Passes
swift
import UIKit
class PassSharing {
func sharePass(passFile: URL) {
let activityViewController = UIActivityViewController(
activityItems: [passFile],
applicationActivities: nil
)
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let rootViewController = windowScene.windows.first?.rootViewController {
rootViewController.present(activityViewController, animated: true)
}
}
func sharePassViaEmail(passFile: URL) {
let subject = "Your Boarding Pass"
let body = "Attached is your boarding pass for your upcoming flight."
if let mailToString = "mailto:?subject=\(subject)&body=\(body)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let mailURL = URL(string: mailToString) {
UIApplication.shared.open(mailURL)
}
}
}Best Practices
- 1Design: Follow Apple's design guidelines
- 2Security: Secure pass generation
- 3Updates: Implement pass update service
- 4Testing: Test on real devices
- 5Barcodes: Use appropriate barcode formats
- 6Localization: Support multiple languages
- 7Error Handling: Handle errors gracefully
- 8UX: Provide clear instructions
PassKit enables seamless wallet integration!