# Quick Start

This is the shortest path from zero to a completed purchase with OnsideKit. Copy the two files below, replace the placeholders, and you have a working flow. Each step links to a deeper guide when you need more detail.

{% hint style="info" %}
**Before you start**

* OnsideKit is added to your project — see [Installation](/sdk/getting-started/installation.md).
* Your app is registered with Onside and you have at least one product configured in the [Onside Developer Console](https://developer.onside.io).
* You declared a custom URL scheme in your `Info.plist` for app-to-app login — see [Initializing the SDK](/sdk/getting-started/initialization.md).
  {% endhint %}

## 1. Initialize the SDK and handle URLs

Do this once at launch, in your `AppDelegate`. `Onside.initialize()` must be called **before** any other OnsideKit API.

```swift
import UIKit
import OnsideKit

@main
final class AppDelegate: UIResponder, UIApplicationDelegate {

    // Keep a strong reference to your purchase manager for the app's lifetime.
    let store = PurchaseManager()

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // 1. Initialize OnsideKit before calling any other OnsideKit API.
        Onside.initialize()

        // 2. Provide the callback URL scheme you declared in Info.plist.
        Onside.callbackScheme = "com.yourapp.onside"

        // 3. Register a transaction observer as early as possible. The payment
        //    queue stays idle until at least one observer is registered.
        Onside.defaultPaymentQueue().add(observer: store)

        return true
    }

    // 4. Forward incoming URLs so the app-to-app login flow can finish.
    func application(
        _ app: UIApplication,
        open url: URL,
        options: [UIApplication.OpenURLOptionsKey: Any] = [:]
    ) -> Bool {
        Onside.handle(url: url)
    }
}
```

## 2. Create a purchase manager

A single object fetches products, starts purchases, and processes transactions. The payment queue only makes progress while at least one observer is registered (that is why we add it at launch in step 1).

```swift
import OnsideKit

final class PurchaseManager: OnsidePaymentTransactionObserver {

    // Retain in-flight product requests — a dropped request is cancelled.
    private var productsRequest: OnsideProductsRequest?

    // MARK: - Fetch a product

    func loadProduct(identifier: String) {
        let request = Onside.makeProductsRequest(productIdentifiers: [identifier])
        request.delegate = self
        productsRequest = request   // keep it alive for the whole request
        request.start()
    }

    // MARK: - Start a purchase

    func buy(_ product: OnsideProduct) {
        let payment = OnsidePayment(product: product)
        Onside.defaultPaymentQueue().add(payment) { result in
            // The completion only reports the pre-flight outcome, e.g. the user
            // dismissed the login screen. Transaction updates arrive below.
            if case .failure(let error) = result {
                print("Couldn't start the purchase: \(error)")
            }
        }
    }

    // MARK: - Process transactions (OnsidePaymentTransactionObserver)

    func onsidePaymentQueue(
        _ queue: OnsidePaymentQueue,
        updatedTransactions: [OnsidePaymentTransaction]
    ) {
        for transaction in updatedTransactions {
            switch transaction.transactionState {
            case .purchased, .restored:
                unlockContent(for: transaction.payment.product.productIdentifier)
                queue.finishTransaction(transaction)   // required — see the warning below

            case .failed:
                print("Transaction failed: \(String(describing: transaction.error))")
                queue.finishTransaction(transaction)   // required

            case .purchasing:
                break   // still in progress — show a spinner if you like

            @unknown default:
                break
            }
        }
    }

    private func unlockContent(for productIdentifier: String) {
        // Grant access to the purchased content and persist it
        // (e.g. in UserDefaults or the Keychain) so it survives relaunches.
    }
}

// MARK: - Receive the fetched product

extension PurchaseManager: OnsideProductsRequestDelegate {

    func onsideProductsRequest(
        _ request: OnsideProductsRequest,
        didReceive response: OnsideProductsResponse
    ) {
        guard let product = response.products.first else { return }
        // Show `product` in your UI, then call buy(product) when the user taps Buy.
        buy(product)
    }

    func onsideProductsRequest(
        _ request: OnsideProductsRequest,
        didFailWithError error: OnsideProductsRequestError
    ) {
        print("Couldn't load products: \(error)")
    }
}
```

## 3. Run the flow

Kick everything off from your UI — for example, load a product when a screen appears:

```swift
appDelegate.store.loadProduct(identifier: "your.product.identifier")
```

The flow then runs on its own:

1. `loadProduct` fetches the product and `onsideProductsRequest(_:didReceive:)` delivers it.
2. `buy` adds the product to the payment queue. If the user is not logged in, OnsideKit presents the login screen automatically and resumes the purchase afterwards.
3. Each state change is delivered to `onsidePaymentQueue(_:updatedTransactions:)`, where you unlock content and **finish** the transaction.

{% hint style="warning" %}
**You must finish every transaction.** Call `finishTransaction(_:)` for every transaction that reaches `.purchased`, `.restored`, or `.failed`. If you don't, OnsideKit considers it unprocessed and re-delivers it on the next launch. See [The Payment Queue & Transactions](/sdk/core-concepts/payment-queue.md).
{% endhint %}

{% hint style="info" %}
OnsideKit delivers all delegate and observer callbacks on the **main actor**, and you must retain request objects and observers yourself. See [Threading & Object Lifetime](/sdk/core-concepts/threading-and-retention.md).
{% endhint %}

## Next steps

* [Initializing the SDK](/sdk/getting-started/initialization.md) — the `Info.plist` URL scheme, `callbackScheme`, and `handle(url:)` in detail
* [Authentication & User Account](/sdk/core-concepts/authentication.md) — how login works and how to check session state
* [Fetching Products](/sdk/products-and-subscriptions/fetching-products.md) — reading titles, prices, and subscription details
* [Making a Purchase](/sdk/purchasing/making-a-purchase.md) — purchase and restore flows, errors, and the storefront safety gate
* [Local Testing with a .storekit File](/sdk/advanced-and-tooling/local-testing.md) — try the whole flow without a backend


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.onside.io/sdk/getting-started/quick-start.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
