Hi <@UHAJKUSTU> after i created the project from <...
# decompose
s
Hi @Arkadii Ivanov after i created the project from kmp.jetbrains.com without shared ui for android and iOS, And while im trying to export the dependencies of decompose to ios, its not working, I have added he following code in build.gradle :
Copy code
targets
        .filterIsInstance<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>()
        .filter { it.konanTarget.family == org.jetbrains.kotlin.konan.target.Family.IOS }
        .forEach {
            it.binaries.framework {

                export("com.arkivanov.decompose:decompose:3.0.0-alpha04")
                export("com.arkivanov.essenty:lifecycle:1.3.0")
            }
        }
But while i am opening the iOS app in XCode, its not able to resolve RootComponent and all other decompose library classes
Getting this error, but i have already specified them as apis in iosMain target:
Copy code
error: Following dependencies exported in the debugFramework binary are not specified as API-dependencies of a corresponding source set:

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':shared:linkDebugFrameworkIosSimulatorArm64'.
> Following dependencies exported in the debugFramework binary are not specified as API-dependencies of a corresponding source set:
  
  Files: [/Users/sunil/.gradle/caches/modules-2/files-2.1/com.arkivanov.essenty/lifecycle-iossimulatorarm64/1.3.0/661134f843b9fde5208c897822a737541fc5b758/lifecycle.klib]
  Files: [/Users/sunil/.gradle/caches/modules-2/files-2.1/com.arkivanov.essenty/lifecycle-iossimulatorarm64/1.3.0/661134f843b9fde5208c897822a737541fc5b758/lifecycle.klib]
  
  Please add them in the API-dependencies and rerun the build.
a
I think you need to use Essenty 2.0.0-alpha02, please try this one.
s
Hi actually i tried this version, now that error is gone, but now i am getting this error:
Copy code
/Users/sunil/AndroidStudioProjects/KMPProjectWithoutSharedUi/iosApp/iosApp/RootView.swift:13:15: error: cannot find type 'RootComponent' in scope
    let root: RootComponent
              ^~~~~~~~~~~~~
/Users/sunil/AndroidStudioProjects/KMPProjectWithoutSharedUi/iosApp/iosApp/RootView.swift:16:9: error: cannot find 'StackView' in scope
        StackView(
        ^~~~~~~~~
/Users/sunil/AndroidStudioProjects/KMPProjectWithoutSharedUi/iosApp/iosApp/RootView.swift:17:25: error: cannot find 'StateValue' in scope
            stackValue: StateValue(root.stack),
                        ^~~~~~~~~~
/Users/sunil/AndroidStudioProjects/KMPProjectWithoutSharedUi/iosApp/iosApp/RootView.swift:28:65: error: cannot find 'ListView' in scope
                case let child as RootComponentChild.ListChild: ListView(child.component)
                                                                ^~~~~~~~
/Users/sunil/AndroidStudioProjects/KMPProjectWithoutSharedUi/iosApp/iosApp/RootView.swift:29:68: error: cannot find 'DetailsView' in scope
                case let child as RootComponentChild.DetailsChild: DetailsView(child.component)
                                                                   ^~~~~~~~~~~

warning: Run script build phase 'Compile Kotlin' will be run during every build because it does not specify any outputs. To address this warning, either add output dependencies to the script phase, or configure it to run in every build by unchecking "Based on dependency analysis" in the script phase. (in target 'iosApp' from project 'iosApp')
** BUILD FAILED **
Still Its not able to resolve decompose in ios project
a
I think it's not able to resolve RootComponent. You will need to double check the naming etc. And also your views are unresolved for some reason. Can't help without a reproducer.
s
Ok thanx, let me check it once more
One question here, Using StackView in iOS is compulsory right? and that we need to copy from sample, right?
a
With Decompose, almost everything is not compulsory. StackView is absolutely not compulsory - it's just a way of binding Decompose with SwiftUI native navigation. You can also switch views manually using a regular
switch
on
stack.active.instance
. Or write something your own.
s
ok thnx
That error is sorted now and Im using the StackView now in iOS app, But navigation is not working from list to detail, no error , nothing is happening. This is my RootView:-
Copy code
struct RootView: View {
    let root: RootComponent

    var body: some View {
        StackView(
            stackValue: StateValue(root.stack),
            getTitle: {
                switch $0 {
                case is RootComponentChild.ListChild: return "List"
                case is RootComponentChild.DetailChild: return "Details"
                default: return ""
                }
            },
            onBack: { toIndex in
                root.onBackClicked()
            },
            childContent: {
                switch $0 {
                case let child as RootComponentChild.ListChild: ListView(child.component)
                case let child as RootComponentChild.DetailChild: DetailView(child.component)
                default: EmptyView()
                }
            }
        )
    }
}
and this is my RootComponent from shared module:-
Copy code
package root

import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.router.stack.push
import com.arkivanov.decompose.value.Value
import data.RandomUser
import detail.DefaultDetailComponent
import detail.DetailComponent
import kotlinx.serialization.Serializable
import list.DefaultListComponent
import list.ListComponent

interface RootComponent {

    val stack: Value<ChildStack<*, Child>>

    fun onBackClicked()

    // Defines all possible child components
    sealed class Child {
        class ListChild(val component: ListComponent) : Child()
        class DetailChild(val component: DetailComponent) : Child()
    }
}

class DefaultRootComponent(
    componentContext: ComponentContext,
) : RootComponent, ComponentContext by componentContext {

    private val navigation = StackNavigation<Config>()

    override val stack: Value<ChildStack<*, RootComponent.Child>> =
        childStack(
            source = navigation,
            serializer = Config.serializer(),
            initialConfiguration = Config.List, // The initial child component is List
            handleBackButton = true, // Automatically pop from the stack on back button presses
            childFactory = ::child,
        )

    //This will decide which component to show according to currently pushed config
    private fun child(config: Config, componentContext: ComponentContext): RootComponent.Child =
        when (config) {
            is Config.List -> RootComponent.Child.ListChild(listComponent(componentContext))
            is Config.Details -> RootComponent.Child.DetailChild(
                detailComponent(
                    componentContext,
                    config
                )
            )
        }

    private fun listComponent(componentContext: ComponentContext): ListComponent =
        DefaultListComponent(
            componentContext = componentContext,
            onItemSelected = { item: RandomUser -> // Supply dependencies and callbacks
                println("push detail")
                navigation.push(Config.Details(item = item)) // Push the details component
            },
        )

    private fun detailComponent(componentContext: ComponentContext, config: Config.Details): DetailComponent =
        DefaultDetailComponent(
            componentContext = componentContext,
            item = config.item, // Supply arguments from the configuration
            onBack = ::onBackClicked, // Pop the details component
        )

    override fun onBackClicked() {
        navigation.pop()
    }

    @Serializable // kotlin-serialize plugin must be applied if you are targeting Android
    private sealed interface Config {
        @Serializable
        data object List : Config
        @Serializable
        data class Details(val item: RandomUser) : Config
    }
}
And i can see in logs, while clicking on list item, its printing log "push detail" before navigation.push which i have printed in onItemSelected callback. But view is not changing in StackView in iOS app. @Arkadii Ivanov Any idea what can be the issue
a
All looks good. I would add more logs, e.g. check if StackView receives the updated stack. You can also refer to the working sample: https://github.com/arkivanov/Decompose/blob/master/sample/app-ios/app-ios/CountersView.swift
s
Its not logging in StackView on item click, i called print there, Only its logging there initially for ListContent, but not for when pushing DetailContent to navigation
a
s
Actually i already followed this template while i did setup of the iOS App , Not sure what is the issue with navigation
a
Would you be able to provide a complete reproducer?
s
I will try, and will let u know when done
👍 1
ONe thing which i doubt might be related to this, Actually in ObservableValue class which i copied, i m getting few errors in xcode in this line:
Copy code
self.cancellation = value.observe { [weak self] value, _ in self?.value = value } as? Cancellation
I have attached the errors screenshot. And i corrected them like this:-
Copy code
self.cancellation = value.observe(\.value) { [weak self] newValue, _ in
                self?.value = newValue as! T
            } as? any Cancellation
Not sure if this is correct or not, As i have only basic knowledge in iOS SO not sure if this issue can be causing the navigation state changes to be not detected
Not sure if you can also get this error and check if you use latest XCode version 15.1
a
Which version of Decompose are you using?
s
Copy code
3.0.0-alpha04
a
That v3.0 branch contains the updated sample for version 3.0
s
Thanku so much, It worked. I was using these helpers from master branch samples, that was the mistake. Thanku
a
Awesome! Thanks for the update.
s
BackButton Does not work if use StackView, But if i use custom view and custom back button, then it works, but no animation there ? Can we apply animation anyhow while using CustomView or can we make Back Button work in case of Stack View?
a
The back button should work, I believe it works in the sample. Same for back gestures.
Please check the sample
Or the template
Please also mind that by default it's possible for the user to go back to a specific screen, by long-tap on the back button.
s
Actually while i click on Back button, then in logs i can see, it work, but it takes me to blank screen, not the list
a
It's hard to say without the full code, sorry. Please check your implementation.
s
ok
Ok one last question, If in case we use our custom view instead of StackView, then can we specify animations anyhow?
a
I think in this case we would use whatever SwiftUI provides. I'm not an expert in SwiftUI, unfortunately.
And I would suggest to show every screen on top of the previous screen, and never actually remove the previous screen. This is to keep the UI state preserved. Because I'm not sure how we can save the state otherwise.
s
ok thanku
👍 1
a
There are some transitions, as far as I remember
s
Ok, thanku so much, i will check it🙌
👍 1