{
"$type": "site.standard.document",
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreicuuudzzbx6plotgcin6t533jow5cvwkn4orrtah7oqgwmadkalhm"
},
"mimeType": "image/webp",
"size": 30140
},
"description": "A look at some of the technical changes made to Singapore Buses.",
"path": "/2025-09-16-singapore-buses-v2026/",
"publishedAt": "2025-09-16T01:00:03.000Z",
"site": "at://did:plc:ex23caczr45rodrfcxrwps6h/site.standard.publication/self",
"tags": [
"singapore-buses",
"app-updates"
],
"textContent": "Singapore Buses v2026 is out now. It's been a challenging development cycle, to say the least, and I'm glad it's over. The Design Diaries series covers the major Liquid Glass updates. This post looks at some of the other technical changes that have been made in the v2026 update.\n\nGRDB\n\nSingapore Buses, has always, always, always—way back to 2018 when it was SG Transit—used Core Data. Even when SwiftUI came along, followed by SwiftData, it stuck with Core Data. Not any more. It's now using GRDB.\n\nEach app version is seeded with an updated SQLite database containing the latest bus stops, routes, and services. This database is created using a separate app on the Mac. Long-term, the aim is to move away from having to release new app versions with an up-to-date database and instead move to an over-the-air model that refreshes the database in the background. The move to GRDB should smooth the path.\n\nMap Performance\n\nThe database is now loaded into memory instead of being read from disk. As a result, panning the map and determining what bus stops to show (or hide) is now instantaneous instead of requiring a small pause and the end of the pan gesture. It's a simple call site:\n\n.onMapCameraChange(frequency: .continuous) { context in\nmodel.showStops = context.camera.distance < 9500 // hide stops when above 9,500m\nTask {\nawait model.updateVisibleBusStops(context.rect)\n}\n}\n\nApp Clip\n\nSingapore Buses now contains an App Clip with arrival estimate UI.\n\nWhen users share estimated arrival times from the main app, text is generated with the current arrival times along with a URL: https://clips.singaporebuses.app?... That URL has the following query parameters:\n\nParameter\nUsage\nExample\n\nc\nBus Stop Code\n08138\n\nn\nBus Stop Name\nConcorde Hotel S'pore\n\nr\nRoad Name\nOrchard Rd\n\ns\nService No\n174\n\nlat\nLatitude\n1.3004785730821\n\nlon\nLongitude\n103.841847006076\n\ne\nExpiry\n1757767987\n\nv\nHMAC Validation\nsignature\n\nThe parameters c, n, and r are used to populate the UI in the App Clip, while lat and lon are used to determine if a Look Around view can be displayed. s in conjunction with c are used to query the LTA API for the latest estimates. e is the time the App Clip expires, and v is a signature that is validated to make sure the URL is genuine.\n\nIf the recipient has Singapore Buses installed, tapping the link opens the app and presents arrivals for that bus stop. Otherwise, an App Clip launches with the same bus arrival UI as the main app and updates arrival estimates every 15 seconds for 10 minutes...then invites the user to download the app from the App Store.\n\nThe **clips**.singaporebuses.app subdomain is powered by Cloudflare Workers. It serves an AASA, some basic content, and a favicon, but is otherwise is there to enable redirects to the app or the App Clip.\n\nThis is the first time I've implemented an App Clip. I'm interested to see how (or if) it affects download statistics.\n\niPad Support\n\nGiven that iPadOS is, in my opinion, the star of the *26 *series of OS updates, it only made sense to extend Singapore Buses to the iPad. It now has a native layout with some iPad specific niceties, like keyboard shortcuts and a full-screen route views.\n\n(I've never seen anyone actually use an iPad at a bus stop in Singapore. With that in mind, Singapore Buses will be on Apple Watch in the future.)\n\nLandTransportKit\n\nLastly, all interaction with the Land Transport API is now handled with LandTransportKit.\n\nI've open sourced this package—it's free for anyone to use!",
"title": "Singapore Buses v2026"
}