Linkit
SDK Reference

Swift SDK

Native Swift SDK for the Linkit API — async/await, Codable, URLSession, aligned with OpenAPI v1.

Swift SDK

Requirements

Swift 6.0+ (package tools version 6.0) · macOS 14+ · iOS 17+ · watchOS 10+ · tvOS 17+ · Pure Foundation — zero external dependencies.


Base URL

Pass the origin only (no trailing slash), for example https://linkit.works. All service methods use full paths such as /api/v1/products.

If you still pass https://linkit.works/api/v1, the client automatically strips the /api/v1 suffix so paths are not doubled.


Installation

Swift Package Manager

Add to your Package.swift:

dependencies: [
    .package(url: "https://github.com/linkit-sa/linkit-swift-sdk", from: "0.2.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",
    jwtToken: "your-jwt-token"
)

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

// SKU (quantities use API field `qty`; availability uses `available`)
let sku = try await client.skus.create(
    SKURequest(ivId: "S-001", branchIvId: "BR-RYD", productIvId: "P-001",
               price: 29.99, qty: 150, available: true)
)

// List SKUs (response uses `items`, exposed also as `data`)
let skuPage = try await client.skus.list(branchIvId: "BR-RYD", enabled: true)
for row in skuPage.data {
    print("\(row.ivId): qty=\(row.qty ?? 0)")
}

Configuration

let config = LinkitConfiguration(
    baseUrl: "https://linkit.works",
    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 overview

AccessorHighlights
productsCRUD, list / listAll, bulk, fixMissingSkus()
skusCreate returns envelope data; get/update/delete optional branchIvId query; updateStock (qty / available); bulk
branchesList (branches + pagination), create/update (refetch), bulk, updateWorkingHours
customersCRUD, lookup, search, addresses, customer-groups
ordersList (orders + pagination), create, update, updateStatus, bulkCreate (JSON array), bulkDelete, bulkUpdateStatus, analyticsSummary() / export()Data
offersCRUD, status, bulk, analyticsReport(days:)Data
brandsCRUD by brand code path, bulk, updateProducts
categoriesCRUD by category code, tree, move, updateProducts, list filters
genericsCRUD by generic code, bulk, search(body:), clearCache()
integrationslistApps() (GET /api/v1/apps), getConfig / execute / executeBatch with organizationId + slug
healthcheck(), system(), ping(), telemetry()Data

Branches & working hours

let branches = try await client.branches.list(page: 1, limit: 20, search: "Riyadh")
for b in branches.data {
    print(b.ivId ?? "", b.nameEn ?? "", b.active ?? false)
}

let periods = [WorkingHoursPeriod(weekDay: "Monday", openHour: 9, openMinute: 0, closeHour: 17, closeMinute: 0)]
let body = BranchWorkingHoursBody(workingHours: periods)
let wh = try await client.branches.updateWorkingHours("BR-001", body: body)

Integrations (org + slug)

let apps = try await client.integrations.listApps()

let cfg = try await client.integrations.getConfig(organizationId: "org_123", slug: "salla")
let run = try await client.integrations.execute(
    organizationId: "org_123",
    slug: "salla",
    request: IntegrationExecuteRequest(fullSync: true)
)

let batch = try await client.integrations.executeBatch(
    organizationId: "org_123",
    request: BatchExecuteRequest(slugs: ["salla", "zid"], maxConcurrent: 2)
)

Orders (bulk + analytics)

let page = try await client.orders.list(status: "confirmed", sort: "-created")
for o in page.data { print(o.id, o.orderStatus ?? "") }

let created = try await client.orders.create(
    OrderRequest(
        currency: "SAR",
        customerName: "Ahmad Ali",
        destination: "BR-RYD",
        fulfillmentMethod: "delivery",
        fulfillmentStatus: "pending",
        orderStatus: "pending",
        paymentMethod: "cash",
        paymentStatus: "pending",
        skus: ["sku_iv_1"],
        source: "pos",
        sourceTimeStamp: ISO8601DateFormatter().string(from: .now),
        sourceTransactionId: "txn-001",
        totalAmount: 120.0
    )
)

let bulkStatus = try await client.orders.bulkUpdateStatus(
    OrderBulkStatusRequest(
        orderIds: ["ord_1", "ord_2"],
        status: OrderStatusUpdateRequest(orderStatus: "shipped")
    )
)

let analyticsJSON = try await client.orders.analyticsSummary(from: "2026-01-01T00:00:00Z", to: "2026-04-01T00:00:00Z")

Health & telemetry

let health = try await client.health.check()
let sys = try await client.health.system()
let pong = try await client.health.ping()
let telem = try await client.health.telemetry() // decode JSON in your app

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
  • Codable — JSON with convertToSnakeCase / convertFromSnakeCase on the HTTP layer
  • Actor-based circuit breaker — Thread-safe via Swift concurrency
  • Checked continuation rate limiter — Backpressure without GCD
  • AsyncStream — Auto-paginating listAll() on products and customers where supported