Jetpack Navigation safeNavigate 확장함수

이지훈·2022년 11월 30일
0
post-custom-banner

Jetpack Navigation을 사용하다 다음과 같은 에러를 뱉고 앱이 죽은 경험을 한 적이 있다.

"Navigation action cannot be found in the current destination"

참고)
https://nezspencer.medium.com/navigation-components-a-fix-for-navigation-action-cannot-be-found-in-the-current-destination-95b63e16152e

해당 글에 의하면
같은 navigation action을 연속해서 호출해서 발생하는 문제이기 때문에 연속해서 호출되는 것을 막는 방법으로 해결할 수 있다.

이를 해결하기 위한 두 가지의 방법중 safeNavigate라는 확장함수를 만드는 방법을 추천한다.

해당 확장함수는 current destination이 존재하지 않을 경우 navigation action 이 trigger 되지 않도록 설계되었기 때문에 에러의 대한 해결 방법으로 적합하다.

safeArgs 와 연계해서 사용하면 된다.

fun NavController.safeNavigate(direction: NavDirections) {
    currentDestination?.getAction(direction.actionId)?.run { navigate(direction) }
}

fun NavController.safeNavigate(
    @IdRes currentDestinationId: Int,
    @IdRes id: Int,
    args: Bundle? = null
) {
    if (currentDestinationId == currentDestination?.id) {
        navigate(id, args)
    }
}

블로그에서 제시한 사용 예시이다.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        btn_open_screen_two.setOnClickListener {
            findNavController().safeNavigate(FragmentOneDirections.actionFragmentOneToFragmentTwo())
        }

        btn_double_click.setOnClickListener {
            performDoubleClick()
        }

    }

    private fun performDoubleClick() {
        for (i in 1 downTo 0) {
            btn_open_screen_two.performClick()
        }

    }

    fun NavController.safeNavigate(direction: NavDirections) {
        Log.d(clickTag, "Click happened")
        currentDestination?.getAction(direction.actionId)?.run {
            Log.d(clickTag, "Click Propagated")
            navigate(direction)
        }
    }

내가 사용한 방법은 다음과 같다.


override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        bind {
            vm = registerLessonViewModel
        }
        initViews()
        initObserver()
    }

    private fun initObserver() = with(registerLessonViewModel) {
        repeatOnStarted {
            launch {
                onNavigateToRegisterLessonCheckClick
                    .collect {
                        registerLessonViewModel.setLessonInfo()
                        navigateToRegisterLessonCheck()
                    }
            }

    private fun navigateToRegisterLessonCheck() {
        val action = RegisterLessonSecondFragmentDirections.actionRegisterLessonSecondToRegisterLessonCheck()
        findNavController().safeNavigate(action)
    }

sharedViewModel 을 통해 프래그먼트간의 데이터를 공유하는 방법을 사용하였기 때문에 인자를 넘겨주진 않았는데, 인자를 넘겨줄 필요가 있을 경우 safeArgs 을 이용하여 complie time에 validation을 할 수 있어 안전하게 데이터를 넘겨줄 수 있다.

profile
실력은 고통의 총합이다. Android Developer
post-custom-banner

0개의 댓글