Skip to content

Commit

Permalink
Keeps a strong reference to the delegate wrapper inside Purchases.
Browse files Browse the repository at this point in the history
  • Loading branch information
JayShortway committed Sep 20, 2024
1 parent cc4f72b commit 1810b44
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
}

0 comments on commit 1810b44

Please sign in to comment.