Swift SDK

The official Skytells SDK for Swift — access Skytells AI services from iOS, macOS, tvOS, and watchOS.

Requirements

The Skytells Swift SDK requires:

  • iOS 15.0+ / macOS 12.0+ / tvOS 15.0+ / watchOS 8.0+
  • Swift 5.9+
  • Xcode 15.0+

Installation

Adding to your project

The Skytells Swift SDK is distributed as a Swift Package and can be added via Xcode or Swift Package Manager.

After installation, import the Skytells module into your project and start making API calls with just a few lines of code.

1. In Xcode, go to File → Add Package Dependencies…
2. Enter the repository URL:
   https://github.com/Skytells/swift-sdk.git
3. Set the dependency rule to "Up to Next Major Version"
   starting from 1.0.0.
4. Select the "Skytells" library product and add it
   to your target.

Quick Start

Getting Started with Skytells

The SDK provides a simple and intuitive interface for interacting with the Skytells API. The main entry point is SkytellsClient, which you initialize with your API key.

The example on the right demonstrates the basic workflow:

  1. Initialize the client with your API key
  2. Make a prediction using a model
  3. Process the results
Example
Basic Usage
import Skytells

// Initialize with your API key
let client = SkytellsClient(apiKey: "sk-your-api-key")

// Or use the factory method
let client = Skytells.createClient(apiKey: "sk-your-api-key")

Complete Example

import Skytells

// Initialize the client
let client = SkytellsClient(apiKey: "sk-your-api-key")

// List available models
let models = try await client.listModels()
print("Available models:", models)

// Make a prediction
let prediction = try await client.predict(.init(
    model: "vendor/model-name",
    input: ["prompt": "Generate a creative story about AI"],
    await: true
))

// Access the result
if let text = prediction.outputString {
    print("Output:", text)
}

Authentication

Setting Up Authentication

To access the Skytells API, you need an API key from your Skytells dashboard. This key authenticates your requests and determines your access level to different features and models.

Authentication
Authentication Options
import Skytells

// With API key
let client = SkytellsClient(apiKey: "sk-your-api-key")

API Reference

Available Methods

The SkytellsClient provides the following methods for interacting with the Skytells API.

Predictions

  • predict(_:) — Run a prediction on a model
  • getPrediction(id:) — Get a prediction by ID
  • cancelPrediction(id:) — Cancel a running prediction
  • deletePrediction(id:) — Delete a prediction

Models

  • listModels() — List all available models
API
API Methods
// Run a prediction
let prediction = try await client.predict(.init(
    model: "vendor/model-name",
    input: ["prompt": "Your prompt here"],
    await: true
))

Error Handling

Handling Errors

The SDK throws SkytellsError for API errors, which includes the error message, error ID, and HTTP status code. Use Swift's do-catch to handle errors gracefully.

Network errors and other system-level errors are thrown as standard Swift errors.

Errors
Error Handling
do {
    let prediction = try await client.predict(.init(
        model: "vendor/model",
        input: ["prompt": "test"]
    ))
} catch let error as SkytellsError {
    print("Error: \(error.message)")
    print("Error ID: \(error.errorId)")
    print("HTTP Status: \(error.httpStatus)")
} catch {
    print("Unexpected error: \(error)")
}

Best Practices

Avoiding Memory Leaks & Retain Cycles

When using the SDK inside classes (e.g. view models, coordinators), capturing self strongly in async closures or Task blocks can create retain cycles that prevent deallocation.

Always use [weak self] when capturing self in closures or Task blocks, and guard against nil before proceeding.

Best Practices
Memory Safety
class PredictionViewModel: ObservableObject {
    private let client = SkytellsClient(
        apiKey: "sk-your-api-key"
    )
    @Published var output: String = ""
    @Published var isLoading = false

    func generate(prompt: String) {
        isLoading = true
        Task { [weak self] in
            guard let self else { return }
            do {
                let prediction = try await self.client
                    .predict(.init(
                        model: "vendor/model",
                        input: ["prompt": prompt],
                        await: true
                    ))
                await MainActor.run {
                    self.output = prediction
                        .outputString ?? ""
                    self.isLoading = false
                }
            } catch {
                await MainActor.run {
                    self.isLoading = false
                }
            }
        }
    }
}

Client Lifecycle

Create a single SkytellsClient instance and reuse it across your app rather than creating a new one for each request. This avoids unnecessary allocations and lets the underlying URLSession reuse connections.

For SwiftUI apps, inject the client through the environment or a shared singleton.

Best Practices
Client Reuse
// A shared client instance for the lifetime of the app
extension SkytellsClient {
    static let shared = SkytellsClient(
        apiKey: Config.apiKey
    )
}

// Usage anywhere in your app
let prediction = try await SkytellsClient.shared
    .predict(.init(
        model: "vendor/model",
        input: ["prompt": "hello"]
    ))

Concurrency Tips

Always update UI on the main actor. The SDK's network calls run on a background executor, so you must dispatch back to MainActor before mutating any @Published or @State properties.

Use structured concurrency (async let, TaskGroup) when making multiple independent API calls to run them in parallel.

Best Practices
Concurrency
// Run multiple predictions in parallel
async let photo = client.predict(.init(
    model: "vendor/image-model",
    input: ["prompt": "A mountain landscape"],
    await: true
))
async let text = client.predict(.init(
    model: "vendor/text-model",
    input: ["prompt": "Describe a mountain"],
    await: true
))

// Await both results
let (photoResult, textResult) = try await (
    photo, text
)

DO ✅

DO
Correct Patterns
// ✅ DO: Capture self weakly in Task blocks
Task { [weak self] in
    guard let self else { return }
    let result = try await self.client.predict(.init(
        model: "vendor/model",
        input: ["prompt": "hello"],
        await: true
    ))
    await MainActor.run {
        self.output = result.outputString ?? ""
    }
}

DON'T ❌

DON'T
Anti-Patterns
// ❌ DON'T: Capture self strongly — causes retain cycles
Task {
    let result = try await self.client.predict(.init(
        model: "vendor/model",
        input: ["prompt": "hello"],
        await: true
    ))
    self.output = result.outputString ?? ""
}

Webhooks

Predictions with Webhooks

When running long-running predictions, you can attach a webhook URL so Skytells notifies your server when the prediction completes, fails, or is cancelled — instead of polling.

Pass a webhook dictionary in your prediction input with:

  • url — your HTTPS endpoint
  • events — an array of event types: completed, failed, canceled
Webhooks
Webhook Examples
import Skytells

let client = SkytellsClient(apiKey: "sk-your-api-key")

// Run a prediction with a webhook callback
let prediction = try await client.predict(.init(
    model: "vendor/image-model",
    input: ["prompt": "A futuristic cityscape"],
    webhook: [
        "url": "https://your-server.com/webhook",
        "events": ["completed", "failed"]
    ]
))

// The prediction starts asynchronously.
// Skytells will POST to your webhook URL when done.
print("Prediction ID:", prediction.id)

Handling Webhook Payloads

When Skytells triggers a webhook, it sends a POST request with the full prediction object as JSON. Here's how to handle it on a Swift server (e.g. using Vapor).

The webhook payload matches the same Prediction structure returned by the SDK.

Webhooks
Server-Side
import Vapor

// Define the webhook payload structure
struct WebhookPayload: Content {
    let id: String
    let status: String
    let model: String
    let output: AnyCodable?
    let error: String?
}

func routes(_ app: Application) throws {
    app.post("webhook") { req -> HTTPStatus in
        let payload = try req.content.decode(
            WebhookPayload.self
        )

        switch payload.status {
        case "succeeded":
            // Handle successful prediction
            print("Prediction \(payload.id) succeeded")
            print("Output: \(payload.output ?? "")")
        case "failed":
            // Handle failure
            print("Prediction \(payload.id) failed")
            print("Error: \(payload.error ?? "")")
        case "canceled":
            // Handle cancellation
            print("Prediction \(payload.id) canceled")
        default:
            break
        }

        return .ok
    }
}

Was this page helpful?