Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Real-time London Underground status for Android & iOS — built with Kotlin Multiplatform and Compose Multiplatform.

Notifications You must be signed in to change notification settings

IntSoftDev/LondonTubeStatus

Repository files navigation

London Tube Status - Kotlin Multiplatform Library

🧩 Real-time London Underground status for Android & iOS — built with Kotlin Multiplatform and Compose Multiplatform.

Maven Central License: MIT Kotlin Compose Multiplatform

A Kotlin Multiplatform library providing real-time London Underground tube line status information with ready-to-use UI components and shared business logic for Android and iOS applications.

Features

  • Real-time TFL Data: Fetches live tube line status from Transport for London API
  • Kotlin Multiplatform: Shared business logic for Android and iOS
  • Compose Multiplatform UI: Native-feeling UI components
  • Two Integration Options: Core library only or complete UI solution
  • Easy Integration: Drop into existing navigation patterns
  • Offline Handling: Graceful error states and retry mechanisms

Architecture

The project consists of three main modules:

Core Library (com.intsoftdev:tflstatus-core)

  • Business logic and data layer
  • TFL API integration with Ktor
  • ViewModels and use cases
  • Koin dependency injection setup
  • Custom UI implementation support

UI Library (com.intsoftdev:tflstatus-ui)

  • Pre-built Compose Multiplatform UI components
  • Authentic TFL branding and colors
  • Ready-to-use screens and components
  • Includes core library as dependency

Sample App (/composeApp)

  • Demonstrates library integration
  • Example theming and navigation
  • Development and testing environment

Development Setup

Prerequisites

  • Android Studio Iguana+ with Kotlin Multiplatform plugin
  • Xcode 15+ (for iOS development)
  • Java 11+
  • TFL API Credentials (free from TFL Developer Portal) - Not required to run the app, but recommended to avoid throttling and rate limitations from TFL servers

Local Development

  1. Clone the repository:

    git clone https://github.com/IntSoftDev/LondonTubeStatus.git
    cd LondonTubeStatus
  2. Setup TFL API credentials: Create local.properties file:

    tfl.app.id=your_tfl_app_id
    tfl.app.key=your_tfl_app_key
  3. Configure local development: In gradle.properties, set:

    importLocalKmp=true
  4. Run the sample app:

    ./gradlew :composeApp:assembleDebug

Development Commands

# Android-only build
./gradlew assembleDebug

# Tests 
./gradlew allTests

# Lint check
./gradlew ktlintCheck

# Full build 
./gradlew build

# Clean build cache
./gradlew clean

Project Structure

LondonTubeStatus/
├── composeApp/                 # Sample Android/iOS app
│   └── src/
│       ├── androidMain/        # Android-specific code
│       ├── commonMain/         # Shared Compose UI
│       └── iosMain/            # iOS-specific code
├── tflstatus-core/             # Core business logic library
│   └── src/
│       ├── commonMain/         # Shared business logic
│       ├── androidMain/        # Android HTTP client
│       └── iosMain/            # iOS HTTP client
├── tflstatus-ui/               # UI components library
│   └── src/
│       ├── commonMain/         # Compose Multiplatform UI
│       └── ...
├── iosApp/                     # iOS app wrapper
└── PUBLISHING_GUIDE.md         # Library publishing documentation

Documentation

Quick Start

Android Integration

To use the pre-built artifacts:

In gradle.properties, set:

importLocalKmp=false

Add to your build.gradle.kts:

dependencies {
    // Option 1: Complete UI solution (recommended)
    implementation("com.intsoftdev:tflstatus-core:<version>")
    implementation("com.intsoftdev:tflstatus-ui:<version>")
    
    // Option 2: Core library only (custom UI)
    implementation("com.intsoftdev:tflstatus-core:<version>")
}

Initialisation

The TFL SDK can be initialised in your Application class with various configuration options:

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        // Option 1 - Basic initialisation without using API keys or Koin
        initTflSDK(context = this)

        // With custom API configuration
        val apiConfig = TflApiConfig(
            appId = "your-app-id",
            appKey = "your-api-key"
        )
        // Option 2 - if App does not use Koin
        initTflSDK(context = this, apiConfig = apiConfig)

        // Option 3 - with Koin
        val koinApp = startKoin { /* your modules */ }
        initTflSDK(context = this, koinApplication = koinApp, apiConfig = apiConfig)
    }
}

Basic usage with navigation:

@Composable
fun AppNavigation() {
    val navController = rememberNavController()
    
    NavHost(navController = navController, startDestination = "home") {
        composable("home") {
            HomeScreen(onNavigateToTFL = { 
                navController.navigate("tfl_status") 
            })
        }
        
        composable("tfl_status") {
           tflStatusUI(onBackPressed = {
               navController.popBackStack() 
            })
        }
    }
}

Theming & Customization

The UI library uses authentic TFL branding but can be customised:

@Composable
fun CustomThemedTFL() {
    MaterialTheme(
        colorScheme = yourCustomColorScheme,
        typography = yourCustomTypography
    ) {
         tflStatusUI(onBackPressed = { /* handle back */ })
    }
}

iOS Integration

CocoaPods Setup

Add the TFL Status library to your iOS project using CocoaPods:

Local Development

# Podfile
platform :ios, '17.0'

install! 'cocoapods', :deterministic_uuids => false

target 'YourApp' do
    use_frameworks!
    
    # Local paths - adjust to match your project structure
    pod 'tflstatus_core', :path => '../tflstatus-core/'
    pod 'tflstatus_ui', :path => '../tflstatus-ui/'
end

Then run:

cd ios
pod install
// From iOS Swift code:
let viewController = TFLStatusBridgeKt.createTFLStatusViewController(
    showBackButton: true,
    onBackPressed: {
        // Handle back button press
    },
    enableLogging: true,
    apiConfig: TflApiConfig()
)

SwiftUI Integration

Add the framework to your iOS project and use in SwiftUI:

import SwiftUI
import tflstatus_ui
import tflstatus_core

struct TFLStatusComposeView: UIViewControllerRepresentable {
   var showBackButton: Bool = true
   var onBackPressed: (() -> Void)?

   func makeUIViewController(context: Context) -> UIViewController {
      return TFLStatusBridgeKt.createTFLStatusViewController(
              showBackButton: showBackButton,
              onBackPressed: onBackPressed ?? {
              },
              enableLogging: true,
              apiConfig: TflApiConfig(
                      appId: "your-app-id",
                      appKey: "your-api-key"
              )
      )
    }

   func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
      // No-op: Compose content is self-updating
   }
}

struct TFLStatusScreen: View {
   @Environment(\.dismiss) private var dismiss

   var body: some View {
      NavigationView {
         ZStack {
            Color.black
                .navigationBarTitleDisplayMode(.inline)
                .edgesIgnoringSafeArea(.all)

            // Pipe SwiftUI's dismiss into KMP's onBackPressed
            TFLStatusComposeView(
                    showBackButton: true,
                    onBackPressed: { dismiss() }
            )
         }
         .toolbar {
            ToolbarItem(placement: .principal) {
               Text("TFL Status")
                   .foregroundColor(.white)
                   .font(.headline)
            }
         }
         .navigationBarBackButtonHidden(true)
         .navigationBarItems(leading: Button(action: {
            dismiss()
         }) {
            Image(systemName: "arrow.left")
                .foregroundColor(.white)
            })
        }
        .preferredColorScheme(.dark)
    }
}

// Usage in your main ContentView
struct ContentView: View {
    @State private var showTFLStatus = false
    
    var body: some View {
        NavigationView {
            VStack {
                Button("London Tube Status") {
                    showTFLStatus = true
                }
                .buttonStyle(.borderedProminent)
            }
            .navigationTitle("Your App")
            .sheet(isPresented: $showTFLStatus) {
               TFLStatusScreen()
            }
        }
    }
}

Custom UI with Core Library

@Composable
fun CustomTFLScreen(viewModel: TubeStatusViewModel = koinInject()) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    
    LaunchedEffect(Unit) {
        viewModel.getLineStatuses("victoria,central,northern")
    }
    
    when (val state = uiState) {
        is TubeStatusUiState.Loading -> LoadingScreen()
        is TubeStatusUiState.Success -> CustomLineList(state.tubeLines)
        is TubeStatusUiState.Error -> ErrorScreen(state.message)
    }
}

Screenshots and usage in Live apps

Android iOS
Used in Play Store app Used in Apple App Store

Publishing

Libraries are published to Maven Central:

  • Core: com.intsoftdev:tflstatus-core
  • UI: com.intsoftdev:tflstatus-ui

See PUBLISHING_GUIDE.md for detailed publishing instructions.

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/<feature_name>)
  3. Commit your changes (git commit -m 'Add <feature>')
  4. Push to the branch (git push origin feature/<feature_name>)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Transport for London for providing the public API
  • JetBrains for Kotlin Multiplatform and Compose Multiplatform
  • Koin for dependency injection framework

Support


About

Real-time London Underground status for Android & iOS — built with Kotlin Multiplatform and Compose Multiplatform.

Resources

Stars

Watchers

Forks

Packages

No packages published