Back

SetOut Backend

2023 / 08 / 21

In my previous blog post I described the motivation behind the SetOut iOS app. In this post I wanted to cover some of the work that went into the back end component of the app.

Apple Maps Won't Be Enough

There are a number of ways to access the Apple Maps servers (as of 2023), MapKit for iOS apps, MapKit JS for web apps and the recently updated Maps Server API. MapKit for SwiftUI is excellent, but only recently (WWDC23) added a cleaner API for rendering map overlays and drawing routes. When I was doing my original scoping and research, I wanted to avoid having a server if at all possible. Ultimately my goal was to present the user with a list of tourist attractions that were pulled from Apple Maps, based on some criteria and then draw a simple route between the top ranked ones. However, I quickly ran into issues when looking into Apple Map's API:

1. The criteria to filter points of interest by isn't very detailed, specifically there are no filters for tourist attractions, museums, galleries, etc.

2. There are no ratings.

3. There are no images, descriptions or other helpful facts.

If I wanted to have this information, I had to build my own server and database.

Options for the Server

When approaching the design of the server app and the overall architecture, I first wanted to settle on the tech stack. Based on that, I could decide on the appropriate cloud solution.

The first option was to look for an app development solution like Firebase, but my database options there were limited to NoSQL only, unless I wanted to use JavaScript. My JavaScript isn't great, so I didn't really want to go down this route. Additionally, a document style database didn't really fit my use case, since I wanted to have a fairly efficient and easy to query method for finding the optimal route between tourist attractions. Unlike NoSQL, graph databases are great for that and Neo4j's Aura in particular comes with a library of shortest path algorithms. I decided that more flexibility is better for this project and the whole point is to learn new things, so I set my mind on building my own server app.

For the server app itself, I wanted to try Go in a larger project and that fit really nicely with my goal to use Neo4j's AuraDB, since it also comes with a great Go API. With the stack finalised it made sense to go for Google Cloud (GCP), since it offers a great development environment for Go applications.

High-Level Architecture

Running a Go app on GCP is fairly straight forward, so the main choice come down to which compute resource and environment to run it in. From the available products in that category the two options that made most sense for me were AppEngine and CloudRun. AppEngine looked very attractive, since it was cheaper than CloudRun, it could scale down to 0 instances and I also didn't want to faff around with containers. For the API gateway I ended up using Cloud Endpoints with ESPv2, which was ideal for a simple RESTful API exposing a few methods.

setout backend

High-level view of the back end architecture.

Finally for the databases, I chose Neo4j's AuraDB for the tourist attraction data and Cloud Storage for images.

Server App

I wanted a simple RESTful API, so I opted for Cloud Endpoints which did the job admirably. From the initial set up, all the way to updating the API specs and re-deploying it, it all worked without a hitch. The only bit that I found odd was that the OpenAPI specs were limited to version 2.0.

I didn't want to overcomplicate things with the server app, so I opted for three methods:

• /GET/

• /GET/id

• /POST/start_location

The first GET method would return the top tourist attractions in an area (i.e. London), while the second would return detailed information about a specific tourist attraction. Finally, the POST method would generate a new itinerary, given a start location and return the itinerary in the form of geo coded location coordinates.

The decision to generate the whole itinerary and return it in the POST response, mainly came from necessity. I didn't consider the itinerary generation logic to be too complex, so it was easier and quicker to go down this route. Opting for Pub/Sub plus Cloud Functions instead would have made more sense, but it would have increased the development time, not to mention the cost.

AuraDB

Connecting to the AuraDB instance from the Go app was easier than I expected, but that's largely due to the excellent documentation (in Go!) that the Neo4j team have created. The database connection worked in a fairly standard way, where you would create an instance of the database driver, which in turn would be used to create bespoke sessions. Sessions are sequential and the database driver is responsible for maintaining a list of sessions and managing the requests.

Neo4j's query language, Cypher, comes with an impressive library, including a list of shortest path algorithms. I opted for the Minimum Weight Spanning Tree algorithm, which is often used to solve route planning problems. Given a "cost" (or weight) and a starting location, the algorithm would find all reachable nodes and return the shortest path.

Apple Maps Server

The final piece of the puzzle was calling the Apple Maps Server API to calculate the ETA between the starting location provided by the user and the nearest tourist attraction.

The client app provided users with the option to set the start location of their trip as their current location, so it was a matter of just passing their coordinates and checking which of the top attractions is closest to them. The one that's closest is then used to traverse the nodes in the tourist attraction database. The process worked the same if the user simply selected a start point, other than their latest location.

In Conclusion

From, researching cloud solutions, to deploying apps in the cloud and integrating with other APIs, I had a lot of fun working on this project. The main challenge during this project was the constant switching between programming languages. Some days I'd work on the iOS app in Swift, while on others I'd work with Go on the server app. Add in that mix a new query language (Cypher) and the result was the first half hour of every day being spent on re-introduction to the language basics. I was also very happy with my choice of using Google Cloud. I also have lots of good things to say about the Apple Maps Server API and AuraDB's Go interface. Overall I learned a lot and I'm really happy that I picked it up as a hobby project.