From 1810b44490c0b0941836fe36c24bf122fbe5703e Mon Sep 17 00:00:00 2001 From: JayShortway <29483617+JayShortway@users.noreply.github.com> Date: Fri, 20 Sep 2024 12:47:32 +0200 Subject: [PATCH] Keeps a strong reference to the delegate wrapper inside Purchases. --- .../revenuecat/purchases/kmp/Purchases.ios.kt | 12 +++++++++++- .../purchases/kmp/PurchasesDelegate.ios.kt | 18 ------------------ 2 files changed, 11 insertions(+), 19 deletions(-) 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 ccddcb16..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() 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 5e24a209..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 @@ -17,7 +17,6 @@ internal fun RCPurchasesDelegateProtocol.toPurchasesDelegate(): PurchasesDelegat internal fun PurchasesDelegate?.toRcPurchasesDelegate(): RCPurchasesDelegateProtocol? = this?.let { PurchasesDelegateWrapper(it) } - .also { PurchasesDelegateStrongReference.delegate = it } private class PurchasesDelegateWrapper(val wrapped: PurchasesDelegate) : RCPurchasesDelegateProtocol, @@ -46,20 +45,3 @@ private class PurchasesDelegateWrapper(val wrapped: PurchasesDelegate) : } } - -/** - * On the iOS platform side, the backing field of `Purchases.delegate`, `Purchases.privateDelegate`, - * is a `weak var`. On the multiplatform side, we wrap the actual delegate in - * `PurchasesDelegateWrapper` to conform to `RCPurchasesDelegateProtocol`. Since that is a private - * implementation detail, the only reference held to our wrapper is the `weak var`. This means that - * it gets deallocated the first chance it gets. For this reason, we keep a strong reference to our - * `PurchasesDelegateWrapper` here. This matches the Android behavior, which keeps a strong - * reference on the platform side already. - * - * **Note**: we cannot make our `PurchasesDelegateWrapper` an `object` directly, as objects cannot - * extend `NSObject`. See also - * [KT67930](https://youtrack.jetbrains.com/issue/KT-67930/Getting-Crash-while-building-KMM-project-with-XCode-15.3#focus=Comments-27-9796215.0-0) - */ -private object PurchasesDelegateStrongReference { - var delegate: RCPurchasesDelegateProtocol? = null -}