For the complete documentation index, see llms.txt. This page is also available as Markdown.

Threading & Object Lifetime

OnsideKit's main-actor threading model and the lifetime rules for request objects, observers, and delegates.

Two rules prevent most integration bugs with OnsideKit: call it from the main actor, and keep a strong reference to the objects you hand it.

Threading: everything UI-facing is @MainActor

OnsideKit's entry points and callbacks are main-actor isolated:

  • The Onside facade is @MainActor.

  • OnsidePaymentQueue, OnsideProductsRequest, and OnsideSignedInAppsHistoryRequest — and all of their methods — are @MainActor.

  • All delegate and observer methods (OnsideDelegate, OnsidePaymentQueueDelegate, OnsidePaymentTransactionObserver, OnsideProductsRequestDelegate, OnsideSignedInAppsHistoryRequestDelegate) are @MainActor.

In practice: call OnsideKit from the main actor, and expect every callback and completion handler on the main actor. You can touch UIKit directly inside them without hopping queues.

The value types you receive are Sendable and not actor-isolated, so they are safe to pass to background work:

  • Models — OnsideProduct, OnsidePayment, OnsidePaymentTransaction, OnsideStorefront, OnsidePrice, OnsidePeriod, OnsideProductsResponse, OnsideAttributionMetadata, OnsideSignedInAppsHistory.

  • Error enums — all Onside*Error types are Sendable (and Codable).

Object lifetime: what you must retain

OnsideKit holds your callback objects weakly. If you don't keep a strong reference, they are deallocated and you stop receiving callbacks.

Request objects — retain until they finish

Onside.makeProductsRequest(...) and Onside.makeSignedInAppsHistoryRequest() return request objects that you own. The SDK does not keep them alive.

final class ProductLoader: OnsideProductsRequestDelegate {
    private var request: OnsideProductsRequest?   // strong reference

    func load() {
        let request = Onside.makeProductsRequest(productIdentifiers: ["my.product"])
        request.delegate = self
        self.request = request   // retain for the whole request
        request.start()
    }
}

Use stop() to cancel an in-flight request; a cancelled request reports .cancelled to its failure delegate.

Observers — retain and remove

The payment queue holds observers weakly. Keep a strong reference to your observer, and balance add(observer:) with remove(observer:) when the observer goes away.

A long-lived owner (an app-level coordinator or your AppDelegate) is the natural place to hold the queue observer, so it stays alive for the whole app session and never misses a transaction.

Delegates — retain them too

Onside.delegate (OnsideDelegate) and Onside.defaultPaymentQueue().delegate (OnsidePaymentQueueDelegate) are weak references. Assign an object you keep alive — typically your AppDelegate or an app-level object.

Switching over OnsideKit enums

OnsideKit ships as a binary framework built for library evolution, so its public enums are resilient (non-frozen). When you switch over one — such as OnsidePaymentTransactionState or OnsidePeriod — include an @unknown default to stay forward-compatible with future cases:

Checklist

  • ✅ Call OnsideKit from the main actor; handle callbacks on the main actor.

  • ✅ Retain request objects until they finish; stop() to cancel.

  • ✅ Retain observers and delegates; remove(observer:) when done.

  • ✅ Add an @unknown default to every switch over an OnsideKit enum.

Last updated

Was this helpful?