serotonincrash/folibusapi
A native Swift package for accessing the Föli (Turku Region Public Transport) real-time public transport API. It's quick, utilising modern Swift Concurrency and a modern `await`able API, and a built in caching layer.
Disclaimer
This API is an experimental project for me to try out Xcode's new agentic programming systems. Whilst I will personally do my best to maintain and review the code generated by AI models, I cannot personally guarantee that the wrapper will continue working. The Föli data API updates frequently. Use at your own risk.
Docs are a work in progress and generated by AI models.
Overview
FoliBusAPI provides async Swift interfaces for:
- SIRI stop monitoring and real-time arrivals
- GTFS routes, stops, trips, stop times, and calendar dates
- Optional on-disk caching for GTFS-backed resources
- SwiftUI-friendly access through
FoliServiceand client providers - Transport injection for tests and advanced networking setups
The package is built around FoliClient, an actor that owns request execution, response decoding, in-flight deduplication, and cache coordination.
Requirements
- iOS 15.0+
- macOS 12.0+
- watchOS 8.0+
- tvOS 15.0+
- Swift 6.2 toolchain
Installation
Swift Package Manager
Add the package to your Package.swift dependencies:
.package(url: "https://github.com/serotonincrash/FoliBusAPI.git", branch: "main")Then add the product to your target:
.target(
name: "MyApp",
dependencies: [
.product(name: "FoliBusAPI", package: "FoliBusAPI")
]
)Common usage patterns
Direct client usage
import FoliBusAPI
let client = FoliClient(
cacheBehavior: .forceRefresh,
cacheTimeout: .default
)
let routes = try await client.fetchRoutes()
let stopMonitoring = try await client.fetchStopMonitoring(for: "1000")
let arrivals = try await client.fetchArrivals(for: "1000")Shared convenience facade
import FoliBusAPI
let routes = try await FoliBusAPI.fetchRoutes()
let arrivals = try await FoliBusAPI.fetchArrivals(for: "1000")SwiftUI integration
import SwiftUI
import FoliBusAPI
@main
struct DemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.environment(
\.foliClientProvider,
DefaultFoliClientProvider(
configuration: FoliClientConfiguration(
cacheBehavior: .staleWhileRevalidate,
cacheTimeout: .default
)
)
)
}
}
}
struct ContentView: View {
@FoliService var foliService
var stopId: String
@State var arrivals: [Foli.Arrival] = []
var body: some View {
List(arrivals, id: \.id) { arrival in
Text(arrival.lineRef)
}
.task {
do {
arrivals = try await foliService.fetchArrivals(for: stopId)
} catch {
print("Failed to fetch arrivals: \(error.localizedDescription)")
}
}
}
}Inject a custom transport
import Foundation
import FoliBusAPI
let client = FoliClient(
transport: URLSessionTransport(session: .shared),
cacheBehavior: .cachedOrFetch
)Caching
GTFS-backed resources support the following cache strategies:
cachedOrFetchstaleWhileRevalidateforceRefreshcachedOnlynoCache
Use staleWhileRevalidate when you want fast UI reads backed by a best-effort background refresh. If metadata revalidation fails transiently, the existing cached value remains usable until a later refresh succeeds.
Use forceRefresh when you need the freshest known data.
Documentation
The package includes a .docc catalog under Sources/FoliBusAPI/FoliBusAPI.docc.
If your local toolchain supports DocC generation, you can build docs with Xcode:
xcodebuild docbuild \
-scheme FoliBusAPI \
-destination 'platform=macOS,arch=arm64,name=My Mac' \
-derivedDataPath .build/DerivedData \
-resultBundlePath .build/FoliBusAPI-docbuild.xcresultDevelopment
Run the test suite with:
swift testPackage Metadata
Repository: serotonincrash/folibusapi
Default branch: main
README: README.md