https://kotlinlang.org logo
#decompose
Title
# decompose
a

Anshulupadhyay03

03/23/2024, 2:19 PM
Hi Fellas, I have been facing some issue while doing navigation.push to a screen on iOS. In Android on item click I am doing navigation.push to detailScreen which is working fine however the same code(as i am using compose multiplatform for both iOS and Android ) is not navigating to the detailScreen for iOS. I have debugged that for iOS it doesn't call onComplete callback but for Android it does. decompose lib version : decompose = "3.0.0-alpha07"
Copy code
private fun onMovieSelected(movieId: Int) {
        println("item onMovieSelected-$movieId")
        navigation.push(Configuration.Details(movieId), onComplete = {
            println("Ansh: isMovieSelected ")
        })
    }
main class for iOS
Copy code
fun MainViewController(lifecycle: LifecycleRegistry) = ComposeUIViewController {
    CompositionLocalProvider{
        val root = MovieBuffRootImpl(DefaultComponentContext(lifecycle = lifecycle))
        App(root)
    }
}
MovieBuffRootImpl : https://pastebin.com/rTBU8Zax
a

Arkadii Ivanov

03/23/2024, 2:34 PM
There are not enough details to say what's wrong. The related code is the same for all platforms, so it should work fine. A reproducer could help.
a

Anshulupadhyay03

03/23/2024, 2:51 PM
@Arkadii Ivanov thanks for your response. I have edited the question with more details . let me know if you want more info.
a

Arkadii Ivanov

03/23/2024, 2:59 PM
Thanks! I don't see any problems with the code. The completion callback should always be called. Would it be possible to create a reproducer?
a

Anshulupadhyay03

03/23/2024, 3:06 PM
@Arkadii Ivanov If you dont see any basic issue the code then i will have to do some debugging from my end and come back to you with more concrete information.
a

Arkadii Ivanov

03/23/2024, 3:11 PM
Yeah, the code looks good. There might be a bug in Decompose, though. Feel free to share a reproducer.
Actually, I have just realised that you are creating the root component and the component context incorrectly. You have to create those outside of Compose. Please see the doc: https://arkivanov.github.io/Decompose/component/overview/#root-componentcontext-in-jetpackjetbrains-compose
a

Anshulupadhyay03

03/30/2024, 1:24 PM
@Arkadii Ivanov Thanks for the suggestion however i found the solution bit different. I moved the code out of the compose but that did not work either but when I pass the LifecycleRegirty() inside the ComponentContext rather than taking it from the iOS code, it started working. But I am not sure what was going wrong with it . Please explain if you understood. This is what works :
Copy code
fun MainViewController(lifecycle: LifecycleRegistry) = ComposeUIViewController{
    val root = MovieBuffRootImpl(DefaultComponentContext(lifecycle = LifecycleRegistry()))
    App(root)
}
This is the ios code where I was passing lifecycle instance
Copy code
struct ComposeView: UIViewControllerRepresentable {
    private var lifeCycleHolder:LifeCycleHolder {LifeCycleHolder()}
    func makeUIViewController(context: Context) -> UIViewController {
        Main_iosKt.MainViewController(lifecycle:lifeCycleHolder.lifecycle)
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}

struct ContentView: View {
    var body: some View {
        ComposeView()
                .ignoresSafeArea(.keyboard) // Compose has own keyboard handler
    }
}
and here it is the LifecycleHolder class
Copy code
class LifeCycleHolder : ObservableObject {
    
    let lifecycle:LifecycleRegistry
    
    init(){
        lifecycle = LifecycleRegistryKt.LifecycleRegistry()
        
        lifecycle.onCreate()
    }
    
    deinit {
        lifecycle.onDestroy()
    }

}
a

Arkadii Ivanov

03/30/2024, 2:45 PM
Thanks! Your
MainViewController
function appears to be incorrect as well. You shouldn't call
MovieBuffRootImpl(DefaultComponentContext(lifecycle = LifecycleRegistry()))
inside a Composable function, as explained in the docs. Keep in mind that
ComposeUIViewController {}
lambda block is Composable. You can do something like this:
Copy code
fun MainViewController(lifecycle: Lifecycle): UIViewController {
  val root = MovieBuffRootImpl(DefaultComponentContext(lifecycle = lifecycle))
  return ComposeUIViewController { App(root) }
}
a

Anshulupadhyay03

03/30/2024, 4:04 PM
@Arkadii Ivanov i am not sure if you understood what I mentioned in my above reply. It is possible that my MainViewController is not correct however moving root outside of ComposeUIViewController did not work either. What actually worked was passing
LifecycleRegistry()
instead of lifecycle passed from my iOS lifecycle
MainViewController(lifecycle: Lifecycle)
and I am clueless what was the problem as earlier same code was working fine for older versions of decompose. This is the code that works
Copy code
fun MainViewController(): UIViewController {
    val root = MovieBuffRootImpl(DefaultComponentContext(lifecycle = LifecycleRegistry()))
    return ComposeUIViewController {
        App(root)
    }
}
a

Arkadii Ivanov

03/30/2024, 4:08 PM
I can't tell what was the problem without a reproducer, unfortunately. But with your current approach you should also control LifecycleRegistry somehow. Otherwise your components won't receive any lifecycle events.
a

Anshulupadhyay03

03/30/2024, 5:17 PM
@Arkadii Ivanov Yes makes sense ....You can checkout this https://github.com/youranshul/KmmMovieBuff/tree/decompose_navigation ...If you want I can share the api_key separately as this is just a learning app.
a

Arkadii Ivanov

03/30/2024, 10:31 PM
Thanks for the reproducer! Turned out our LifecycleHolder is garbage collected, and so the lifecycle is destroyed. If you want to use this "Holder" pattern, then please follow this doc: https://arkivanov.github.io/Decompose/getting-started/quick-start/#ios-with-swiftui-without-the-experimental-applicationlifecycle. Alternatively, since you have only one root component and it lives in the app scope, you can use
ApplicationLifecycle
instead: https://arkivanov.github.io/Decompose/getting-started/quick-start/#ios-with-swiftui-with-the-experimental-applicationlifecycle
a

Anshulupadhyay03

03/31/2024, 10:12 AM
@Arkadii Ivanov Thanks for the response. Is this happening because I bumped up my decompose library to use it for wasm as in earlier version which I used was all good and it was working fine ?
a

Arkadii Ivanov

03/31/2024, 10:21 AM
I don't think so. SwiftUI views are structs, a view struct is recreated on each view update. So any class stored as a property is also recreated, and the previous instance is deallocated. Storing objects in a view requires some extract care, e.g. using StateObject. Or just using the vanilla
UIApplicationDelegate
as described here. Or just use the new
ApplicationLifecycle
, as it should work perfectly fine for your use case (one component hierarchy for the entire app).
a

Anshulupadhyay03

03/31/2024, 10:24 AM
@Arkadii Ivanov Thanks a lot!
👍 1
7 Views