Linkit
SDK Reference

Swift SDK

High-performance native Swift SDK for the Linkit API — async/await, Codable, URLSession, zero dependencies.

Swift SDK

Platform Support

macOS 13+ · iOS 16+ · watchOS 9+ · tvOS 16+ · Swift 5.9+ · Pure Foundation — zero external dependencies.


Installation

Swift Package Manager

Add to your Package.swift:

dependencies: [
    .package(url: "https://github.com/linkit-sa/linkit-swift-sdk", from: "0.1.0"),
]

Or add via Xcode: File → Add Package Dependencies → Enter URL.

Direct Download

Download the release archive from the Downloads page.


Quick Start

import LinkitSDK

let client = try LinkitClient.quickSetup(
    baseUrl: "https://linkit.works/api/v1",
    jwtToken: "your-jwt-token"
)

// Create a product
let product = try await client.products.create(
    ProductRequest(ivId: "P-001", nameEn: "Aspirin 500mg", averagePrice: 12.50)
)

// List SKUs
let skus = try await client.skus.list(branchIvId: "BR-RYD", enabled: true)
for sku in skus.data {
    print("\(sku.ivId): \(sku.price ?? 0) SAR")
}

Configuration

let config = LinkitConfiguration(
    baseUrl: "https://linkit.works/api/v1",
    jwtToken: "your-jwt-token",
    timeoutSeconds: 45,
    maxConcurrentRequests: 20,
    maxRetries: 5,
    retryBaseDelayMs: 100,
    retryMaxDelayMs: 30_000,
    circuitBreakerThreshold: 10,
    circuitBreakerRecoveryMs: 30_000,
    userAgent: "my-pharmacy-app/1.0",
    defaultHeaders: ["X-Custom": "value"]
)
let client = try LinkitClient(configuration: config)
SettingDefaultDescription
timeoutSeconds30Request timeout
maxConcurrentRequests10Rate limiter permits
maxRetries3Retry attempts for 5xx/429
retryBaseDelayMs200Base exponential backoff
retryMaxDelayMs10000Max backoff cap
circuitBreakerThreshold5Failures before opening
circuitBreakerRecoveryMs60000Recovery timeout

Services Reference (11 Services)

ServiceMethodsDescription
client.productscreate(), getByIvId(), update(), delete(), list(), listAll()Product catalog
client.skuscreate(), getByIvId(), update(), delete(), list()SKU / inventory
client.branchescreate(), getByIvId(), update(), delete(), list()Physical locations
client.customerscreate(), getById(), update(), delete(), list(), listAll(), lookupByEmail(), lookupByPhone(), search(), getAddresses(), createAddress(), updateAddress(), deleteAddress(), getGroups(), createGroup(), updateGroup(), deleteGroup()Customer profiles
client.ordersgetById(), list(), updateStatus()Order management
client.offerscreate(), getById(), update(), updateStatus(), delete(), list(), bulkUpsert()Promotions & discounts
client.brandscreate(), getById(), update(), delete(), list()Brand management
client.categoriescreate(), getById(), update(), delete(), list()Product categories
client.genericscreate(), getById(), update(), delete(), list()Generic medicines
client.integrationslist(), getById(), getConfig(), execute()Integration management
client.healthcheck(), detailed()Health checks

Products

// Create
let product = try await client.products.create(
    ProductRequest(
        ivId: "P-001",
        nameEn: "Aspirin 500mg", nameAr: "أسبرين ٥٠٠ مجم",
        averagePrice: 12.50, isEnabled: true, isPom: false
    )
)

// List with filters
let products = try await client.products.list(
    page: 1, limit: 20, search: "aspirin",
    enabled: true, isFastMoving: true
)

// Stream all products (auto-paginates)
for await product in client.products.listAll() {
    print(product.nameEn ?? "")
}

// Update
let updated = try await client.products.update("P-001",
    request: ProductRequest(ivId: "P-001", averagePrice: 14.00))

// Delete
try await client.products.delete("P-001")

SKUs

let sku = try await client.skus.create(
    SKURequest(ivId: "S-001", branchIvId: "BR-RYD", productIvId: "P-001",
               price: 29.99, stock: 150)
)

let skus = try await client.skus.list(
    branchIvId: "BR-RYD", productIvId: "P-001", enabled: true
)

Customers

// Create
let customer = try await client.customers.create(
    CustomerRequest(firstName: "Ahmad", lastName: "Ali",
                     email: "ahmad@company.sa", phone: "+966512345678")
)

// Lookup
if let found = try await client.customers.lookupByEmail("ahmad@company.sa") {
    print("Found: \(found.id)")
}

// Address management
let address = try await client.customers.createAddress(customer.id,
    request: AddressRequest(label: "Home", line1: "123 King Rd", city: "Riyadh", country: "SA"))

// Group management
let group = try await client.customers.createGroup(
    CustomerGroupRequest(name: "VIP Customers"))

Orders

let orders = try await client.orders.list(
    status: "confirmed", paymentStatus: "completed",
    from: "2026-01-01", currency: "SAR", sort: "-created"
)

let updated = try await client.orders.updateStatus("ORD-001",
    request: OrderStatusUpdateRequest(orderStatus: "shipped", notes: "Via Aramex"))

Offers

// Create
let offer = try await client.offers.create(
    OfferRequest(name: "Weekend 15%", offerType: "direct_discount",
                 status: "active", targetScope: "product",
                 targetIvId: "P-001", discountType: "percentage",
                 discountValue: 15.0)
)

// List with filters
let offers = try await client.offers.list(
    status: "active", offerType: "direct_discount",
    isActive: true, sort: "-created"
)

// Update status
try await client.offers.updateStatus(offer.data.id,
    request: OfferStatusUpdate(status: "paused", isActive: false))

// Bulk upsert
let bulk = try await client.offers.bulkUpsert([
    OfferRequest(name: "BOGO", offerType: "bogo", buyQty: 1, getQty: 1),
    OfferRequest(name: "Bundle", offerType: "bundle_deal"),
])
print("Processed: \(bulk.data.processed), Failed: \(bulk.data.failed)")

// Delete
try await client.offers.delete(offer.data.id)

Brands / Categories / Generics

// Brand
let brand = try await client.brands.create(
    BrandRequest(brandCode: "BRD-001", nameEn: "Panadol", nameAr: "بنادول"))

// Category
let category = try await client.categories.create(
    CategoryRequest(categoryCode: "CAT-001", nameEn: "Pain Relief"))

// Generic medicine
let generic = try await client.generics.create(
    GenericRequest(genericCode: "GEN-001", nameEn: "Paracetamol", nameAr: "باراسيتامول"))

Error Handling

do {
    let product = try await client.products.getByIvId("INVALID")
} catch let error as LinkitError {
    switch error {
    case .notFound(let type, let id):
        print("Not found: \(type) \(id ?? "")")
    case .authentication(let msg, _):
        print("Auth: \(msg)")
    case .rateLimited(let retryAfter):
        print("Rate limited, retry after \(retryAfter ?? 0)s")
    case .circuitOpen(let endpoint):
        print("Circuit open: \(endpoint)")
    case .validation(let field, let msg):
        print("Validation: \(field)\(msg)")
    default:
        print(error.description)
    }
}

Architecture

  • URLSession — Native Apple networking, no external dependencies
  • Codable — Built-in JSON serialization with convertToSnakeCase / convertFromSnakeCase
  • Actor-based circuit breaker — Thread-safe via Swift concurrency
  • Checked continuation rate limiter — Backpressure without GCD
  • AsyncStream — Auto-paginating listAll() on Products and Customers
  • Sendable — All models conform to Sendable for safe cross-actor usage