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
Onsidefacade is@MainActor.OnsidePaymentQueue,OnsideProductsRequest, andOnsideSignedInAppsHistoryRequest— and all of their methods — are@MainActor.All delegate and observer methods (
OnsideDelegate,OnsideExternalLoginDelegate,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*Errortypes areSendable(andCodable).
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.
Delegates — retain them too
Onside.delegate (OnsideDelegate), Onside.externalLoginDelegate (OnsideExternalLoginDelegate), 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 defaultto everyswitchover an OnsideKit enum.
Last updated
Was this helpful?