diff --git a/core/src/commonMain/kotlin/com/revenuecat/purchases/kmp/Purchases.kt b/core/src/commonMain/kotlin/com/revenuecat/purchases/kmp/Purchases.kt index 088fa818..66459d99 100644 --- a/core/src/commonMain/kotlin/com/revenuecat/purchases/kmp/Purchases.kt +++ b/core/src/commonMain/kotlin/com/revenuecat/purchases/kmp/Purchases.kt @@ -107,6 +107,10 @@ public expect class Purchases { /** * The delegate is responsible for handling promotional product purchases (App Store only) and * changes to customer information. + * + * **Note:** If your delegate is not a singleton, make sure you set this back to null when + * you're done to avoid memory leaks. For instance, if your delegate is tied to a screen, set + * this to null when the user navigates away from the screen. */ public var delegate: PurchasesDelegate? diff --git a/core/src/iosMain/kotlin/com/revenuecat/purchases/kmp/Purchases.ios.kt b/core/src/iosMain/kotlin/com/revenuecat/purchases/kmp/Purchases.ios.kt index 40c3132e..09121b7e 100644 --- a/core/src/iosMain/kotlin/com/revenuecat/purchases/kmp/Purchases.ios.kt +++ b/core/src/iosMain/kotlin/com/revenuecat/purchases/kmp/Purchases.ios.kt @@ -1,6 +1,7 @@ package com.revenuecat.purchases.kmp import cocoapods.PurchasesHybridCommon.RCCommonFunctionality +import cocoapods.PurchasesHybridCommon.RCPurchasesDelegateProtocol import cocoapods.PurchasesHybridCommon.RCStoreProduct import cocoapods.PurchasesHybridCommon.configureWithAPIKey import cocoapods.PurchasesHybridCommon.recordPurchaseForProductID @@ -117,9 +118,18 @@ public actual class Purchases private constructor(private val iosPurchases: IosP public actual val appUserID: String get() = iosPurchases.appUserID() + /** + * Making sure we keep a strong reference to our delegate wrapper, as iosPurchases only keeps + * a weak one. This avoids the wrapper from being deallocated the first chance it gets. This + * behavior matches the Android platform behavior. + */ + private var _delegateWrapper: RCPurchasesDelegateProtocol? = null public actual var delegate: PurchasesDelegate? get() = iosPurchases.delegate()?.toPurchasesDelegate() - set(value) = iosPurchases.setDelegate(value?.toRcPurchasesDelegate()) + set(value) { + _delegateWrapper = value.toRcPurchasesDelegate() + iosPurchases.setDelegate(_delegateWrapper) + } public actual val isAnonymous: Boolean get() = iosPurchases.isAnonymous() @@ -340,7 +350,7 @@ public actual class Purchases private constructor(private val iosPurchases: IosP } public actual fun close() { - iosPurchases.setDelegate(null) + delegate = null } public actual fun getCustomerInfo( diff --git a/core/src/iosMain/kotlin/com/revenuecat/purchases/kmp/PurchasesDelegate.ios.kt b/core/src/iosMain/kotlin/com/revenuecat/purchases/kmp/PurchasesDelegate.ios.kt index 69f6ba43..81d5f065 100644 --- a/core/src/iosMain/kotlin/com/revenuecat/purchases/kmp/PurchasesDelegate.ios.kt +++ b/core/src/iosMain/kotlin/com/revenuecat/purchases/kmp/PurchasesDelegate.ios.kt @@ -15,8 +15,8 @@ import platform.darwin.NSObject internal fun RCPurchasesDelegateProtocol.toPurchasesDelegate(): PurchasesDelegate = (this as PurchasesDelegateWrapper).wrapped -internal fun PurchasesDelegate.toRcPurchasesDelegate(): RCPurchasesDelegateProtocol = - PurchasesDelegateWrapper(this) +internal fun PurchasesDelegate?.toRcPurchasesDelegate(): RCPurchasesDelegateProtocol? = + this?.let { PurchasesDelegateWrapper(it) } private class PurchasesDelegateWrapper(val wrapped: PurchasesDelegate) : RCPurchasesDelegateProtocol,