Aplicación iOS nativa (SwiftUI) para consultar en tiempo real cuánto falta para que llegue cada autobús urbano a una parada de León, España.
Importante: de momento no hay API oficial confirmada. La app incluye un proveedor mock de demostración claramente identificado. Los datos no son oficiales ni deben usarse como información de servicio real.
Requisitos
- macOS con Xcode actual (SDK iOS 26+)
- iOS 26.0 o superior
- Sin dependencias externas de terceros
Cómo ejecutar
- Abre
LeonBusLive.xcodeprojen Xcode. - Selecciona un simulador o un iPhone físico.
- Pulsa Run (⌘R).
Si solo tienes project.yml, regenera el proyecto:
brew install xcodegen # si hace falta
xcodegen generate
open LeonBusLive.xcodeproj
Ejecutar tests:
xcodebuild test -scheme LeonBusLive -destination 'platform=iOS Simulator,name=iPhone 16'
Objetivo de producto
- Ver paradas cercanas y próximos buses
- Consultar detalle de parada con llegadas en vivo
- Explorar líneas y recorrido
- Guardar favoritos (paradas con alias, líneas)
- Buscar líneas, paradas y destinos
- Funcionar sin permiso de ubicación (fallback al centro de León)
Arquitectura
La UI no conoce el origen de los datos. Todo pasa por el protocolo TransitDataProvider.
Views / ViewModels
│
▼
AppModel (Observation)
│
▼
TransitDataProvider ◄── MockTransitDataProvider
◄── APITransitDataProvider (URLSession, listo para REST)
│
▼
MockRealtimeEngine (simulación 5–10 s)
LocationService (CoreLocation)
FavoritesStore / SettingsStore (UserDefaults)
Estructura
LeonBusLive/
├── App/ # Entrada, tabs, AppModel
├── Models/ # BusStop, BusLine, BusVehicle, BusArrival…
├── Services/ # Providers, ubicación, realtime, favoritos
├── Features/ # Nearby, Stops, Lines, Favorites, Search, Settings…
├── Components/ # ArrivalRow, LineBadge, glass helpers…
├── Resources/ # Assets + Info.plist
└── Utilities/ # Formatters, Liquid Glass helpers
Sistema de datos mock
- Catálogo:
LeonDemoCatalog(paradas y líneas inspiradas en León). - Motor:
MockRealtimeEnginemueve buses por polilíneas y recalcula ETAs. - Cada modelo lleva
isDemoDatay la UI muestra el aviso “Datos de demostración — no oficiales”.
Cambiar de mock a API: Ajustes → Fuente de datos, o SettingsStore.providerKind.
Cómo conectar una API real
- Configura
APITransitConfiguration.baseURL(y API key si aplica). - Completa/ajusta los DTOs en
APIModels.swiftal contrato real. APITransitDataProviderya usaURLSession+async/awaity mapea a modelos de dominio.- Selecciona el proveedor
.apien Ajustes.
Las vistas no requieren cambios si el protocolo se mantiene.
Cómo integrar GTFS
GTFS es estático (paradas, rutas, trips, stop_times):
- Descarga/publica el feed GTFS (zip).
- Parsea
stops.txt,routes.txt,trips.txt,stop_times.txt,shapes.txt. - Implementa un
GTFSTransitDataProviderque conformeTransitDataProvider. - Mapea:
stops→BusStoproutes→BusLineshapes/ secuencia destop_times→LineRoute.polyliney orden de paradas
- Las llegadas teóricas salen de
stop_times→BusArrivalconisRealtime = false.
Cómo integrar GTFS-Realtime
GTFS-RT aporta Vehicle Positions, Trip Updates y Service Alerts:
- Consume el protobuf GTFS-RT (o un proxy JSON).
- Combina con el GTFS estático:
- Trip Updates →
predictedArrival,delaySeconds,isRealtime = true - Vehicle Positions →
BusVehicle
- Trip Updates →
- Puedes extender
MockRealtimeEnginehacia unRealtimeIngestorque actualice un store observable. - Mantén el mismo
TransitDataProviderpara que la UI siga igual.
Liquid Glass
La app usa componentes de sistema (TabView, NavigationStack, List, toolbars) que adoptan Liquid Glass automáticamente en iOS 26+.
Para superficies custom se usan APIs nativas cuando existen:
glassEffect(_:in:)- variante interactiva
.regular.interactive() - fallback a
.ultraThinMaterialsi la API no estuviera disponible
No se recrea el efecto con hacks visuales.
Accesibilidad
- VoiceOver labels en filas, badges y mapa
- Dynamic Type (fuentes de sistema)
- Favoritos y acciones con área táctil ≥ 44 pt
- El color de línea no es el único canal de información (número + texto)
Licencia / aviso
Proyecto de demostración. Nombres de paradas y líneas son ficticios o aproximados con fines de UI. No afiliado al Ayuntamiento de León ni a operadores de transporte.
