Home > Net >  Not able to return variable from inside a function
Not able to return variable from inside a function

Time:08-29

So i'm trying to return a value from a function to use inside the mainActivity class, but I get an error when I try to return the variable stating that the 'variable must be initialized' even though I have given a value to it. Any thoughts on this?

private fun getCLocation() : LocationClass {

        var loc: LocationClass

        if(checkPermission()){

            if(isLocationEnabled()){

                locationVariable.lastLocation.addOnCompleteListener(this) { task->

                    val location:Location? = task.result

                    if(location == null){
                        Toast.makeText(this,"NULL",Toast.LENGTH_LONG).show()
                    }
                    else{
                        loc = LocationClass(location.latitude.toString(),location.longitude.toString())
                    }
                }

            } else {
                // location not enabled,open settings
                val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
                startActivity(intent)
            }

        } else {
            //Permission Not Enabled
            requestPermission()
        }

        return loc
    }

Error is: Variable 'loc' must be initialized

CodePudding user response:

The two problems with your code:

  1. The user may not have granted the permission yet, so it would enter the else block, where the function requests a permission, but doesn't do anything to set the initial location value. The function couldn't possibly return a valid location in this circumstance.
  2. Getting location updates is asynchronous. Even if the permission is already granted, your OnCompleteListener will not be called until some time in the future, after this function has already returned. You can read more explanations of what asynchronous APIs are here.

Here's a basic strategy for what to do. The function that gets a location takes a callback parameter instead of returning the LocationClass directly since it is asynchronous. You need a higher-level logic that can be retried after location or permissions become available.

private var triedPromptingLocationSetting = false
private var locationWorkflowInitiated = false // SET THIS BACK TO FALSE IN onViewDestroyed() IF IN FRAGMENT

private fun fetchCLocation(onReceived: (LocationClass?)->Unit) {
    if (!checkPermission() || isLocationEnabled()) {
        Log.e("Do not call getCLocation() before permission is granted and location is turned on! Ignoring.")
        return
    }
    locationVariable.lastLocation.addOnCompleteListener(this) { task->
        val location:Location? = task.result
        if (location == null) {
            onReceived(null)
        }
        else {
            var loc = LocationClass(location.latitude.toString(),location.longitude.toString())
            onReceived(loc)
        }
    }
}

// This is where the logic is for whatever you wanted to do with this location.
// In this example, it's assumed it would be called in onResume()
private fun doMyLocationWorkflow() {
    if (!checkPermission()) {
        requestPermission()
        return
    }
    if (!isLocationEnabled()) {
        if (triedPromptingLocationSetting) {
            // can't infinitely loop back to settings. 
            showSomeUiRequestingUserToManuallyEnableLocation()
        } else {
            triedPromptingLocationSetting = true
            val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
            startActivity(intent)
        }
        return
    }

    locationWorkflowInitiated = true

    fetchCLocation { location ->
        if (location == null) {
            Toast.makeText(this,"NULL",Toast.LENGTH_LONG).show()
            //...
            return@fetchCLocation 
        }
        // Do something with the LocationClass
    }
}

override fun onResume() {
    super.onResume()
    if (!locationWorkflowInitiated) {
        doMyLocationWorkflow()
    }
}

private val locationPermissionRequest = registerForActivityResult(
        ActivityResultContracts.RequestMultiplePermissions()
    ) { permissions ->
        when {
            permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
                doMyLocationWorkflow() // go back to original workflow
            }
            permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
                doMyLocationWorkflow() // go back to original workflow
            } else -> {
                showSomeUiTellingUserPermissionMustBeGranted()
            }
        }
    }

private fun requestPermission() {
    locationPermissionRequest.launch(arrayOf(
    Manifest.permission.ACCESS_FINE_LOCATION,
    Manifest.permission.ACCESS_COARSE_LOCATION))
}
  • Related