Guía PES

App móvil

Elegir la tecnología y montar una arquitectura que aguante tres sprints

Ninguna tecnología es obligatoria. Lo que cuenta es que la decisión esté justificada y que el código sea limpio, probado y fácil de cambiar cuando cambien el backend o el servicio del otro equipo.

Las opciones

Nativo

Android

Kotlin + Jetpack Compose, la opción oficial. Java o Kotlin con Views es el mundo antiguo: lo encontraréis en código heredado.

Nativo

iOS

Swift + SwiftUI. UIKit es la opción antigua. Hace falta un Mac para compilar y para publicar.

Multiplataforma

Flutter

Dart y un solo código para Android, iOS, web y escritorio. Muchos paquetes en pub.dev y buena integración con Firebase o Supabase.

Multiplataforma web

React Native

JavaScript o TypeScript con React. Ionic y Cordova son alternativas más basadas en web.

Nativo compartido

Kotlin Multiplatform

La lógica en Kotlin compartida entre Android e iOS. Con Compose Multiplatform también compartís la UI.

Cómo elegir

Preguntas clave

  • ¿Qué sabe ya el equipo?
  • ¿Necesitáis iOS y tenéis Mac?
  • ¿Hay librerías para lo que queréis (mapas, login social, chat)?
  • ¿Lo justificáis en la memoria?

El mismo modelo en tres lenguajes

data class Station(val id: Int, val name: String, val freeBikes: Int)

// null safety: si no hay estación, un texto por defecto
fun label(s: Station?) = s?.name ?: "Desconocida"

Dos arquitecturas que separan la UI de la lógica

La regla es la misma: la vista nunca habla directamente con la API. Así podéis probar la lógica sin pantallas y cambiar la fuente de datos (mock o real) sin tocar la UI.

MVVM (Android, iOS)

View
binder
ViewModel
llamada
Model

El binder es el enlace reactivo: StateFlow, LiveData, Combine. La vista observa el estado y se redibuja sola (patrón Observer).

BLoC (Flutter)

UI
events / states
BLoC
petición
Data

Los events entran (un toque, abrir la pantalla), el BLoC los procesa y emite states que la UI pinta.

class StationsViewModel(private val source: StationsDataSource) : ViewModel() {
    private val _stations = MutableStateFlow<List<Station>>(emptyList())
    val stations: StateFlow<List<Station>> = _stations

    init {
        viewModelScope.launch { _stations.value = source.getStations().sortedBy { it.name } }
    }
}

@Composable
fun StationsScreen(vm: StationsViewModel = koinViewModel()) {
    val stations by vm.stations.collectAsStateWithLifecycle()
    LazyColumn {
        items(stations) { StationRow(it) }
    }
}

Para proyectos más grandes, el paquete flutter_bloc hace explícitos los events y los states. Para empezar, un ValueNotifier ya separa la lógica de la UI.

Linters: código listo para enseñar

Un linter analiza el código sin ejecutarlo y encuentra errores y problemas de estilo. El objetivo es un código limpio, como un documento que otra persona tendrá que leer. Cuenta para la gestión de la calidad.

  • Kotlin: ktlint (estilo) y detekt (problemas de código).
  • Swift: SwiftLint.
  • Dart: flutter analyze con el paquete flutter_lints, y dart format.
  • JavaScript o TypeScript: ESLint y Prettier.
  • Todo junto: SonarQube o SonarCloud para seguirlo a lo largo del curso.

Un test que el mock hace posible

test('ordena las estaciones por nombre', () async {
  final bloc = StationsBloc(FakeStationsDataSource());
  await bloc.load();
  final names = bloc.stations.value.map((s) => s.name).toList();
  expect(names, [...names]..sort());
});

Sin red y en milisegundos. Generad la fuente de datos falsa en el generador y añadid la cobertura a la memoria (flutter test --coverage, Kover o JaCoCo en Kotlin).

Integración continua con GitHub Actions

Cada pull request pasa el linter, los tests y el build antes de fusionarse. Es la gestión de configuraciones que se evalúa. Guardad el fichero en .github/workflows/ci.yml. Alternativas: GitLab CI, Jenkins, Codemagic o Azure DevOps.

name: Android CI
on:
  pull_request:
  push:
    branches: [develop, main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '17'
      - name: Lint, tests y build
        run: |
          chmod +x gradlew
          ./gradlew lint testDebugUnitTest assembleDebug
      # con los plugins de ktlint y detekt: ./gradlew ktlintCheck detekt

Hacer llegar la app a profesores y stakeholders

Privada · gratuita

Firebase App Distribution

Enviad cada versión por correo a quien queráis que la pruebe, Android e iOS. La manera más cómoda para PES.

Privada

TestFlight

Para iOS. Hace falta el programa de desarrolladores de Apple.

Tiendas

Google Play y App Store

La pista de pruebas interna de Google Play es un buen primer paso. También hay tiendas alternativas como F-Droid.

Para la entrega final, la información de la release tiene que explicar paso a paso cómo ejecutar la app. Un APK en GitHub Releases más un enlace de distribución lo pone muy fácil. Ved el Fag-5 →

Asistentes de IA: sí, pero con ojo

Gemini, Copilot, Claude y similares ayudan a planificar y diseñar (borradores de arquitectura), a generar código repetitivo, a depurar (explicar errores, refactorizar) y a automatizar flujos con agentes.

Ejemplos y recursos

Dos implementaciones de la misma app de estaciones de Bicing, mostradas en directo en la sesión de desarrollo móvil del curso. Fijaos en cómo separan UI, lógica y datos.

Flutter y Dart