Skip to content

Swift SDK

Official Swift SDK for thelawin.dev.

Requires Swift 5.9+ / iOS 14+ / macOS 11+

Installation

Swift Package Manager

swift
// Package.swift
dependencies: [
    .package(url: "https://github.com/steviee/thelawin-clients.git", branch: "main")
],
targets: [
    .target(
        name: "YourApp",
        dependencies: [
            .product(name: "Thelawin", package: "thelawin-clients", condition: nil)
        ],
        path: "swift"
    )
]

Oder in Xcode: File → Add Package Dependencies → https://github.com/steviee/thelawin-clients

TIP

Swift Package Manager unterstützt Mono-Repos mit Subdirectories. Das SDK liegt im swift/ Verzeichnis.

Quick Start

swift
import Thelawin

let client = ThelawinClient(apiKey: "env_sandbox_xxx")

let result = try await client.invoice()
    .number("2026-001")
    .date("2026-01-15")
    .seller(name: "Acme GmbH", vatId: "DE123456789", city: "Berlin", country: "DE")
    .buyer(name: "Customer AG", city: "München", country: "DE")
    .addItem(description: "Consulting Services", quantity: 8, unit: .hour, unitPrice: 150, vatRate: 19)
    .template(.minimal)
    .generate()

switch result {
case .success(let invoice):
    try invoice.savePdf(to: URL(fileURLWithPath: "./invoice.pdf"))
    print("Generated: \(invoice.filename)")
case .failure(let errors):
    errors.forEach { print("\($0.path): \($0.message)") }
}

Client Options

swift
let client = ThelawinClient(
    apiKey: "env_sandbox_xxx",
    baseUrl: URL(string: "https://api.thelawin.dev")!,  // optional
    timeout: 30                                         // optional, seconds
)

Builder API

Invoice Details

swift
client.invoice()
    .number("2026-001")           // Required
    .date("2026-01-15")           // Required, String or Date
    .dueDate("2026-02-15")        // Optional
    .currency("EUR")              // Default: "EUR"

Parties

swift
.seller(
    name: "Acme GmbH",           // Required
    vatId: "DE123456789",        // Required
    street: "Hauptstraße 1",
    city: "Berlin",              // Required
    postalCode: "10115",
    country: "DE"                // Required
)
.buyer(
    name: "Customer AG",         // Required
    vatId: "DE987654321",
    city: "München",             // Required
    country: "DE"                // Required
)

Line Items

swift
.addItem(
    description: "Consulting",   // Required
    quantity: 8,                 // Required
    unit: .hour,                 // Required (enum)
    unitPrice: 150.00,           // Required
    vatRate: 19.0                // Required
)

Unit Types

swift
enum UnitCode: String {
    case hour = "HUR"
    case day = "DAY"
    case month = "MON"
    case piece = "C62"
    // ... more
}

Customization

swift
.template(.minimal)             // .minimal, .classic, .compact
.locale(.german)                // .english, .german, .french, .spanish, .italian
.accentColor("#8b5cf6")
.footerText("Thank you!")
swift
// From file URL (auto Base64)
.logoFile(URL(fileURLWithPath: "./logo.png"))
.logoFile(URL(fileURLWithPath: "./logo.png"), widthMm: 30)

// From Data
.logoData(imageData, widthMm: 30)

// From Base64
.logoBase64("iVBORw0KGgo...", widthMm: 30)

Result Handling

swift
switch result {
case .success(let invoice):
    print(invoice.filename)       // "invoice-2026-001.pdf"
    print(invoice.validation)     // ValidationResult(status: .valid, ...)

    // Save to file
    try invoice.savePdf(to: URL(fileURLWithPath: "./invoice.pdf"))

    // Get Data
    let data = invoice.toData()

    // Get data URL
    let dataUrl = invoice.toDataUrl()

case .failure(let errors):
    for error in errors {
        print("\(error.path): \(error.message)")
    }
}

Error Handling

swift
do {
    let result = try await client.invoice().generate()
} catch ThelawinError.quotaExceeded {
    print("Quota exceeded, upgrade your plan")
} catch ThelawinError.network(let message) {
    print("Network error: \(message)")
} catch ThelawinError.api(let statusCode, let message) {
    print("API error \(statusCode): \(message)")
}

SwiftUI Example

swift
import SwiftUI
import Thelawin

struct InvoiceView: View {
    @State private var isGenerating = false
    @State private var pdfData: Data?

    let client = ThelawinClient(apiKey: "env_sandbox_xxx")

    var body: some View {
        VStack {
            if let data = pdfData {
                PDFKitView(data: data)
            }

            Button("Generate Invoice") {
                Task { await generateInvoice() }
            }
            .disabled(isGenerating)
        }
    }

    func generateInvoice() async {
        isGenerating = true
        defer { isGenerating = false }

        let result = try? await client.invoice()
            .number("2026-001")
            .seller(name: "Acme GmbH", vatId: "DE123456789", city: "Berlin", country: "DE")
            .buyer(name: "Customer AG", city: "München", country: "DE")
            .addItem(description: "Consulting", quantity: 8, unit: .hour, unitPrice: 150, vatRate: 19)
            .generate()

        if case .success(let invoice) = result {
            pdfData = invoice.toData()
        }
    }
}

Source Code

github.com/steviee/thelawin-clients/tree/main/swift

ZUGFeRD 2.3 & Factur-X 1.0 compliant