Open-source project
joreilly/GalwayBus avatar
joreilly/GalwayBus

GalwayBus: a Kotlin Multiplatform sample that ships on Android, iOS and Desktop

Galway Bus Kotlin Multiplatform project using Jetpack Compose and SwiftUI

590 stars48 forksKotlinLicense varies

At a glance

What is it?
GalwayBus is a Compose Multiplatform app for live Galway bus times, backed by a Ktor service that serves GTFS and GTFS-Realtime data. It is more useful as a worked example of shared UI and shared logic than as a transit product.
Who is it for?
Adopt GalwayBus as a reference for Compose Multiplatform structure, not as a transit product: the shared module layout, the Ktor GTFS and GTFS-Realtime backend and the fastlane setup are the parts worth copying. Skip it if you need a maintained, licensed dependency, since the repository declares no licence and publishes no releases.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Kotlin, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What GalwayBus actually solves, and for whom

GalwayBus shows live bus times for Galway city: real-time departures for saved stops, stops near you, live on-map bus tracking with a journey timeline, and network-wide stop search. That is the product description from the README. The audience is narrower than the feature list suggests. This is a sample app from a developer who has published a series of Kotlin Multiplatform samples, and the README says it acted as an initial platform for exploring Kotlin Multiplatform capabilities. The person who gets value from it is an engineer who wants to see one non-trivial app built once and run on Android, iOS and Desktop (JVM), with a real backend rather than a mocked list of strings.

The repository topics point the same way: coroutines, coroutines-flow, koin, ktor, jetpack-compose, swiftui, kotlin-native, fastlane. Those are the pieces a team evaluating KMP wants to see wired together. If you are looking for an app to install on your phone for tomorrow's commute, this is not that, and the README does not present it as one.

Shared UI, platform entry points and a Ktor GTFS backend

The architecture is visible in the repository layout. The README describes a single Compose Multiplatform UI shared across Android, iOS and Desktop (JVM), backed by a Ktor service that serves GTFS schedule and GTFS-Realtime data. So there are two halves: a client that renders the same composables everywhere, and a server that turns transit feeds into something the client can consume.

On the client side, /shared holds the code that is common across targets. commonMain is for code common to all targets; iosMain is where Apple-specific calls belong, and jvmMain is where Desktop-specific code goes. The README is explicit that even when you share your UI with Compose Multiplatform you still need /iosApp as the iOS entry point, and that is where SwiftUI code goes. That split is the whole point of the sample: the shared module is not a thin networking layer, it carries the UI.

Koin appears in the topics, which matches the usual pattern of declaring dependencies once in common code and letting each platform supply its own bindings. SQLDelight shows up in one of the linked blog posts about multiplatform persistence, so local storage is part of the story rather than something bolted on per platform. The server half is the part that is easy to underestimate: GTFS schedule data and GTFS-Realtime data are different feeds with different shapes, and the README's claim that a Ktor service serves both means the client is not parsing raw transit archives.

Building and running GalwayBus on each target

The README gives run configurations through the IDE's run widget and a set of Gradle commands. The Android app builds with a single task:

bash
./gradlew :androidApp:assembleDebug

That produces a debug APK for the Android target. For Desktop there are two options, and the difference matters if you are iterating on UI:

bash
./gradlew :desktopApp:hotRun --auto
./gradlew :desktopApp:run

The first uses hot reload, the second is a standard run. Hot reload is the reason to start with Desktop even if your eventual target is mobile: you change a composable and see it without a full rebuild cycle. iOS does not go through Gradle in the README's instructions. You open the /iosApp directory in Xcode and run it from there.

Tests are split by target, which tells you how the project expects you to verify shared code:

bash
./gradlew :shared:testAndroidHostTest
./gradlew :shared:jvmTest
./gradlew :shared:iosSimulatorArm64Test

The iOS test task targets the arm64 simulator specifically, so an Intel Mac is not the assumed environment. The README also notes you can use the run button in your IDE's editor gutter instead. It does not document a first-run configuration step, a required API key, or a local server you must start before the app is useful, so treat the backend as something you will have to locate in the source yourself.

Where GalwayBus is the wrong tool

The repository declares no licence. The README does not state one, the description does not state one, and no licence file appears in the top-level entries. For a sample you read on GitHub that is a minor annoyance. For anything you copy into a product it is a real problem, because the default position without a licence grant is that you have no rights beyond what the platform's terms give you. If you want to lift the shared module structure or the Ktor service into your own codebase, resolve that first.

There are no releases. The README does not describe publishing artifacts to Maven Central or anywhere else, so you cannot depend on GalwayBus as a library; you consume it as source. Combined with the missing licence, that makes it a study object rather than a dependency.

The sample is also tied to a specific city and a specific feed. The features are defined around Galway stops, routes and real-time departures, and the README describes the backend as serving GTFS and GTFS-Realtime data without saying which agency publishes it or what the terms of use are. Pointing the app at another city is not a configuration change described anywhere in the README. And the last push was on 2026-09-09, so the project has moved recently, but with no releases and no changelog there is nothing to tell you what changed or whether a given commit is stable.

The documentation is thin in one more place: the README explains how to run the apps and the tests and how the code is organised, and stops there. There is no architecture document, no description of the data flow between the Ktor service and the shared UI, and no rollback or migration guidance, because there is nothing to migrate.

GalwayBus against a plain Kotlin Multiplatform setup

The obvious alternative is a bare Kotlin Multiplatform project with a shared networking and model module and two separate native UIs: Jetpack Compose on Android and SwiftUI on iOS. That approach keeps each UI idiomatic to its platform. You get native navigation, native gesture behaviour and no Compose runtime on iOS. The cost is that every screen is written twice, and the divergence between the two implementations grows with the feature count.

GalwayBus takes the other branch. It shares the UI itself through Compose Multiplatform, and the README still keeps /iosApp as the iOS entry point with room for SwiftUI code. That is a hybrid rather than a pure bet: the shared module carries the screens, and the iOS app remains the place for platform-specific SwiftUI work. The trade-off is that your iOS UI is Compose rendering on iOS, not SwiftUI, so anything that depends on deep platform integration has to cross back into the iosMain side.

A second alternative, for the backend half, is to skip the Ktor service entirely and have each client talk to the transit feed directly. GalwayBus does not do that, and the reason is easy to see: GTFS schedule and GTFS-Realtime are different formats, and a server that normalises both means the shared client code deals with one model. If you are building a single-platform app, that server is extra infrastructure you may not want.

Maintenance, licence and what a fork costs you

The last push was on 2026-09-09, so the repository is current. There are no releases, which means there is no version to pin, no changelog and no upgrade path other than tracking main. Renovate is configured in the repository, so dependency updates are being proposed automatically, and that is a reasonable signal about how the project is kept alive. It is not a signal about API stability.

Licence is the open question. No licence identifier appears in the README or the repository description, so the safest reading is that no permission has been granted. That does not stop you from reading the code, and it does not stop you from learning the structure. It does mean that copying files into your own repository, or shipping a derivative app, is something to clarify with the author before you build on it. This is not legal advice, and the answer depends on your jurisdiction and your use, but the absence of a licence file is a fact you can check in the repository root yourself.

Upgrade cost for a fork is dominated by the Kotlin and Compose versions. The README's badge shows Kotlin 2.4.0. Compose Multiplatform, Koin, Ktor and SQLDelight all move on their own schedules, and Renovate will surface those bumps as pull requests. Since there is no test suite description beyond the three Gradle test tasks, the practical safety net when you upgrade is :shared:jvmTest running on the desktop target, which is the fastest of the three to execute.

Editorial conclusion

Adopt GalwayBus as a reference for Compose Multiplatform structure, not as a transit product: the shared module layout, the Ktor GTFS and GTFS-Realtime backend and the fastlane setup are the parts worth copying. Skip it if you need a maintained, licensed dependency, since the repository declares no licence and publishes no releases. Before reusing anything, confirm the licence question with the author and check whether the GTFS and GTFS-Realtime endpoints the Ktor service depends on are ones you are allowed to consume.

Frequently asked questions

How do I install and run GalwayBus?

There is no installer. Build the Android app with ./gradlew :androidApp:assembleDebug, run the Desktop app with ./gradlew :desktopApp:run or ./gradlew :desktopApp:hotRun --auto for hot reload, and open the /iosApp directory in Xcode for iOS.

Does GalwayBus work on Android and iOS from the same code?

Yes. The README states that a single Compose Multiplatform UI is shared across Android, iOS and Desktop (JVM). The iOS app still needs its own entry point in /iosApp, which is also where SwiftUI code belongs.

What backend does GalwayBus use for live bus data?

The README describes a Ktor service that serves GTFS schedule and GTFS-Realtime data. The client consumes that service rather than parsing the raw feeds itself.

How do I run the GalwayBus tests?

The README lists three Gradle tasks: ./gradlew :shared:testAndroidHostTest, ./gradlew :shared:jvmTest and ./gradlew :shared:iosSimulatorArm64Test. You can also use the run button in your IDE's editor gutter.

What licence is GalwayBus released under?

No licence is stated in the README or the repository description, and no licence file appears among the top-level entries. Treat reuse of the source as something to confirm with the author first.

Official sources

  1. Issues
  2. joreilly/GalwayBus on GitHub
  3. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/joreilly-galwaybus.svg)](https://hysenlabs.com/projects/joreilly-galwaybus)