Swift Library

Last updated: 2026-05-19

Swift Library

The Swift EPAS Client Library is a native Apple wrapper for ECR applications that communicate directly with Westpay payment terminals over TCP and EPAS XML. The public API is intentionally aligned with the .NET and Java libraries, while adapting to Swift naming conventions, async or await, and Swift error handling.

Requirements

ItemValue
DistributionSwift Package Manager
ProductEpasClient
Main clientEpasClient
Supported platformsmacOS and iPadOS
Callback protocolEpasClientDelegate

Package Setup

Add the package to Xcode and import EpasClient where needed.

swift
import EpasClient

Minimal Setup

swift
import EpasClient

@MainActor
final class EcrApplication: EpasClientDelegate {
    lazy var epas = EpasClient(delegate: self)

    func display(text: String) {
        print(text)
    }

    func printReceipt(receipt: ReceiptData) {
        print(receipt.generateSimpleReceipt(width: 32))
    }

    func printReport(report: String) {
        print(report)
    }

    func verifySignature(displayText: String) async -> BooleanCallbackResult? {
        display(text: displayText)
        return BooleanCallbackResult(handled: true, value: true)
    }

    func log(level: LogLevel, text: String) {
        print("[\(level)] \(text)")
    }
}

Implement the remaining delegate methods that your ECR needs for voice referral, payment code, VAT, loyalty, DCC, parameter download, and terminal status handling.

Create the Client

Use one EpasClient instance per terminal connection.

swift
let clientApp = EcrApplication()
let client = EpasClient(delegate: clientApp)

Connect and Login

swift
let connection = TerminalNetworkConnection(
    terminalConnectionDetails: IPEndPoint("192.168.10.25", 6001),
    useKeepAlive: true
)

try await client.connect(connection)

let environment = TerminalEnvironment(
    terminalId: "80000082",
    storeId: "store_1",
    workstationId: "pos_1",
    configurationServer: IPEndPoint("185.27.171.42", 55133),
    authorisationServer: IPEndPoint("185.27.171.42", 55144),
    operatorId: "cashier_1",
    iso639_1_LanguageCode: TerminalEnvironment.langSwedish
)

let login = try await client.login(environment)
guard login.loggedIn else {
    throw EpasClientError.invalidState("Terminal login was rejected.")
}

connect(_:) and operational methods throw when communication or validation fails. TransactionResponse, LoginResponse, and other response models still carry the business outcome from the terminal.

Supported API Surface

The current Swift library supports the main EPAS workflows:

  • connect(_:)
  • disconnect()
  • login(_:)
  • logout()
  • purchase(_ request: PurchaseRequest)
  • purchase(_ transactionAmounts: PurchaseTransactionAmounts)
  • purchase(_ purchaseAmount: Decimal)
  • purchaseBeforeAmount()
  • sendPurchaseAmounts(_:)
  • refund(_ request: RefundRequest)
  • refund(_ amount: Decimal)
  • reverseLastTransaction(_:transactionTerminalId:)
  • cashAdvance(_:)
  • getLastTransaction(_:)
  • enableCardReaders(_:_:)
  • requestAdministrativeOperation(_:)
  • sendRawXml(_:)
  • abort()
  • status()

Purchase Examples

Simple Purchase

swift
let response = try await client.purchase(100.00)

Purchase with Cashback

swift
let response = try await client.purchase(
    PurchaseTransactionAmounts(100.00, cashbackAmount: 20.00)
)

Purchase with Full Request

swift
let request = PurchaseRequest(
    amount: 100.00,
    cashbackAmount: 0.00,
    paymentMethod: "Swish",
    terminalId: nil,
    merchantCategoryCode: 5411
)

let response = try await client.purchase(request)

Pre-Amount Purchase

swift
Task {
    let response = try await client.purchaseBeforeAmount()
    handleTransactionResponse(response)
}

let sendResult = try await client.sendPurchaseAmounts(
    PurchaseTransactionAmounts(100.00, cashbackAmount: 0.00)
)

Other Transaction Flows

swift
let refund = try await client.refund(45.00)
let reversal = try await client.reverseLastTransaction("1234567890")
let cashAdvance = try await client.cashAdvance(200.00)
let last = try await client.getLastTransaction("80000082")

Administrative and Diagnostic APIs

swift
let readers = try await client.enableCardReaders(
    true,
    EnableCardReaderOptions(
        notifyOnCancel: true,
        transactionType: .payment
    )
)

let admin = try await client.requestAdministrativeOperation(.printConfiguration)

let xmlResult = try await client.sendRawXml("""
<?xml version="1.0" encoding="utf-8"?>
<SaleToPOIRequest>
  ...
</SaleToPOIRequest>
""")

Delegate Callbacks

The Swift SDK routes terminal activity through EpasClientDelegate.

Display, receipt, report, and log

swift
func display(text: String)
func printReceipt(receipt: ReceiptData)
func printReport(report: String)
func log(level: LogLevel, text: String)

Cashier decision callbacks

swift
func verifySignature(displayText: String) async -> BooleanCallbackResult?
func forceFallback(displayText: String) async -> BooleanCallbackResult?
func handleVoiceReferral(displayText: String) async -> TextCallbackResult?
func paymentCodeRequired(displayText: String) async -> TextCallbackResult?
func vatAmountRequired(displayText: String) async -> DecimalCallbackResult?
func loyaltyCardPresented(cardNumber: String) async -> BooleanCallbackResult?
func checkDccOnOriginalTransaction(displayText: String) async -> BooleanCallbackResult?
func parameterDownloadAvailable(displayText: String) async -> Bool

Status and diagnostics

swift
func linkStatusChanged(isConnected: Bool)
func busyStatusChanged(isBusy: Bool)
func cardStatusChanged(isInserted: Bool)
func epasMessageReceived(message: String)

Receipt Handling

ReceiptData is structured and can be stored, reformatted, printed, or rendered electronically. The sample ECR groups receipts by transaction and lets the operator switch between merchant and cardholder copies.

swift
func printReceipt(receipt: ReceiptData) {
    receiptStore.append(receipt)
    printer.print(receipt.generateSimpleReceipt(width: 32))
}

Status Handling

The Swift client maintains a ClientStatus model that can be polled when needed.

swift
let status = await client.status()

if status.isConnected && status.isLoggedIn {
    ui.showReady()
}

ClientStatus exposes:

  • isConnected
  • isLoggedIn
  • isBusy
  • isCardInserted
  • terminalId

Error Model

Swift uses throws for transport, validation, and internal-state failures. ApiResult is still used as a semantic compatibility layer for methods such as disconnect(), sendPurchaseAmounts(_:), and sendRawXml(_:).

Common API result values include:

  • ok
  • errorInvalidParameter
  • errorConnectionFailure
  • errorNotConnected
  • errorCommunicationsFailure
  • errorLibraryFailure
  • errorParseFailure

Sample ECR

The repository contains a macOS sample ECR application that exercises the Swift SDK and exposes:

  • connection and login forms
  • transaction actions
  • XML trace
  • terminal display mirroring
  • receipt history
  • callback dialogs

Use the sample as the fastest parity and regression test tool while integrating new SDK functionality.

Related Pages