Android Retrofit enqueue method does not return value

Viewed 980
fun getSurvey() : MutableList<SurveyMaster>{
        //Retrofit creates implementation for this method getSurveyList()
        val call : Call<List<SurveyMaster>> = retrofitPlaceHolderAPI.getSurveyList("bearer "+customIDToken)

        val surveyList =   mutableListOf<SurveyMaster>()

        //enqueue - executing on the background thread, to execute asynchronously
        call.enqueue(object : Callback<List<SurveyMaster>>{
            override fun onFailure(call: Call<List<SurveyMaster>>, t: Throwable) {
                Log.e("Error", t.message)
                //Log.e("Error", t.localizedMessage)
            }

            override fun onResponse(call: Call<List<SurveyMaster>>, response: Response<List<SurveyMaster>>) {
                if (!response.isSuccessful){
                    Log.e("SurveyList Error", "code: "+response.code())
                    return
                }

                val surveys = response.body()

                for(element in surveys!!){
                    surveyList.add(element)
                }
            }
        })
        return surveyList
    }

Enqueue method runs on background thread but how to return the list inside enqueue method. The above code returns empty list. Thanks in advance

2 Answers

Like you already mentioned that enqueue will execute this API call on a background thread so your function will always return empty list as right now no response has been received from the API ,so this loop that you've mentioned:-

for(element in surveys!!){
    surveyList.add(element)
}

This loop will run later on(when response from API is received) meanwhile you've returned the list which is empty at this instant.Here you cannot directly return data from the method.

You can use callback approach using interfaces. As you're already getting the response as a list no need to create a separate list and add the data into it.

fun getSurvey(onGetSurveyListener: OnGetSurveyListener){

    val call : Call<List<SurveyMaster>> = retrofitPlaceHolderAPI.getSurveyList("bearer "+customIDToken)

    val surveyList =   mutableListOf<SurveyMaster>()

    //enqueue - executing on the background thread, to execute asynchronously
    call.enqueue(object : Callback<List<SurveyMaster>>{
        override fun onFailure(call: Call<List<SurveyMaster>>, t: Throwable) {
            Log.e("Error", t.message)
            onGetSurveyListener.onGetSurveyFailure(t.localizedMessage)
            //Log.e("Error", t.localizedMessage)
        }

        override fun onResponse(call: Call<List<SurveyMaster>>, response: Response<List<SurveyMaster>>) {
            if (!response.isSuccessful){
                Log.e("SurveyList Error", "code: "+response.code())
                return
            }
            response.body?.let{list->
               onGetSurveyListener.onGetSurveySuccess(list)
            }
        }
    })
}

interface OnGetSurveyListener {
    fun onGetSurveySuccess(surveyList:List<SurveyMaster>)
    fun onGetSurveyFailure(errorMessage: String)
}

Now here with the success and failure callbacks you can handle both cases.

That is an asynchronous call so onResponse is called after the network calls succeeded and you are returning the list before that. You need to return the data using callbacks. Modify your function like this:

fun getSurvey(onComplete: (MutableList<SurveyMaster>) -> Unit) {
    //Retrofit creates implementation for this method getSurveyList()
    val call : Call<List<SurveyMaster>> = retrofitPlaceHolderAPI.getSurveyList("bearer "+customIDToken)
    
    val surveyList =   mutableListOf<SurveyMaster>()
    
    //enqueue - executing on the background thread, to execute asynchronously
    call.enqueue(object : Callback<List<SurveyMaster>>{
        override fun onFailure(call: Call<List<SurveyMaster>>, t: Throwable) {
            Log.e("Error", t.message)
            //Log.e("Error", t.localizedMessage)
        }
        
        override fun onResponse(call: Call<List<SurveyMaster>>, response: Response<List<SurveyMaster>>) {
            if (!response.isSuccessful){
                Log.e("SurveyList Error", "code: "+response.code())
                return
            }
            
            val surveys = response.body()
            
            for(element in surveys!!){
                surveyList.add(element)
            }
            onComplete.invoke(surveyList)
        }
    })
}

And call your method like this:

    getSurvey { list ->
      // Do something with your list.
    }

Depending on your problem you may also want to return something onFailure event to be aware if the network request did not succeed.

Related