FCFinder V2 - Football (Soccer), GraphQL, and SwiftUI Pandemic Adventures

FCFinder V2 - Football (Soccer), GraphQL, and SwiftUI Pandemic Adventures

FCFinder V2 is an application for displaying the locations of football clubs by league and by year.

Background

A combination of events earlier this year led me to my most recent personal side project, FC Finder V2. Specificaly, I’d been wanting to explore GraphQL as well as start learning SwiftUI, especailly after Apple announced it’s second rev at WWDC in June. My problem was that I couldn’t decide on a good way to do so until the timely combination of the COVID-19 pandemic and the deprecation of Mapbox Classic Map Styles.

The European and North American pandemic shutdowns in the spring resulted in the last 2 months of the traditional season of football leagues in Europe being put on hold until pandemic could be managed in each country (except for the USA 🤦‍♂️). Seasons traditionally start in August and end in May so that meant in order to begin the new 2020-2021 season many leagues had to first complete their 2019-2020 seasons. The result was that there was a glut of football being broadcast in the USA over the summer as different countries’ leagues returned to finish their seasons and I became curious to learn more about these leagues and their clubs, many of which I’d never watched before.

I’d built a Web application called FC Finder back in 2012 that mapped football leagues’ clubs and displayed the current favorite beers of the local supporters using the Untappd API. There were a couple of major problems with repurposing this Web app though, namely

  1. The hardcoded club rosters had become out of date due to relegation.
  2. The map stopped displaying due to the End of Life of Mapbox Classic Map Styles.

From this FCFinder V2 was born.

Extensible Data Model Served By GraphQL

Extensibility was the primary feature for the new system. While the original had leagues and their clubs hardcoded client side, the new system needed to be able to support different countries, different league tiers, and different seasons. To solve this problem I created a data model containing Clubs, Leagues, and Countries that were tied together via a lookup table called ClubsToLeagues running in PostgreSQL.

I had never worked with GraphQL before so I was eager to find out for myself how it all operated and if it lived up to the hype. I decided that I wanted to write the server application in Kotlin and host it on Heroku. I was really pleased to find and make use of Expedia’s GraphQL-Kotlin library for this project. I found the library and examples to be exactly what I needed to not only wrap my head around how GraphQL worked but also how to then build out what I needed. It integrated easily with the Spark Java Web framework and JetBrain’s Exposed ORM to provide a clean multi-tiered architecture.

One of my favorite learnings about GraphQL is that generates API schema when run. This is immensely helpful for confirming what has been built while in active development. It’s also incredibly helpful for clients that will make use of the API. That said, I found that it’s absolutely imperative to use the GraphQL Playground app to work with it.

Thanks to all of this open source software and community I was able to learn GraphQL and to build a functional GraphQL server that powers the client application. To pay it forward I’ve also made my server code available on GitHub for others to see and hopefully learn from. It’s available at:

https://github.com/bleege/FCFinderV2

SwiftUI Client

On the surface SwiftUI’s opinionated design, namely a stateless declaritive UI that reflects a dynamic data model, makes a lot of sense. In essence it’s a classic MVVM design pattern. In practice though I found it to be more of a lift to wrap my head around it than I originally anticipated. Specifically, the interplay of the new annotations @ObservedObject, @Published, and @State and their bindings to the View’s objects. The biggest issue was with the Picker and getting it to track selections. I eventually was able to settle on a solution by specifying a .tag() for the Text in each Picker and binding it the @Published var in the ViewModel. It makes sense now that it has been built but building it initially took more thought than I originally had expected.

Once I was able to nail down the data state binding issue I found the rest of the build out to be quite smooth. The Combine Framework is a very welcome shift in the Apple ecosystem to provide out of the box support for reactive applications. I’m a big fan of Rx so it’s nice to no longer have to bundle 3rd party libraries to build a reactive application. Combine does a good job of providing a lot of the necessary glue that binds SwiftUI’s View and Model so this whole system works. That said I did find that just like RxSwift really shines when paired with RxCocoa, Combine is very much missing that Widget wrapper layer. Thankfully the Combine Community is actively building out the CombineCocoa library to fill this gap.

I’ve also made the source code for the client available on GitHub. Please feel free to explore it at:

https://github.com/bleege/FCFinderV2App

Thoughts

I really enjoyed this project (not to mention definitely filled some of the time gap caused by the pandemic lockdown) and getting to wrap my head around both of these relatively new technologies. I can see the merits of GraphQL, but like anything it’s not a one size fits all tool. I think it’s major use case is going to be as an abstraction layer for existing data services.

As for SwiftUI, I think it’s a major step in the right direction for Apple. That said it still feels a bit under baked (see MapKit’s Map). It’s also held back by being an iOS 13+ technology (and iOS 14+ only in cases like MapKit). Thankfully iOS 12 usage numbers should finally start to dry up in many mainstream apps which will allow SwiftUI and Combine to start being used more. I think Combine is actually going to lead when it comes to adoption as it’s easier to refactor existing app plumbing.

Next Steps

Next on the TODO list to keep adding countries, leagues, and seasons to the data model. I really love seeing the clubs mapped out, especially Tier 1 vs Tier 2 in the same country, as I find it an interesting way to learn about likely rivalries (derbys) between clubs as well as get a feel for some of the socio-economic situations for different clubs and their supporters.

I’m also considering adding macOS support to the client. One of the promises of SwiftUI is that it’s relatively easy to be able to build apps that work well across all of Apple’s hardware and OS environments. I’d like to get a feel for just how true that is.