Jacob Maizel
ai generated fitness focused header image
Fitness Competitions App DemoA fitness competition app built with Go, SwiftUI, and NextJS
iOSiOS
WebWeb
WatchWatch
Completed
Open Source
Demo
Video
Go
SwiftUI
NextJS
TypeScript
Docker
Redis
PostgreSQL
Heroku

Published: Dec 12, 2023

NOTE

This project is not finished, and is not intended to be. The main purpose of this project was to learn SwiftUI and Golang, and to get a feel for the development process of a full stack application. Please keep this in mind while reading through the code. That being said, feel free to use this code as a reference for your own projects, or to learn from.

Inspiration

Front end development is a daunting, scary world, and I could not find an obvious way in by tip toeing around the outside. The only option was to dive head first and see how much I could learn.


I used the usual tactic of popping the first idea I saw off the top of my apple notes titled:


cool app ideas


It looked something like this:


Group fitness comps ios + watch


Not much, but enough to work with!


About

This project consists of an ios app, watch os app and a backend api to drive it. The idea was to create a platform for fitness competitions, where users could create competitions, invite friends, and compete against each other in various fitness challenges. However, the functionality I left off at is the following:


iOS App


Watch App


Backend API

Tech Stack

Why this tech stack?

I had a few considerations to account for while researching for a tech stack:

  1. Ensure I pick well documented frameworks: It's significantly easier to learn if you have solid documentation to refer to, especially starting from total scratch.
  2. Pick a stack that will actually benefit my career as a developer, not strictly for fun: I wanted a stack that I had a non 0% chance that I could actually use these at a company one day.
  3. It has to be iOS: I'll admit it, I am fully engulfed in the apple ecosystem, and wanted to learn the native development tools.
  4. Anything except Django/Python for the backend: I wanted to branch out to different languages and frameworks for backend architecture, since Django was my life at the 9-5.

Backend Architecture

Tech stack

Language: Go
Golang was high on my to-learn list for a few reasons:


Web Framework: Gin
I was enticed by Gin's promise of simplicity, efficiency and speed. It is a very lightweight framework that is easy to get up and running with. It also has a very active community, and a large amount of plugins and middleware to choose from. Many services have Gin specific plugins available as well.


ORM: Ent
I was extremely impressed by Ent. Their documentation gives us examples of 99% of use cases you could come up with, and for those 1% of situations you can find an answer to their, they have an active Discord community that is very helpful. Ent managed Database migrations, schema generation and has a great query builder. Highly recommend.


DB: Postgres
The database choice here was also a no-brainer. Postgres is not only what I was most comfortable working with and deploying, but it is performant, scalable and has a rich feature set for any use case you could imagine including things like full-text search, json-field support, as well as PostGIS for Geometric calculations.


Cache: Redis Stack
For a project of this scale, this was not necessary at all. However it was a great experience getting to know not only Redis (the cache) but also the other plugin products they offer like Redis Search. It's super easy to set up with a docker service, and easy to host using their cloud service.


Containerization: Docker
Docker was only used for local development. With go it is simple to put together an optimized container for deployment as well, but as we will see shortly, my choice of deployment platform streamlined the process for me.


Services

Authentication: Auth0
I chose Auth0 due to its well supported SDK Library for all of the platforms I needed. Their Swift Library has fantastic walkthroughs, blogs, and technical documentation that will walk you through integration step by step.
Also, I am an advocate for not solving a problem that has already been solved 50 billion times by really smart people, especially authentication.


Error Logging: Sentry
I chose sentry to handle error logging for similar reasons to Auth0. Extremely good documentation and well maintained SDKs. The amount of time and effort you will save by picking services based on those two factors will be enormous.


Telemetry: New Relic
While I did not end up taking nearly full advantage of what new relic has to offer, I did plug it into the backend api to start getting a feel for tracing and telemetry/APM since it was still very foreign to me at the time.


Hosting: Heroku
For hosting, I wanted the most plug and play solution there was. I had some basic familiarity with Heroku and just wanted to get up and running. Their BuildPack support for Go made it a breeze to set it up, with automatic HTTPS support as well, for less thant $10 a month.

Project structure

I tried to keep the project folder structure as flat as possible. Golang's simplicity made configuring the project structure a breeze.


Endpoints: /api/<resource_type>/endpoints.go
To keep it simple, all of the endpoints for a given resource are kept in a single file.


Middleware: /middleware/*.go
The only middleware I needed to write for this demo was a logger to intercept the request and response, and a middleware to handle authentication, which was handled by Auth0. The auth middleware also injected the user object into the context, so it could be accessed by the endpoint functions.


Models: /ent/schema/<resource_type>.go
With Ent, all of the models are defined in a single file, in order to be picked up by code gen. You can customize the path to the models, but I found this to be the most simple.


Server Object: /server/server.go
Unlike Rust (which we will see in a future article), Go makes it super easy to set up a shared server object that can be used across the entire application. This is where all of the entry points into the service sdks are held, so they can all easily be used no matter where you are in the application. In the main server functions held in main.go, we simply go through these services and initialize them all before starting the server.


Utilities: /utils/*.go
I kept all kinds of miscellaneous utilities in this folder. Things like error handling, logging, and other helper functions.


Redis: /redis/redis_helpers.go
Here I kept all of the redis search indexing functions that were triggered to update the documents by ent hooks when a resource was created or updated.

Frontend Architecture

Tech stack

Mobile: SwiftUI
Since supplying a native iOS experience was one of my prerequisites, this was an easy choice. SwiftUI is well established enough to support creating apps far more complex than what I needed it for, and thankfully, apple has created a great developer workflow allowing SwiftUI to be used across watchOS and iOS apps. Not only that, but Swift in general was extremely interesting for me, since it seemed like a nice middle ground between python


NextJS: Web
Although it isn't the star of the show, I still needed a web platform in order to deeplink into the iOS app and initially intended to have a web-app counterpart as we will get into later on. I bounced between several "Top web frameworks 2023" articles, and the majority of them had NextJS topping the list. Not the best reason to pick a framework, but good enough for me at the time!


Services

Authentication: Auth0


Error Logging: Sentry


Telemetry: New Relic


SwiftUI Project structure

Views: /Views/*.swift
I seperated all of the individual shared lower level components, and higher level views into their own files.


Models: /Models/*.swift
All of the models that I used to deserialize the JSON responses from the backend were kept in this folder.


Services: /Services/*.swift
This folder held all of the iOS Service managers, for things like HealthKit, WeatherKit, Notifications etc. As well as the model definition for the Datamodel which was the main object that held all of the data for the app. It also held /api which was a folder that held all of the endpoints for the backend api.

What's Next?

I could go into great detail in this blog, but I think the short intro above gives you enough context to jump into the code and take a look around. All in all this demo project ended up being a fantastic learning experience, giving me the confidence I needed to jump into the world of full stack development.

Back to Top