Skip to content
All essays
iOSMarch 27, 202511 min

Swift PassKit: Apple Wallet Integration

Integrate Apple Wallet with PassKit. Create passes, tickets, and payment cards.

Ü
Ümit Uz
Mobile & Full Stack Developer

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

  1. 1Design: Follow Apple's design guidelines
  2. 2Security: Secure pass generation
  3. 3Updates: Implement pass update service
  4. 4Testing: Test on real devices
  5. 5Barcodes: Use appropriate barcode formats
  6. 6Localization: Support multiple languages
  7. 7Error Handling: Handle errors gracefully
  8. 8UX: Provide clear instructions

PassKit enables seamless wallet integration!

Related essays

Next essay
Swift HomeKit: Smart Home Integration