<@UHAJKUSTU> What would be the correct way to hand...
# decompose
u
@Arkadii Ivanov What would be the correct way to handle deeplink from native code with retained component. I keep getting
Copy code
java.lang.IllegalArgumentException: SavedStateProvider with the given key is already registered
This is possibly because of retainedComponent trying to recreate using the same key. The trouble is Android allows sending deeplink intent from onNewIntent as well as onCreate. Adding code in thread
App.android.kt
Copy code
@OptIn(ExperimentalDecomposeApi::class)
fun AppCompatActivity.setUpMedialApp(
    modifier: Modifier = Modifier,
    deeplink: String? = null
) {
    val rootComponent = retainedComponent {
        RootComponentImpl(it, deeplink)
    }

    setContent {
        MedialApp(
            darkTheme = isSystemInDarkTheme(),
            dynamicColor = true,
            component = rootComponent,
            modifier = modifier
        )
    }

}
This is how I am calling it from Android native
Copy code
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        KoinConfig(this).initKoin()
        Firebase.initialize(this)
        checkForUpdates()
        content(intent)
        ExceptionHelper()
    }


    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        content(intent)
    }

    private fun content(intent: Intent? = null) {
        setUpMedialApp(deeplink = getDeeplink(intent))
    }
a
This is possibly because of retainedComponent trying to recreate using the same key.
This looks like the cause,
setUpMedialApp
is being called twice. Usually (without retained components) there are two ways of handling deeplinks. 1. In
onNewIntent
, call
setIntent(intent)
and then call
recreate()
. And in
onCreate
you can call
defaultComponentContext(discardSavedState = deepLink != null)
, where
deepLink
is a deeplink parsed from the Activity intent. 2. In
onNewIntent
, call just call a
root
component's method and pass the deeplink there. The
root
component then just navigates to the required state. The first option is preferable from my point of view. But some people actually prefer the second variant. But since you are using retained components, currently there is no way of using the option #1 (there is no
discardSavedState
argument in
retainedComponent
function), only option #2 is possible. I think I could add
discardSavedState
to
retainedComponent
function. What do you think?
u
Ahhh..this is precisely what I wanted to ask. How to discard saved state in retained component. I believe Android deeplink handling mandates this functionality considering retainedComponent will be the goto way of porting VM's to decompose.
Regarding option 2. I have my concerns with exposing RootComponent methods to native code. I might be wrong though but that sounds like a hack.
a
Cool. I will try to update
retainedComponent
API. Filed https://github.com/arkivanov/Decompose/issues/588
u
Thanks a lot. Kudos to you for being super active
decompose intensifies 1
@Vaibhav Jaiswal
@Arkadii Ivanov any timeline on this? Resuming app from app icon while it's in the background also results in a crash. I think this again because of retainedComponent being recreated.
a
I think the app shouldn't crash when you start it from background, and the Root component shouldn't be recreated. It should just resume as usual.
Why are you trying to recreate the component in this case?
u
It's the same as the code above. I think onNewIntent is getting called when resume app from app icon
What should I do in that case?
a
Perhaps just ignore and do nothing?
Otherwise the entire navigation state will be reset
I think you can call setIntent(intent), and then if there is no deeplink - just return from onNewIntent and do nothing.
Anyway, if you really need the fix, you can temporarily copy it from here: https://github.com/arkivanov/Decompose/pull/594
u
Thanks will take a look
Will update here on how that goes.
👍 1
v
@Arkadii Ivanov This is not working
discardSavedState
It still crashes with the same error
a
You might be using it incorrectly. This is supposed to be used only in onCreate.
I will document this explicitly: https://github.com/arkivanov/Decompose/issues/new
v
@Arkadii Ivanov I changed my
onNewIntent()
to this
Copy code
override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        setIntent(intent)
        recreate()
    }
Now its not crashing, but navigation is reset Checking if deeplink is there or not and doing nothing will handle the scenario when app is minimized and opened
a
Yes. I think recreate is only needed if there is a new deeplink. If no deeplink, then just don't recreate.
I will document this as well, thanks.
🙌 2
u
Thanks a lot @Arkadii Ivanov
🙏 1
v
@Arkadii Ivanov There's a small issue when using this, when we change device theme (dark to light or vice versa) when using app, as onCreate is called again RootComponent gets created again hence nav state is lost Do you know of any workaround?
a
I have updated the docs with samples some time ago. I think it should help. https://arkivanov.github.io/Decompose/navigation/stack/deeplinking/#handling-deep-links-since-v300-alpha01
v
Thanks, will try it out
👍 1
a
Decompose 3.0.0-alpha05 is released with the changes in
retainedComponent
function.
u
Thanks a lot @Arkadii Ivanov. Will be adding this in our next release
👍 1
@Arkadii Ivanov I was unable to solve the scroll vs drag pointer input problem. But I did notice an interesting caveat that webview page prevents swiping and hogs the scroll event to itself. Can something similar be achieved with scrollable components?
a
I doubt that. If you really want fullscreen back swiping, I would recommend trying to place the overlay under the content, and don't use Surfaces for your screens. Otherwise, I don't think there is an easy way to achieve this. Maybe also ask in #compose channel - like how we can place a touch interceptor on top of the content and only react to touch events that are not being processed by a widget underneath? Currently it seems that scrollable widgets don't consume the events, and the events continue their way through other widgets.