Adapter — What It Is, the Interface Transformation Pattern for Mobile Development

Author: IT Sectr Published: 2026-02-18 Reading time: 10 min

Adapter is a structural design pattern that converts the interface of one class into another interface expected by the client. In mobile development, Adapter is most often used in RecyclerView.Adapter for Android and UITableViewDataSource for iOS. According to Google I/O (2024), RecyclerView is used in 95% of Android apps to display lists, and each one requires its own Adapter implementation.

Key Takeaways

  • Adapter is a structural pattern that allows objects with incompatible interfaces to work together
  • RecyclerView.Adapter is an implementation of the pattern in Android, converting data into ViewHolders for the list
  • UITableViewDataSource is an implementation in iOS, combining the Adapter and DataSource patterns
  • Object Adapter uses composition: the adapter holds a reference to the adaptee object
  • Class Adapter uses multiple inheritance — only possible in C++, not available in Java/Swift/Kotlin

What Is the Adapter Pattern?

Adapter is a structural pattern that solves the problem of interface incompatibility. It wraps one object (Adaptee) into a class (Adapter) with the interface expected by the client (Target). The client works with Target without knowing about the existence of Adaptee. This is a variant of the Wrapper pattern.

Kotlin
// Existing class with incompatible interface
class LegacyAuthApi {
    fun loginWithToken(token: String): Map {
        return mapOf("status" to "ok", "user_id" to 42)
    }
}

// Target interface (what the client expects)
interface AuthService {
    suspend fun login(credentials: Credentials): Result
}

// Adapter — transforms LegacyAuthApi into AuthService
class LegacyAuthAdapter(
    private val legacyApi: LegacyAuthApi
) : AuthService {
    override suspend fun login(credentials: Credentials): Result {
        val token = "${credentials.login}:${credentials.password}"
            .encodeToByteArray().let { Base64.encodeToString(it) }
        val response = legacyApi.loginWithToken(token)
        return if (response["status"] == "ok") {
            Result.success(User(response["user_id"] as Int))
        } else {
            Result.failure(AuthException("Login failed"))
        }
    }
}

LegacyAuthAdapter converts the login(credentials) call into loginWithToken(token) for the old API. The client (ViewModel) works through the AuthService interface and does not know whether it is using the latest Firebase Auth or a decade-old legacy API underneath. This allows replacing implementations without changing the client code.

Adapter in Android: RecyclerView and ListAdapter

RecyclerView.Adapter is the most common implementation of the Adapter pattern in Android. It converts data (a list of objects) into ViewHolders that RecyclerView displays on screen. With the introduction of ListAdapter (Android Architecture Components), the pattern gained built-in diff calculation support for animating changes.

Kotlin
class UserAdapter(
    private val onItemClick: (User) -> Unit
) : ListAdapter(DiffCallback()) {

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): UserViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_user, parent, false)
        return UserViewHolder(view)
    }

    override fun onBindViewHolder(
        holder: UserViewHolder,
        position: Int
    ) {
        holder.bind(getItem(position), onItemClick)
    }

    class UserViewHolder(itemView: View) :
        RecyclerView.ViewHolder(itemView) {
        
        private val tvName = itemView.findViewById(R.id.tvName)
        
        fun bind(user: User, click: (User) -> Unit) {
            tvName.text = user.name
            itemView.setOnClickListener { click(user) }
        }
    }

    private class DiffCallback : DiffUtil.ItemCallback() {
        override fun areItemsTheSame(old: User, new: User) = old.id == new.id
        override fun areContentsTheSame(old: User, new: User) = old == new
    }
}

UserAdapter uses ListAdapter with DiffUtil for efficient list redrawing. When the data is updated, DiffCallback calculates the difference between the old and new list, and RecyclerView animates only the changed items — without redrawing the entire list. This is a key advantage over the older ListView, which required manual notifyDataSetChanged().

Adapter in iOS: UITableView and DiffableDataSource

UITableViewDataSource is both the Adapter pattern and the DataSource pattern simultaneously. It converts model data into table cells. Since iOS 13, Apple introduced DiffableDataSource — a modern replacement that, like ListAdapter in Android, automatically calculates changes and animates updates.

Swift
class UserTableViewAdapter: NSObject, UITableViewDataSource {
    var items: [User] = []
    
    func tableView(_ tableView: UITableView,
                    numberOfRowsInSection section: Int) -> Int {
        items.count
    }
    
    func tableView(_ tableView: UITableView,
                    cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(
            withIdentifier: "UserCell", for: indexPath)
        let user = items[indexPath.row]
        cell.textLabel?.text = user.name
        return cell
    }
}

// Modern version: DiffableDataSource
enum Section { case main }

class ModernUserAdapter {
    var dataSource: UITableViewDiffableDataSource<Section, User>!
    
    func configure(for tableView: UITableView) {
        dataSource = UITableViewDiffableDataSource(
            tableView: tableView
        ) { tableView, indexPath, user in
            let cell = tableView.dequeueReusableCell(
                withIdentifier: "UserCell", for: indexPath)
            cell.textLabel?.text = user.name
            return cell
        }
    }
    
    func update(with users: [User]) {
        var snapshot = NSDiffableDataSourceSnapshot<Section, User>()
        snapshot.appendSections([.main])
        snapshot.appendItems(users)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

ModernUserAdapter with DiffableDataSource automatically animates changes when data is updated: items appear, disappear, or move with smooth animations. iOS DiffableDataSource and Android ListAdapter solve the same problem with different APIs but the same logic — diff calculation + automatic animation.

Object Adapter vs Class Adapter

Object Adapter uses composition: the adapter holds a reference to the Adaptee object and delegates calls to it. Class Adapter uses inheritance: the adapter inherits Adaptee and implements the target interface simultaneously. In Java, Swift, and Kotlin, Class Adapter is impossible due to the lack of multiple inheritance — only Object Adapter remains.

CharacteristicObject AdapterClass Adapter
MechanismComposition (contains Adaptee)Inheritance (extends Adaptee)
FlexibilityHigher — Adaptee can be changed at runtimeLower — Adaptee is fixed at compile time
CouplingLoose (via interface)Tight (via inheritance)
Subclass adaptationYes — Adaptee can be any subclassNo — only a specific superclass
AvailabilityJava, Kotlin, Swift, C++, C#Only languages with multiple inheritance (C++)

In mobile development, only Object Adapter is used. RecyclerView.Adapter holds references to data and LayoutInflater, UITableViewDataSource holds an array of items. Composition makes the code more flexible and testable compared to inheritance.

Adapter Examples in Real Projects

Adapter is used not only for lists. Let's consider three scenarios from mobile development practice where the pattern solves the problem of integrating incompatible components.

ScenarioAdapteeTargetAdapter
Integrating a legacy auth libraryLegacyAuthLib (callback-based)AuthService (suspend)LegacyAuthAdapter
Adapting JSON to a new modelGsonParserJsonParser (Kotlinx Serialization)GsonAdapter
Unified interface for push servicesFCM, APNs, Huawei PushPushService token registerPushServiceAdapter

Each Adapter encapsulates conversion logic: GsonAdapter translates a JSON string into an object via @SerializedName, PushServiceAdapter abstracts token registration for different platforms. The client (business logic) works with a unified interface and does not depend on the specific implementation.

Common Mistakes When Implementing Adapter

Mistakes in Adapter implementation lead to list lag, memory leaks, and bugs with data updates. Let's look at three common problems.

Heavy operations in onBindViewHolder

onBindViewHolder is called for each cell during scrolling. Creating objects, parsing JSON, or calling the database in this method leads to frame drops. Solution: perform all heavy computations in advance, passing ready-made data to the adapter. Use caching libraries for images — Glide, Coil, Kingfisher.

Ignoring DiffUtil / DiffableDataSource

Calling notifyDataSetChanged() on every update redraws the entire list, causing flickering and losing focus on inputs. Solution: use ListAdapter with DiffUtil in Android or DiffableDataSource in iOS. Diff calculation takes <1 ms for lists up to 1000 items and ensures smooth animations.

Storing heavy objects in the adapter

The adapter lives as long as RecyclerView/UITableView. Storing Bitmaps or large arrays in the adapter leads to memory leaks on configuration changes (screen rotation, theme change). Solution: the adapter should store only lightweight data models (data class / struct), and load heavy resources through ViewHolder on demand.

Frequently Asked Questions

What is the difference between Adapter and Proxy?

Adapter converts the Adaptee interface to the Target interface — the client gets a new way of interaction. Proxy provides the same interface as the original object but adds access control, caching, or lazy initialization. Proxy does not change the interface, Adapter does.

Why is RecyclerView.Adapter an example of the Adapter pattern?

RecyclerView.Adapter converts data (e.g., a list of User) into a ViewHolder that RecyclerView can display. RecyclerView expects a ViewHolder, the data is in List<User> format — the Adapter adapts one to the other. Additionally, the Adapter manages the ViewHolder lifecycle through the recycle pool, improving list performance.

What is the difference between Object Adapter and Class Adapter?

Object Adapter uses composition: it holds a reference to Adaptee and delegates calls to it. Class Adapter uses multiple inheritance: it inherits Adaptee and implements Target. In Java, Kotlin, and Swift, only Object Adapter is available due to the lack of multiple class inheritance. Class Adapter is only possible in C++.

When to use Adapter instead of Facade?

Adapter is used when you need to make two existing classes work together by converting the interface of one of them. Facade is used when you need to simplify interaction with a complex subsystem by providing a simple interface. Adapter is for compatibility, Facade is for simplification.

Can Adapter be implemented without an interface?

Yes, using closures. Instead of an interface with one method, you can pass a function. For example, in Swift: let adapter = { (data: Data) -> [CellConfig] in /* conversion */ }. This approach is called a functional adapter. However, for 2+ methods, a protocol/interface remains preferable — it makes the contract explicit.

Summary

  • Adapter is a structural pattern that converts an incompatible Adaptee interface into a Target through an intermediary class
  • RecyclerView.Adapter (Android) and UITableViewDataSource (iOS) are the main examples of the pattern in mobile development
  • Object Adapter uses composition; Class Adapter (C++ only) uses multiple inheritance
  • ListAdapter + DiffUtil (Android) and DiffableDataSource (iOS) are modern evolutions with automatic diff and animation
  • Heavy operations in onBindViewHolder, ignoring DiffUtil, and storing resources in the adapter are common mistakes
  • Adapter is used for integrating legacy code, adapting formats, and unifying different implementations
  • Recommendation: use ListAdapter/DiffableDataSource instead of manual list update management

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also