Swift SDK

Official Swift SDK for TruthMark invisible watermarking on iOS, macOS, and server-side Swift.

Installation

Swift Package Manager (Package.swift)
dependencies: [
    .package(url: "https://github.com/truthmark/sdk-swift", from: "1.0.0")
]

Quick Example

import TruthMarkSDK

let client = TruthMarkClient(config: .init(
    apiKey: "tm_live_your_key",
    baseURL: "https://truthmark-api.onrender.com"
))

// Encode
let result = try await client.encode(
    imagePath: "image.png",
    message: "Copyright 2025"
)
print("Download: \(result.downloadUrl)")
print("PSNR: \(String(format: "%.1f", result.metadata.psnr)) dB")

// Decode
let decoded = try await client.decode(imagePath: "watermarked.png")
if decoded.found {
    print("Message: \(decoded.message ?? "")")
    print("Confidence: \(String(format: "%.1f", decoded.confidence * 100))%")
}

API Reference

TruthMarkClient(config:)

Create a new client with configuration.

struct TruthMarkConfig {
    let apiKey: String
    let baseURL: String?
    let timeout: TimeInterval?
}

encode(imagePath:message:) async throws

Embed an invisible watermark. Returns EncodeResult.

decode(imagePath:) async throws

Extract watermark. Returns DecodeResult.

Error Handling

do {
    let result = try await client.encode(imagePath: path, message: msg)
} catch let error as TruthMarkError {
    switch error {
    case .unauthorized:
        print("Invalid API key")
    case .rateLimited(let retryAfter):
        print("Rate limited. Retry after \(retryAfter)s")
    case .payloadTooLarge:
        print("Image too large (max 20MB)")
    case .apiError(let code, let message):
        print("Error \(code): \(message)")
    }
} catch {
    print("Network error: \(error.localizedDescription)")
}

Advanced Examples

SwiftUI Image Picker

import SwiftUI
import PhotosUI
import TruthMarkSDK

struct WatermarkView: View {
    @State private var selectedItem: PhotosPickerItem?
    @State private var resultURL: String?
    @State private var isProcessing = false

    let client = TruthMarkClient(config: .init(
        apiKey: ProcessInfo.processInfo.environment["TRUTHMARK_API_KEY"] ?? ""
    ))

    var body: some View {
        VStack(spacing: 20) {
            PhotosPicker("Select Image", selection: $selectedItem)
            if isProcessing { ProgressView() }
            if let url = resultURL { Text("Done: \(url)") }
        }
        .onChange(of: selectedItem) { _, item in
            guard let item else { return }
            Task { await processImage(item) }
        }
    }

    func processImage(_ item: PhotosPickerItem) async {
        isProcessing = true
        defer { isProcessing = false }
        guard let data = try? await item.loadTransferable(type: Data.self) else { return }
        let result = try? await client.encode(imageData: data, message: "© 2025")
        resultURL = result?.downloadUrl
    }
}

Vapor Server Route

import Vapor
import TruthMarkSDK

func routes(_ app: Application) throws {
    let client = TruthMarkClient(config: .init(
        apiKey: Environment.get("TRUTHMARK_API_KEY")!
    ))

    app.post("watermark") { req async throws -> EncodeResult in
        let file = try req.content.decode(FileUpload.self)
        return try await client.encode(
            imageData: file.data,
            message: file.message
        )
    }
}

Best Practices

Use async/await throughout

All SDK methods are async — use structured concurrency with Swift's native async/await.

Store keys in Keychain (iOS) or env vars (server)

Never hardcode API keys in your app bundle.

Don't ship API keys in client apps

For iOS apps, proxy through your backend to protect keys.

Need Help?

Check out the API reference or reach out to support.