1

I have a question regarding usage of Google APIs (in my case NavigationAPI) in clean architecture.

So in order to use google navigation it is necessary to have Navigator object from NavigationApi.getNavigator API. Initially I thought that it’s ok to put all necessary implementation in DATA layer, however this layer is supposed to only hold and provide access to data (local, remote). But then Navigator is highly dependent on SupportNavigationFragment which is in presentation layer. As we know, lower layers shouldn’t depend on higher layers.

So next I thought that all mentioned implementation should be moved to fragment, but I am not 100% sure if this is correct even if it sounds reasonable. Does anyone have any experience with google api in clean arch?

Functionality that I need:


private var navigator: Navigator? = null

override fun initializeNavigator(activity: FragmentActivity) {
    NavigationApi.getNavigator(activity, object : NavigationApi.NavigatorListener {
        override fun onNavigatorReady(navigator: Navigator?) {
            navigator = navigator
            addListeners()
        }

        override fun onError(errorCode: Int) {
            handleError(errorCode)
        }
    })
}

override fun startNavigation() {
    navigator?.setAudioGuidance(Navigator.AudioGuidance.VOICE_ALERTS_AND_GUIDANCE)
    navigator?.startGuidance()
}

override fun stopNavigation() {
    navigator?.stopGuidance()
    navigator?.clearDestinations()
}


override fun getRouteSummary(waypoint: Waypoint, options: RoutingOptions) = callbackFlow {
    navigator?.let { navigator ->
        val pendingRoute = navigator.setDestination(waypoint, options)
        pendingRoute.setOnResultListener { code ->
            _navigationStateFlow.value = NavigationState.SUMMARY
            val result = trySend(
                NavigationSummary(
                    response = code,
                    meters = navigator.currentTimeAndDistance.meters,
                    seconds = navigator.currentTimeAndDistance.seconds
                )
            )
        }

        awaitClose {
        …
        }
    }
}

private fun addListeners() {
    val arrivalListener = Navigator.ArrivalListener {
        navigator?.clearDestinations()
    }
    navigator?.addArrivalListener(arrivalListener)

    val routeChangedListener = Navigator.RouteChangedListener {
        …
    }
    navigator?.addRouteChangedListener(routeChangedListener)
}

private fun handleError(errorCode: Int) {
    when (errorCode) {
        NavigationApi.ErrorCode.NOT_AUTHORIZED -> {
            …
        }

        NavigationApi.ErrorCode.TERMS_NOT_ACCEPTED -> {
            ...
        }

        NavigationApi.ErrorCode.NETWORK_ERROR -> {
            …
        }

        NavigationApi.ErrorCode.LOCATION_PERMISSION_MISSING -> {
            …
        }

        else -> {
            ...
        }
    }
}
2
  • "Navigator is highly dependent on SupportNavigationFragment" -- I do not see much of a dependency in the API for SupportNavigationFragment or the API for Navigator. Also, SupportNavigationFragment is optional AFAICT. You might wish to explain, in detail, the nature of the dependency that worries you. Commented Jan 12 at 13:02
  • Hi @CommonsWare, thanks for replay! By saying highly dependent I mean that initialization of navigator needs activity as parameter (there is also possibility to pass application instead, didn’t check that yet) and also, setting the destination (via navigator.setDestination(waypoint, options)) has the effect of drawing a path on the map. Commented Jan 12 at 13:22

1 Answer 1

0

Quoting the docs:

Note that Navigator is a singleton; if you call this method multiple times, each call will return the same Navigator.

As a result, IMHO, there is no reason to have the Navigator be part of the UI layer. I would have my own singleton manager that wraps the Navigator to bridge between the Navigator API and the internal needs of my app. I would have that manager implement an interface describing its API, so I can create test fakes for it.

In terms of your specific concerns:

initialization of navigator needs activity as parameter (there is also possibility to pass application instead, didn’t check that yet)

Off the cuff, I would use the Application approach.

setting the destination (via navigator.setDestination(waypoint, options)) has the effect of drawing a path on the map

The result of a Web service call has the effect of updating your UI. This does not mean that you make the Web service call directly from the UI layer. Similarly, the fact that Navigator triggers UI updates does not mean that the Navigator should be part of the UI layer.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.