Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android crash when unmounting PaywallFooterContainerView: java.lang.IllegalStateException: No ViewModelStoreOwner was provided via LocalViewModelStoreOwner #994

Closed
5 tasks done
carter-0 opened this issue May 2, 2024 · 20 comments · Fixed by #1120
Labels
bug Something isn't working

Comments

@carter-0
Copy link

carter-0 commented May 2, 2024

Describe the bug

When unmounting the RevenueCatUI.PaywallFooterContainerView on Android devices, java.lang.IllegalStateException: No ViewModelStoreOwner was provided via LocalViewModelStoreOwner is thrown.

  1. Environment
    1. Platform: Android only
    2. SDK version: 7.27.1 (both react-native-purchases and react-native-purchases-ui)
    3. OS version: Clip is on Android 10, but bug is on all versions.
    4. Xcode/Android Studio version: idk
    5. React Native version: 0.73.6
    6. SDK installation (CocoaPods + version or manual): expo
    7. How widespread is the issue. Percentage of devices affected: 100%
  2. Debug logs that reproduce the issue:
 ERROR  Your app just crashed. See the error below.
java.lang.IllegalStateException: No ViewModelStoreOwner was provided via LocalViewModelStoreOwner
    at com.revenuecat.purchases.ui.revenuecatui.InternalPaywallKt.getPaywallViewModel(InternalPaywall.kt:301)
    at com.revenuecat.purchases.ui.revenuecatui.InternalPaywallKt.InternalPaywall(InternalPaywall.kt:58)
    at com.revenuecat.purchases.ui.revenuecatui.PaywallKt.Paywall(Paywall.kt:12)
    at com.revenuecat.purchases.ui.revenuecatui.PaywallFooterKt$PaywallFooter$paywallComposable$1.invoke(PaywallFooter.kt:36)
    at com.revenuecat.purchases.ui.revenuecatui.PaywallFooterKt$PaywallFooter$paywallComposable$1.invoke(PaywallFooter.kt:35)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at com.revenuecat.purchases.ui.revenuecatui.PaywallFooterKt.PaywallFooter(PaywallFooter.kt:45)
    at com.revenuecat.purchases.ui.revenuecatui.views.PaywallFooterView$init$2$1.invoke(PaywallFooterView.kt:132)
    at com.revenuecat.purchases.ui.revenuecatui.views.PaywallFooterView$init$2$1.invoke(PaywallFooterView.kt:128)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.ui.platform.ComposeView.Content(ComposeView.android.kt:428)
    at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:252)
    at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:251)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
    at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:195)
    at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:119)
    at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:118)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
    at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:110)
    at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke(Wrapper.android.kt:158)
    at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke(Wrapper.android.kt:157)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
    at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:157)
    at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:142)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:78)
    at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3340)
    at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3273)
    at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:588)
    at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:1013)
    at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:520)
    at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:142)
    at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:133)
    at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:1191)
    at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:133)
    at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:183)
    at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.kt:314)
    at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.kt:192)
    at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:140)
    at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:133)
    at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:1191)
    at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:133)
    at androidx.compose.ui.platform.Wrapper_androidKt.doSetContent(Wrapper.android.kt:104)
    at androidx.compose.ui.platform.Wrapper_androidKt.setContent(Wrapper.android.kt:83)
    at androidx.compose.ui.platform.AbstractComposeView.ensureCompositionCreated(ComposeView.android.kt:251)
    at androidx.compose.ui.platform.AbstractComposeView.onMeasure(ComposeView.android.kt:288)
    at android.view.View.measure(View.java:24723)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6903)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
    at com.revenuecat.purchases.react.ui.PaywallFooterViewManager$createViewInstance$1.onMeasure(PaywallFooterViewManager.kt:40)
    at android.view.View.measure(View.java:24723)
    at com.revenuecat.purchases.react.ui.PaywallFooterViewManager$createViewInstance$1.measureAndLayout$lambda$0(PaywallFooterViewManager.kt:28)
    at com.revenuecat.purchases.react.ui.PaywallFooterViewManager$createViewInstance$1.$r8$lambda$ov1FIeRAm44dBT08dD92Nth7RB0
    at com.revenuecat.purchases.react.ui.PaywallFooterViewManager$createViewInstance$1$$ExternalSyntheticLambda1.run
    at android.os.Handler.handleCallback(Handler.java:883)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7386)
    at java.lang.reflect.Method.invoke(Method.java)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:980)
  1. Steps to reproduce, with a description of expected vs. actual behavior:
  • Create this component:
import {useState} from 'react';
import {Text, Switch} from 'react-native';
import RevenueCatUI from 'react-native-purchases-ui';

export default function PaywallExample() {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <>
            <Switch onChange={() => setIsOpen(!isOpen)} value={isOpen} />

            {isOpen ? (
                <>
                    <RevenueCatUI.PaywallFooterContainerView>
                        <Text>Footer Content</Text>
                    </RevenueCatUI.PaywallFooterContainerView>
                </>
            ) : null}
        </>
    )
}
  • Open paywall with switch
  • Close paywall with switch

Expected behaviour: Paywall footer will be shown and then hidden without any issues
Actual behaviour: Paywall footer is shown succesfully but crashes when unmounted.

Here is a video demonstration of the expected behaviour (on iOS) vs the actual behaviour (on Android) with the exact same code:

8mb.video-7Gv-j6y8b5cE.mp4
@carter-0 carter-0 added the bug Something isn't working label May 2, 2024
@RCGitBot
Copy link
Contributor

RCGitBot commented May 2, 2024

👀 We've just linked this issue to our internal tracker and notified the team. Thank you for reporting, we're checking this out!

@michaelAtRC
Copy link

Hey @carter-0 !

Thanks for reaching out, this error usually happens when LocalViewModelStoreOwner.current is set to null. Can you please verify that this doesn't get set to null, it could be because of something like a rerender being triggered by your code. This could be the result of something in your switch case causing an issue or the state not being re-rendered properly.

Let me know if that helps!

@carter-0
Copy link
Author

carter-0 commented May 6, 2024

@michaelAtRC Thanks for the response but I can't figure out how to check this. Is LocalViewModelStoreOwner not a Kotlin native component handled by react-native-purchases-ui? or is there a way to access this from react native?

Sorry for the ignorance, I'm pretty new to react native & app development😅

@Watersdr
Copy link

I'm experiencing this crash as well. Dismissing the paywall footer on Android causes the app to crash every time

@Watersdr
Copy link

For what it's worth, I'm not using any custom UI with my footer. I'm simply displaying it inside a BottomSheet. @carter-0 I was able to stop this crash from happening by commenting out this line:

This is fine for my use-case because my view isn't ever updating since I'm not using custom UI with my footer, which is the purpose of that call. The crash is 100% happening somewhere inside the onMeasure call within PaywallFooterViewManager.kt but I don't have time to track down the cause right now.

I think I'll just have to patch the library to comment that line to get my app working properly.

@carter-0
Copy link
Author

Thanks @Watersdr, I can confirm commenting that line out also fixes the crash for me, even with custom ui in the footer. I haven't noticed any other effects so far so I'm also going to patch the lib as a temporary fix.

@michaelAtRC
Copy link

Hey There,

Glad to see that this was fixed and things are now working as planned. On a side note, we have updated the SDK which has had more bug improvements since this, so I recommend updating to the latest version to ensure you have all the fixes that we have put out since this.

@michaelAtRC michaelAtRC closed this as not planned Won't fix, can't repro, duplicate, stale Jun 27, 2024
@keremkusmezer
Copy link

Hey There,

I switched to 7.28.1, the issue still persists, I had to change the paywallfootermanager.kt to fix the crash, is it fixed in version 8.0? Because it is a breaking change I don't want to switch for now.

@keremkusmezer
Copy link

The issue persist on 8.2.0 as well.

@bytemtek
Copy link

Still same +

@etothepowerofitimespiequalsminusone
Copy link

etothepowerofitimespiequalsminusone commented Sep 25, 2024

Issue is still present in 8.2.1. Patching the PaywallFooterViewManager.kt (as described in #994 (comment)) does fix the crash. So far haven't noticed any side-effects for my use case, however I am pretty sure that in some cases this is not a good way to fix this.

@keremkusmezer
Copy link

Hey There,

Glad to see that this was fixed and things are now working as planned. On a side note, we have updated the SDK which has had more bug improvements since this, so I recommend updating to the latest version to ensure you have all the fixes that we have put out since this.

Hey there the issue still persist, any official fix planning for this?

@keremkusmezer
Copy link

@michaelAtRC Hi Michael,
As you can see to issue still persists and we are applying patching to the source code, any chance that it will be picked up and reopened?

@Mustafax06
Copy link

Are there any updates on this?

@bytemtek
Copy link

Is there any update about this issue @michaelAtRC ?

@niek-dewit
Copy link

Could someone with knowledge about this codebase check out this issue? @vegaro @michaelAtRC @aboedo
This seems like a significant issue in the SDK affecting many users since at least the beginning of may, with a reproducible example, and a hotfix. Would not be great if we all have to create a fork of the repo just to comment out one line of code.

Seems like the code that is causing this issue was added as a workaround as well facebook/react-native#17968 (comment)
Which was related to auto-sizing content.
Not sure if having some auto-sizing content functionality is worth the trade-off to not being able to use this component at all ;)

@keremkusmezer
Copy link

Still the same for me as well, I applied patch on the library and when I mount it to the navigator , I mount it cleaning the stack so it draws correctly before display, but as I use user prop based discounts after entering a discount code, if the paywall footer refreshes himself after that, the layout still gets out of order. It constantly persists even with the newest sdk release.

@bytemtek
Copy link

It's really so interesting no one care this issue.

@vegaro vegaro reopened this Oct 30, 2024
@vegaro
Copy link
Contributor

vegaro commented Oct 30, 2024

Sorry about the management of this issue. We are taking a look.

@tonidero
Copy link
Contributor

Hi everyone, same here, sorry for missing this issue. We were able to reproduce and find the cause of the issue. We have a fix in #1120 which should go out on the next release.

Again sorry and thanks for reporting this issue!!

tonidero added a commit that referenced this issue Oct 30, 2024
This fixes
#994

Basically when the footer is dismissed within the same view, it triggers
a new layout, which since we call a remeasurement with a `post` call,
this remeasurement may happen after the view has been removed from the
window. This view requires access to the context to be able to obtain
the `ViewModel` in the underlying composable, causing a crash.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.