{"id":50565339,"url":"https://github.com/luisguzmanms/apiapp","last_synced_at":"2026-06-04T14:01:11.950Z","repository":{"id":190641736,"uuid":"600984034","full_name":"luisguzmanms/APIapp","owner":"luisguzmanms","description":"Aplicación móvil con arquitectura MVVM que permite realizar una conexión API REST","archived":false,"fork":false,"pushed_at":"2023-02-13T06:28:56.000Z","size":5374,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-04-20T12:07:09.599Z","etag":null,"topics":["android","api","kotlin","mvvm","rest-api"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/luisguzmanms.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-02-13T05:36:17.000Z","updated_at":"2023-02-21T04:01:40.000Z","dependencies_parsed_at":"2023-08-25T18:54:31.512Z","dependency_job_id":null,"html_url":"https://github.com/luisguzmanms/APIapp","commit_stats":null,"previous_names":["luisguzmanms/apiapp"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/luisguzmanms/APIapp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luisguzmanms%2FAPIapp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luisguzmanms%2FAPIapp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luisguzmanms%2FAPIapp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luisguzmanms%2FAPIapp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luisguzmanms","download_url":"https://codeload.github.com/luisguzmanms/APIapp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luisguzmanms%2FAPIapp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33907694,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","api","kotlin","mvvm","rest-api"],"created_at":"2026-06-04T14:01:10.621Z","updated_at":"2026-06-04T14:01:11.942Z","avatar_url":"https://github.com/luisguzmanms.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Documentación App API REST con MVVM \"Clientes\"\n\n\u003e Documentación y desarrollo móvil realizado por Luis A. Mesa ([luis4mesa@gmail.com](mailto:luisguzman014.m@gmail.com))\n\nIntroducción\n============\n\nEl siguiente documento describe el desarrollo de una aplicación móvil con arquitectura **MVVM** que permite realizar una llamada a un **API REST** en la URL [https://jsonplaceholder.typicode.com/users](https://jsonplaceholder.typicode.com/users) para obtener información sobre usuarios denominados como clientes para este caso.\n\nLos datos son luego cargados en un Recycler View, y se pueden filtrar para una mejor visualización y accesibilidad.\n\n  \n\nTecnologías utilizadas\n======================\n\nPara el desarrollo de la aplicación se han utilizado las siguientes tecnologías:\n\n  \n\n*   **Android Studio**: Es el entorno para desarrollo de aplicaciones para Android.\n*   Lenguaje de programación: **Kotlin**.\n*   **Coroutines** de **Kotlin** para procesar tareas asíncronas.\n*   Arquitectura **MVVM** para la implementación de la base de datos.\n*   **DataBinding** para enlazar los datos del ViewModel con la interfaz.\n*   **MaterialDesign3** para un diseño agradable.\n\n  \n\nCodigo fuente, Video e Instalador APK\n=====================================\n\nEn esta sección se incluirá el código fuente completo de la aplicación, un video demostrativo para ver su funcionamiento y un instalador para descargar e instalar la aplicación.\n\n  \n\n*   Código Fuente: [Github](https://rebrand.ly/APIappGIT)\n*   Demostración: [Video](https://rebrand.ly/APIvideo)\n*   Instalador APK: [Descargar](https://rebrand.ly/APIappAPK)\n\n  \n\nDetalles del desarrollo\n=======================\n\nMVVM\n----\n\nse utiliza en este proyecto para separar la lógica de negocios de la lógica de la interfaz de usuario.\n\nLiveData\n--------\n\nmanejo de cambios en los datos y la actualización automática de la vista\n\nDatabinding\n-----------\n\nVincular los datos directamente a la vista\n\n  \n\n  \n\n  \n\n  \n\n  \n\n![](https://t3128544.p.clickup-attachments.com/t3128544/ff0ea274-1976-4439-b688-b88997eb7f36/image.png)\n\n  \n\nUso de MVVM\n===========\n\n  \n\n*   [**CustomerAdapter**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/adapter/CustomerAdapter.kt): Se encarga de mostrar la información de cada cliente en la lista de clientes.\n*   [**Customer**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/res/layout/customer_item.xml): Contiene las propiedades y métodos necesarios para representar a un cliente.\n*   [**CustomerApiClient**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/api/CustomerApiClient.kt): archivo se encarga de establecer la conexión con la API que proporciona los datos de los clientes.\n*   [**CustomerService**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/CustomerService.kt): Contiene funciones específicas para obtener los clientes.\n*   [**RetrofitHelper**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/RetrofitHelper.kt)**:** facilita la creación de Retrofit.\n*   [**CustomerRepository**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/repository/CustomerRepository.kt)[:](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/repository/CustomerRepository.kt) encargado de gestionar el acceso a los datos de los clientes desde la API.\n*   [**CustomerViewHolder**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/ui/view/CustomerViewHolder.kt): Contiene la información que se mostrará para cada cliente.\n*   [**MainActivity**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/ui/view/MainActivity.kt): actividad principal de la aplicación.\n*   [**CustomerViewModel**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/ui/viewmodel/CustomerViewModel.kt): Obtiene los datos a través del repositorio y proporcionarlos a la actividad principal.\n*   [**GetCustomersUseCase**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/usecase/GetCustomersUseCase.kt): caso de uso que proporciona la manera de obtener los clientes de la aplicación.\n*   [**Util**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/util/Util.kt): contiene funciones útiles como la validación de conexión a internet.\n\n  \n\n![](https://t3128544.p.clickup-attachments.com/t3128544/3c73b060-bbf5-4957-b12f-2c1a3b0272e5/image.png)\n\n  \n\nFuncionamiento de la aplicación\n===============================\n\nSe hace una llamada API REST a [https://jsonplaceholder.typicode.com/users](https://jsonplaceholder.typicode.com/users) para obtener un listado de usuarios denominados \"**clientes**\", la información recibida se muestra en una vista de RecyclerView y se puede filtrar mediante una barra de búsqueda.\n\n  \n\nImplementación API REST:\n------------------------\n\nPara ello, se creó una clase llamada [`CustomerService`](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/CustomerService.kt) [](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/CustomerService.kt)que contiene la configuración de Retrofit para realizar la solicitud. haciendo uso del metodo \"**GET**\" [`CustomerApiClient`](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/api/CustomerApiClient.kt) [](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/network/api/CustomerApiClient.kt)para la consulta en la interfaz, con uso de **Courutines (**[Dispatchers.IO](http://Dispatchers.IO)**)** para la operaciones de datos en segundo plano.\n\n```kotlin\nclass CustomerService {\n    private val retrofit =     RetrofitHelper.getRetrofit(\"https://jsonplaceholder.typicode.com/\")\n    suspend fun getCustomers(): List\u003cCustomer\u003e {\n        return withContext(Dispatchers.IO) {\n            val response = retrofit.create(CustomerApiClient::class.java).getAllCustomers()\n            response.body() ?: emptyList()\n        }\n    }\n}\n\ninterface CustomerApiClient {\n    @GET(\"users\")\n    suspend fun getAllCustomers(): Response\u003cList\u003cCustomer\u003e\u003e\n}\n```\n\n  \n\nLuego, se creó un **Repository**, que es el encargado de obtener los datos de la **API** y proporcionarlos a la **vista**. En este caso, se utilizaron **Coroutines** para manejar de manera asíncrona la solicitud y evitar bloqueos en la UI. [CustomerRepository](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/repository/CustomerRepository.kt)\n\n[**CustomerRepository**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/repository/CustomerRepository.kt)\n\n```kotlin\nclass CustomerRepository {\n\n    private val api = CustomerService()\n\n    suspend fun getCustomers(): List\u003cCustomer\u003e {\n        return api.getCustomers()\n    }\n\n}\n```\n\n  \n\nPor último, en el **ViewModel** se hizo la llamada al **Repository** mediante un **caso de uso** (**GetCustomersUseCase()**) para obtener los datos y exponerlos en la vista.\n\n[CustomerViewModel](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/ui/viewmodel/CustomerViewModel.kt)\n\n```kotlin\nclass CustomerViewModel : ViewModel() {\n    private val _customers = MutableLiveData\u003cList\u003cCustomer\u003e\u003e()\n    val getCustomersUseCase = GetCustomersUseCase()\n\n    val customers: LiveData\u003cList\u003cCustomer\u003e\u003e\n        get() = _customers\n\n    fun getCustomers() {\n        viewModelScope.launch {\n            val result = getCustomersUseCase()\n            _customers.postValue(result)\n        }\n    }\n}\n```\n\n  \n\nImplementación RecyclerView:\n----------------------------\n\n[**Customer**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/data/model/Customer.kt)\n\nSe crea la clase **Customer** que el modelo de cliente que se utilizará en la aplicación. Contiene las propiedades y métodos necesarios para representar al cliente.\n\n```kotlin\npackage com.lamesa.apiapp.data.model\n\ndata class Customer(\n    val id: Int,\n    val name: String,\n    val username: String,\n    val email: String,\n    val address: Address,\n    val phone: String,\n    val website: String,\n    val company: Company\n)\n\ndata class Address(\n    val street: String,\n    val suite: String,\n    val city: String,\n    val zipcode: String,\n    val geo: Geo\n)\n/\ndata class Geo(\n    val lat: String,\n    val lng: String\n)\n\ndata class Company(\n    val name: String,\n    val catchPhrase: String,\n    val bs: String\n)\n```\n\n  \n\n**RecyclerView**\n\nSe implementa la vista en el XML activity\\_main.xml\n\n```xml\n \u003candroidx.recyclerview.widget.RecyclerView\n                android:id=\"@+id/customer_recycler_view\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\" /\u003e\n```\n\n  \n\n[**CustomerAdapter**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/adapter/CustomerAdapter.kt)\n\nSe crea el **CustomerAdapter** que se encargará de mostrar la lista de clientes en el recyclerview El método `updateList` actualiza la lista de clientes y llama a `notifyDataSetChanged` para notificar al adaptador de que la lista ha cambiado. El método `filterList` filtra la lista de clientes y actualiza la lista con los clientes filtrados.\n\n```kotlin\npackage com.lamesa.apiapp.adapter\n\n\nclass CustomerAdapter : RecyclerView.Adapter\u003cCustomerViewHolder\u003e() {\n\n    private var listCustomer = ArrayList\u003cCustomer\u003e()\n    private var lastPosition = -1\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomerViewHolder {\n        val view =\n            LayoutInflater.from(parent.context).inflate(R.layout.customer_item, parent, false)\n        return CustomerViewHolder(view)\n    }\n\n    override fun onBindViewHolder(holder: CustomerViewHolder, position: Int) {\n        val customer = listCustomer[position]\n        holder.bind(customer)\n        setAnimation(holder, position)\n    }\n\n    override fun getItemCount(): Int {\n        return listCustomer.size\n    }\n\n    internal fun updateList(customers: List\u003cCustomer\u003e) {\n        listCustomer.clear()\n        listCustomer.addAll(customers)\n        this.notifyDataSetChanged()\n    }\n\n    internal fun filterList(filteredCustomers: List\u003cCustomer\u003e) {\n        this.updateList(filteredCustomers)\n        this.notifyDataSetChanged()\n    }\n\n    private fun setAnimation(holder: CustomerViewHolder, position: Int) {\n        if (position \u003e lastPosition) {\n            val animation = AlphaAnimation(0f, 1f)\n            animation.duration = 500\n            animation.startOffset = 100 * position.toLong()\n            holder.itemView.startAnimation(animation)\n            lastPosition = position\n        }\n    }\n\n}\n```\n\n  \n\n[**CustomerViewHolder**](https://github.com/luisguzmanms/APIapp/blob/4fb3cd6e5e4e21f05af2ffbe7241cac7096563c0/app/src/main/java/com/lamesa/apiapp/ui/view/CustomerViewHolder.kt)\n\nSe inicializa las vistas que representan los detalles de un cliente, como su ID, nombre, correo electrónico, nombre de usuario, sitio web, teléfono, nombre de la compañía y ciudad de residencia. y se enlaza los datos traidos de la api.\n\n  \n\n![](https://t3128544.p.clickup-attachments.com/t3128544/43e6fd1c-cd09-4376-814b-87ec78c0d794/image.png)\n\n```kotlin\npackage com.lamesa.apiapp.ui.view\n\nclass CustomerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {\n\n    private val cardCustomer = itemView.findViewById\u003cMaterialCardView\u003e(R.id.card_customer)\n    private val id = itemView.findViewById\u003cTextView\u003e(R.id.identification)\n    private val name = itemView.findViewById\u003cTextView\u003e(R.id.fullname)\n    private val username = itemView.findViewById\u003cTextView\u003e(R.id.username)\n    private val email = itemView.findViewById\u003cTextView\u003e(R.id.email)\n    private val website = itemView.findViewById\u003cTextView\u003e(R.id.website)\n    private val phone = itemView.findViewById\u003cTextView\u003e(R.id.phone)\n    private val company = itemView.findViewById\u003cTextView\u003e(R.id.company)\n    private val address = itemView.findViewById\u003cTextView\u003e(R.id.address)\n\n    @SuppressLint(\"SetTextI18n\")\n    fun bind(customer: Customer) {\n        id.text = \"ID: ${customer.id}\"\n        name.text = customer.name\n        username.text = customer.username\n        email.text = customer.email\n        phone.text = customer.phone\n        website.text = customer.website\n        company.text = customer.company.name\n        address.text = customer.address.city\n\n        cardCustomer.setOnClickListener() {\n            Toast.makeText(cardCustomer.context, customer.toString(), Toast.LENGTH_SHORT).show()\n        }\n\n    }\n\n}\n```\n\n  \n\nRecyclerView en MainActivity\n\nSe inicializa el **RecyclerView** desde **MainActivity**\n\n```kotlin\nprivate fun initRecyclerView() {\n        val customerRecyclerView = findViewById\u003cRecyclerView\u003e(R.id.customer_recycler_view)\n        customerRecyclerView.layoutManager = LinearLayoutManager(this)\n        customerAdapter = CustomerAdapter()\n        customerRecyclerView.adapter = customerAdapter\n    }\n```\n\n  \n\nImplementación de la barra de búsqueda.\n---------------------------------------\n\nSe crea un **EditText** en el _activity\\_main.xml_ que mediante un **_TextWatcher_** _se detecta el texto digitado y es apsado a la función_ **_`filterCustomers(searchtext)`_**\n\n  \n\n```kotlin\n// Detectar cambios en etSearch para el filtrado de clientes.\n        binding.etSearch.addTextChangedListener(object : TextWatcher {\n            override fun afterTextChanged(s: Editable) {\n                val searchText = s.toString().trim().lowercase()\n                // Filtrar los clientes.\n                filterCustomers(searchText)\n            }\n            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}\n            override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) {\n                if (text.isEmpty()) {\n                    customerAdapter.updateList(allCustomers)\n                }\n            }\n        })\n\n private fun filterCustomers(searchText: String) {\n        val filteredCustomers = allCustomers.filter {\n            it.id.toString().contains(searchText, ignoreCase = true)\n                    || it.name.contains(searchText, ignoreCase = true)\n                    || it.email.contains(searchText, ignoreCase = true)\n                    || it.phone.contains(searchText, ignoreCase = true)\n                    || it.website.contains(searchText, ignoreCase = true)\n                    || it.address.city.contains(searchText, ignoreCase = true)\n                    || it.company.name.contains(searchText, ignoreCase = true)\n        }\n\n        // Actualizar lista de RecyclerView.\n        customerAdapter.filterList(filteredCustomers)\n\n        // Indicar que no se encuentra ningún resultado.\n        if (filteredCustomers.isEmpty()) {\n            binding.cnEmpty.visibility = View.VISIBLE\n            Toast.makeText(\n                this@MainActivity,\n                \"No se encontraron resultados.\",\n                Toast.LENGTH_LONG\n            ).show()\n        } else {\n            binding.cnEmpty.visibility = View.GONE\n        }\n\n    }\n```\n\n  \n\nEn el adaptador del **RecyclerView**, se usa la función `filterList ()` para actualizar la vista del recycler view:\n\n```kotlin\n internal fun filterList(filteredCustomers: List\u003cCustomer\u003e) {\n        this.updateList(filteredCustomers)\n        this.notifyDataSetChanged()\n    }\n```\n\n  \n\n===\n\nConclusión\n==========\n\nEn esta documentación se presentó la implementación de una aplicación en Android Studio utilizando Kotlin y las siguientes tecnologías: Retrofit2, Coroutines, DataBinding y MaterialDesign3. con arquitectura MVVM, de Retrofit y Coroutines donde se crearon las clases correspondientes para el Repository, el ViewModel y la Vista.\n\n  \n\nLa aplicación realiza una consulta a un API ([https://jsonplaceholder.typicode.com/users](https://jsonplaceholder.typicode.com/users)) para obtener una lista de usuarios, que luego se muestran en un RecyclerView.\n\nAdicional, se implementó una barra de búsqueda permitiendo al usuario filtrar la lista de acuerdo a la información proporcionada.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluisguzmanms%2Fapiapp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluisguzmanms%2Fapiapp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluisguzmanms%2Fapiapp/lists"}