Phase 4 · Data & PersistenceModule 23~32 min read

Networking with Retrofit

Call REST APIs and parse JSON with Retrofit and coroutines — connecting your app to the world.

What you'll learn

Most apps talk to a server — a weather feed, a product catalog, a social API. Retrofit is the standard Android library for calling REST APIs: you describe the endpoints as a Kotlin interface, and it handles the HTTP and JSON for you. Combined with coroutines, a network call is just a suspend function.

By the end you'll be able to:

  • Understand REST endpoints and JSON
  • Model API responses as serializable data classes
  • Declare an API with Retrofit annotations
  • Build Retrofit and handle success and error responses

REST & JSON

A REST API exposes data at URLs (endpoints) you interact with using HTTP methods: GET to read, POST to create, PUT/PATCH to update, DELETE to remove. The server replies with JSON — human-readable text of objects and arrays — plus a status code (200 OK, 404 Not Found, 500 Server Error). Your job: send the right request and turn the JSON reply into Kotlin objects.

Data models

Create a data class that mirrors the JSON. Mark it @Serializable so kotlinx.serialization can parse it, and use @SerialName when a JSON field name doesn't match your Kotlin property. These network classes are often suffixed Dto (Data Transfer Object):

ArticleDto.kt
// Mirror the JSON shape. @Serializable enables kotlinx parsing.
@Serializable
data class ArticleDto(
    val id: Int,
    val title: String,
    @SerialName("published_at") val publishedAt: String,   // map a JSON name
)

Tip

Keep network DTOs separate from your app's domain models, and map between them. The API's shape can be messy or change; a mapping function is your buffer, so a server tweak doesn't ripple through your whole app.

The API interface

Describe each endpoint as a suspend function annotated with its HTTP method and path. @Path fills in a URL segment; @Query adds a ?key=value parameter. Retrofit implements the interface for you:

NewsApi.kt
interface NewsApi {
    @GET("v2/top-headlines")
    suspend fun getHeadlines(
        @Query("country") country: String = "us",
    ): List<ArticleDto>

    @GET("v2/articles/{id}")
    suspend fun getArticle(@Path("id") id: Int): ArticleDto
}

Building Retrofit

Configure Retrofit once with a baseUrl and a converter that knows how to parse JSON, then create your API. Setting ignoreUnknownKeys = true makes parsing resilient — the app won't crash when the server adds a field you don't model:

Network.kt
private val json = Json { ignoreUnknownKeys = true }   // don't crash on extra fields

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
    .build()

val api: NewsApi = retrofit.create(NewsApi::class.java)

Calling & handling errors

Networks fail — no signal, timeouts, server errors — so every call needs error handling. Wrap it in try/catch and return a Result (or your own sealed outcome type) so the ViewModel can turn failures into a friendly UI state rather than a crash:

NewsRepository.kt
suspend fun fetchHeadlines(): Result<List<Article>> =
    try {
        val dtos = api.getHeadlines()          // suspends on a background thread
        Result.success(dtos.map { it.toDomain() })   // map DTO → your model
    } catch (e: IOException) {
        Result.failure(e)                      // no connection / timeout
    } catch (e: HttpException) {
        Result.failure(e)                      // 4xx / 5xx response
    }

Watch out

Two things every real app needs: add the INTERNET permission in the manifest (network calls fail silently without it), and never assume a call succeeds. The two exceptions to expect are IOException (no connection) and HttpException (a non-2xx response).

Recap & quick check

Key takeaways

  • REST APIs expose data at URL endpoints using HTTP methods (GET/POST/…), replying with JSON and a status code.
  • Model responses as @Serializable data classes (DTOs); use @SerialName to map mismatched field names.
  • Declare endpoints as suspend functions with @GET/@POST, @Path, and @Query annotations.
  • Build Retrofit once with a baseUrl and a JSON converter; ignoreUnknownKeys makes parsing resilient.
  • Always handle IOException and HttpException; map DTOs to domain models and return a Result.

Quick check

1. Which HTTP method reads data?

2. How do you declare a Retrofit endpoint that fetches headlines?

3. Why keep DTOs separate from domain models?

4. What does ignoreUnknownKeys = true do?

5. Which exceptions should you catch around a Retrofit call?

You can read local and remote data. The final Phase 4 piece ties them together cleanly — the repository pattern.