Why is my retrofit model returns null? Android Kotlin

So I'm trying to use themoviedb for extracting search results for movies. The url is as follows:

https://api.themoviedb.org/3/search/movie?api_key={apikey}&language=en-US&query={query}

Where in the query I insert the keyword that I want to search. I'm using retrofit library to do this.

This is my code for my ApiService:

interface ApiService {
    @GET("3/search/movie?api_key=${BuildConfig.MOVIE_TOKEN}&language=en-US&")
    fun getMovies(
        @Query("query") query: String
    ): Call<SearchMovieResponse>
}

This is my code for the ApiConfig object:

class ApiConfig {
companion object {
    fun getApiService(): ApiService{
        val loggingInterceptor =
            HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
        val client = OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .build()
        val retrofit = Retrofit.Builder()
            .baseUrl("https://api.themoviedb.org/")
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build()
        return retrofit.create(ApiService::class.java)
    }
}

}

I also have a RemoteDataSouce class which uses that config to get the movies. I have also generated the data class to using POJO. This is the method in the RemoteDataSource class that uses that the API config.

fun getMovies():List<MoviesItem>?{
    val client = ApiConfig.getApiService().getMovies("john")
    var listMovies: ArrayList<MoviesItem> = ArrayList<MoviesItem>()
    client.enqueue(object: Callback<SearchMovieResponse> {
        override fun onResponse(call: Call<SearchMovieResponse>, response: Response<SearchMovieResponse>) {
            if (response.isSuccessful){
                val rawList = response.body()?.results!!
                for (item in rawList){
                    listMovies.add(item)
                }
            }
        }
        override fun onFailure(call: Call<SearchMovieResponse>, t: Throwable) {
            return
        }

    })
    return listMovies
}

The json response of the API is this: JSON

The data model that I use for SearchMovieResponse is this:

data class SearchShowResponse(

@field:SerializedName("page")
val page: Int? = null,

@field:SerializedName("total_pages")
val totalPages: Int? = null,

@field:SerializedName("results")
val results: List<ShowsItem?>? = null,

@field:SerializedName("total_results")
val totalResults: Int? = null
)

data class ShowsItem(

@field:SerializedName("first_air_date")
val firstAirDate: String? = null,

@field:SerializedName("overview")
val overview: String? = null,

@field:SerializedName("original_language")
val originalLanguage: String? = null,

@field:SerializedName("genre_ids")
val genreIds: List<Int?>? = null,

@field:SerializedName("poster_path")
val posterPath: String? = null,

@field:SerializedName("origin_country")
val originCountry: List<String?>? = null,

@field:SerializedName("backdrop_path")
val backdropPath: String? = null,

@field:SerializedName("original_name")
val originalName: String? = null,

@field:SerializedName("popularity")
val popularity: Double? = null,

@field:SerializedName("vote_average")
val voteAverage: Double? = null,

@field:SerializedName("name")
val name: String? = null,

@field:SerializedName("id")
val id: Int? = null,

@field:SerializedName("vote_count")
val voteCount: Int? = null
)

However, the listMovies is returning null. I'm not sure what I did wrong here. Can anyone explain? Thanks

Upvotes: 1

Views: 2146

Answers (3)

shmakova
shmakova

Reputation: 6426

Try to use callback to return your list:

fun getMovies(callback: (List<MoviesItem>) -> Unit) {
    val client = ApiConfig.getApiService().getMovies("john")
    client.enqueue(object : Callback<SearchMovieResponse> {
        override fun onResponse(
            call: Call<SearchMovieResponse>,
            response: Response<SearchMovieResponse>
        ) {
            var listMovies: ArrayList<MoviesItem> = ArrayList<MoviesItem>()
            if (response.isSuccessful) {
                val rawList = response.body()?.results!!
                for (item in rawList) {
                    listMovies.add(item)
                }
            }
            callback(listMovies)
        }

        override fun onFailure(call: Call<SearchMovieResponse>, t: Throwable) {
            callback(emptyList()) // or throw error or use Result structure
        }

    })
}

Upvotes: 1

ahmad bajwa
ahmad bajwa

Reputation: 1068

As, you are using enqueue() that run asynchronous so your function finish before the onResponse() method is called. So you have to return the list after on the complete of the process.

 fun getMovies():List<MoviesItem>?{
        val client = ApiConfig.getApiService().getMovies("john")
        var listMovies: ArrayList<MoviesItem> = ArrayList<MoviesItem>()
        client.enqueue(object: Callback<SearchMovieResponse> {
            override fun onResponse(call: Call<SearchMovieResponse>, response: Response<SearchMovieResponse>) {
                if (response.isSuccessful){
                    val rawList = response.body()?.results!!
                    for (item in rawList){
                        listMovies.add(item)
                    }
                  return listMovies
    
                }
            }
            override fun onFailure(call: Call<SearchMovieResponse>, t: Throwable) {
                return
            }
    
        })
    }

Upvotes: 1

Bogdan Android
Bogdan Android

Reputation: 1395

Your method getMovies() is returning the list before the Retrofit call is done, you are using enqueue() method that run it asynchronous so your method finish before the onResponse() method is called.

Solution, rewrite your code thinking about this information or use execute()method instead enqueue(), this will execute the call in the main thread so you will have to call it in a new thread or a coroutine.

Upvotes: 3

Related Questions