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

Adam Brown

03/04/2023, 7:37 AM
huh, working on my swift code a bit, trying to get some navigation going, but the UI isn't reacting to the router state updating.
Copy code
@ObservedObject
    private var routerState: ObservableValue<ChildStack<AnyObject, ProjectRootDestination<AnyObject>>>
Copy code
private val stack = componentContext.childStack(
		source = navigation,
		initialConfiguration = Config.HomeConfig(projectDef),
		key = "ProjectRootRouter",
		childFactory = ::createChild
	)

	val state: Value<ChildStack<Config, ProjectRoot.Destination<*>>>
		get() = stack

	fun showNotes() {
		navigation.bringToFront(Config.NotesConfig(projectDef = projectDef))
	}
I'm thinking it's because calling
navigation.bringToFront
isn't actually reassigning the variable maybe?
a

Arkadii Ivanov

03/04/2023, 9:43 AM
Which variable? You can check the root component in samples.
a

Adam Brown

03/04/2023, 10:46 AM
ya I was looking at counter example, but looking at the root one now, that seems much closer to what im doing here
I even was just printing out which child was active in a textview, and it prints the initial one, but it never changes as I trigger navigations
hhmmm the main difference is that I'm using a StackView maybe
a

Arkadii Ivanov

03/04/2023, 10:52 AM
Yeah, please try without StackView
a

Adam Brown

03/04/2023, 10:53 AM
even still, stackview aside, that text view isn't even updating
i just changed it to use that computed field:
Copy code
let curDest = destinationTitle(dest: activeDestination)
            Text(curDest)
but no dice
a

Arkadii Ivanov

03/04/2023, 11:04 AM
What if you remove
Text("project root")
?
Also you can try adding logs everywhere, and see where it stops propagating
a

Adam Brown

03/06/2023, 6:53 PM
im still working on this, logging on ios from kotlin wasnt working for me at first but i got that solved.
i've got it logged down to where i make the navigate call, and thats all working
a

Arkadii Ivanov

03/06/2023, 6:56 PM
Does Value emits the updated state on the Swift side?
a

Adam Brown

03/06/2023, 6:57 PM
i dont think so, but i still need to verify that
havent had a lot of time the last few days so havent been able to check much
a

Arkadii Ivanov

03/06/2023, 7:01 PM
It would be useful if you could check that. Also worth checking any errors in Xcode logs.
a

Adam Brown

03/06/2023, 9:24 PM
this should capture any update on the swift side I think:
Copy code
init(component: ProjectRoot, closeProject: @escaping () -> Void) {
        self.component = component
        self.routerState = ObservableValue(component.routerState)
        
        component.routerState.subscribe { stack in
            NSLog("swift router state update")
        }
    }
as long as thats not wrong in some way, then the update is never getting back to the swift side of things
I see the initial state update in that subscribe, but nothing after
a

Arkadii Ivanov

03/06/2023, 9:30 PM
Is the back reproducible on github?
a

Adam Brown

03/06/2023, 9:33 PM
ya, this branch is where the ios work is: https://github.com/Wavesonics/hammer-editor/tree/bbooth-dev
im guessing there is no good way to debug into kotlin code from an ios build?
wonder if its a threading issue or something...
a

Arkadii Ivanov

03/06/2023, 9:36 PM
I remember there was KMP plugin for IDEA, but I never used it.
Let me check the project
a

Adam Brown

03/06/2023, 9:37 PM
ProjectRootUi.swift
is the main swift file here
ProjectRootComponent.kt
is where the router lives
maybe I'm not initializing something, like the lifecycle stuff
huh i think it might be lifecycle related, im getting onDestroy called right after init
a

Arkadii Ivanov

03/06/2023, 9:45 PM
Yep, when the parent is destroyed, all routers unsubscribe from navigation sources (e.g.
StackNavigation
)
a

Adam Brown

03/06/2023, 9:46 PM
ah and that makes sense
a

Arkadii Ivanov

03/06/2023, 9:46 PM
You can add
lifecycle.doOnDestroy { Exception().printStackTrace() }
to identify the cause.
Let me know if you still need any help.
a

Adam Brown

03/06/2023, 9:48 PM
awesome, thanks for pointing me in the right direction!
so I think the issue is that I was creating the component holder in the UI body function, so when it updates, it creates a new component, thus destroying the old one
my use case here is a bit strange, I have two top level Components. on Desktop there are two separate windows, on android two separate Activities. on iOS they are currently just switched between based on state in the SwiftUi code it's self. So I need to find a way to create or destroy these components based on the state updates. Pretty new to both swift and swift UI, so probably just writing some hilarious naive code here
ya moved the component creation out, looks like its working now
a

Arkadii Ivanov

03/07/2023, 9:14 AM
Makes sense! You can try wrapping both roots into a separate struct that extends ObservableObject. Then put that wrapper into a separate View with StateObject annotated property. You can also expose component states from wrappers using State property.
a

Adam Brown

03/07/2023, 6:37 PM
ya that sounds like pretty much what i did, they just live in this state obj now: https://github.com/Wavesonics/hammer-editor/blob/bbooth-dev/ios/ios/Root.swift#L12
a

Arkadii Ivanov

03/07/2023, 6:40 PM
It looks like you can avoid MutableValue in your RootHolder. You can have @Published property.
a

Adam Brown

03/07/2023, 6:41 PM
oh ya good call
i have no idea what im doing in swift 😂
9 Views