iOS. Apple. Indies. Plus Things.

Introducing Elite Soccer Club

// Written by Jordan Morgan // Apr 10th, 2024 // Read it in about 5 minutes // RE: The Indie Dev Diaries

This post is brought to you by Emerge Tools, the best way to build on mobile.

Well, here goes another one — Elite Soccer Club is officially live in the App Store. Please do check it out here!

A screenshot of Elite Soccer Club's running on several devices

The elevator pitch? Elite Soccer (…or, football 😉) Club is an easy way to…

  1. Share lineups with a visually rich graphic [free].
  2. Explain formations or concepts using a traditional whiteboard [free].
  3. And finally, you can record videos with audio commentary to share with your players, staff, parents - whoever [requires pro].

There are some nice little features in there too, like team size, whiteboard customization and more.

Instead of going into the story behind things, I thought it would be interesting to touch on the indie dev aspects of this one. In a recent post, I talked about basing app releases on a particular theme. This one is “Test a hypothesis”. And, while that may be Elite Soccer Club’s raison d’être, I still have other motivations — namely, shipping stuff is just incredibly fun.

But anyways, my theory? Soccer is a global sport, the most popular one on the planet. If I can ship an MVP to help soccer coaches, and later internationalize it — maybe there’s something there?

Luckily, I had a massive head start - Elite Hoop’s codebase. You know the jokes in game development where, when a game is similar to a previous entry, ignorant observers simply say it was “copy and pasted”?

Yeah, well this time, I literally did that…

The Great Refactor

…and it ended up being a lot of work. Luckily, this post was a phenomenal read for the niche crowd of iOS developers who need to wholesale rename a project. By copying and renaming Elite Hoop’s project, I had a really good foundation. Even so, I had hoped this project would take weeks, but it took months.

So much of what Elite Hoops has either isn’t relevant to soccer (i.e. zone overlays, SLOB/BLOB set mode, etc), didn’t make sense to keep or simply was focused on an entirely different sport — basketball.

Keeping all of this in the same app was something I considered, but after going through this, I am so glad I didn’t go that route. There’s too much that’s different between them, and so much room for each to grow - an app for each of them makes perfect sense from a business and maintainability standpoint. There are some things that make sense for both apps, there are many things that are unique to each.

No matter, the result of starting with an existing project to retool? A metric ton of refactoring classes, structs and data access models to reflect soccer. For example, there’s code like this in Elite Hoops:

func addPlayer() {
    if team.roster.count >= Avatar.MaxPlayerCount {
        // Add to bench
    } else {
        // Add to court

But in Elite Soccer Club, team size is variable. A few friends told me separately that the ability to use different sized teams was important, especially in the lens of youth soccer.

A screenshot of Elite Soccer Club's team editor showing different team size options

So, a hard coded team size value no longer made sense. Instead, it’s closer to something like this:

func addPlayer() {
    if team.roster.count >= team.activePlayerChoice.rawValue {
        // Add to bench
    } else {
        // Add to pitch

There were many, small little situations like this — resulting in the development cycle going on longer than I had originally thought. What else is new? It’s like getting a house, but completely remodeling it.

Another tricky one? Positions. In basketball, you generally have a few guards, a forward or two and maybe a big. Reading that sentence back makes sense to those who have played ball their whole life. But, I have a newfound sense of humility to those who may be new to the game.

Why? Because if you read point guard, shooting guard, small forward, power forward and center — you may not know the relation to those things and someone saying “Yeah, he’s a guard”, or “She plays the 4 or 5” but coaches talk like this all the time.

That made little ol’ me trying to figure out soccer positions quite a task. As far as I could make sense of it, soccer positions could have…sub positions?…for lack of a better term. So if you picked defender…you could just be a defender. Or, maybe you wanted to be a centerback. And so on.

One U.X. implication of that meant tapping on one position could reveal several others: A screenshot of Elite Soccer Club's position picker

As such, I had to change the whole data model around players and positions to support that:

static func subPositionsFor(primaryPosition: Avatar.Position) -> [SubPosition] {
    switch primaryPosition {
    case .unset:
        return []
    case .goalKeeper:
        return []
    case .defender:
        return [.centerBack, .fullBack, .wingBack]
    case .midFielder:
        return [.defensiveMidfielder, .centralMidfielder, .wideMidfielder, .attackingMidfielder]
    case .forward:
        return [.wing, .striker]

There are one million examples of this kind of thing that I ran into, but you get the idea. Copy and pasting things means a long road of tiny to medium sized headaches, whereas creating a #BrandNewThing is a massive adventure all its own. I’ve done both, but I’ll always reuse my work where I can and where it makes sense. And this? This was the perfect time to do just that.

However, there are positives to this too. I’ve come across some novel quality of life tweaks I’d love to take back to Elite Hoops. Of those, editing player appearance labels (i.e. name, position, shortened position, number) is nice — and I came up with a grid layout system to “pretty place” players initially. Instead of a scrambled mess of players placed randomly all over the pitch when you first open a team, they are uniformly placed in spots that make sense.

My Soccer/Football/Futbol Diliemma

Originally, this app was called Elite Football Club to cater towards the world at large. Plus, I did enjoy the E.F.C. acronym. In fact, the URL reflects as much: https://www.elitefootballclub.com - but I ended up discovering how non-trivial it would be to get the name of the sport correct.

To wit: An illustration of what the world calls soccer across different locales.

Long story short? It’s on the list. I’d love to call the sport by the correct name. Plus, other things like does this locale call the coach a coach, or is manager more common? That sort of stuff is over my head right now. But, if I was going to launch quickly — I simply had to stick within my comfort zone.

But that’s the thing, right? This app is also pretty far outside of it. I played soccer when I was a little dude for like one season when I was seven. I was the kid who sat on the grass when the soccer ball was not immediately within my vicinity. I mean, I probably stayed there even when it was.

Lipso facto, there is a lot I don’t know about this sport. Thankfully, so many friends gave me a hand. But, I know there’s still quite a bit I will get wrong. So this release is all about adapting quickly, and politely responding to the emails from more experienced coaches who will tell me X or Y makes no sense as implemented.

Website and Quick Wins

Regardless, speed was my goal here. As I’ve established, I already had a battle tested, sturdy foundation to build off of. So, in the spirit of shipping — here are three other ways I achieved a somewhat quick launch.

The Logo
I needed a logo fairly quick, so I simply worked with friend of mine who does graphic design work for me here and there. He was able to turn around a soccer-ized version of what I had for Elite Hoops within the day. Thanks, Kenny! A screenshot of Elite Soccer Club's logo versus Elite Hoop's logo.

The Website
I swear unto thee, I could stand up a website with my eyes closed, seared shut now until forever more and upside down and backwards. I’ve simply found my tech stack:

  1. Jekyll for static site generation.
  2. Github for source control.
  3. Netlify for deploys, hosting and domain registration.
  4. Tailwind CSS for everything else.

I simply push to main, and my website is updated. That’s how this blog is made, my book series website is done and everything else I’ve made on the interwebs.

As such, putting this puppy up took about two and a half hours from conception to production. It embodies the “it ain’t much, but it’s honest work” ethos: A screenshot of Elite Soccer Club's landing page.

The App Icon
Here’s another one I was worried about, but was able to push through. Essentially, I looked at the crest of several soccer club emblems for inspiration, added the laurel symbols converted into shapes, took a shield-like shape from Elite Hoop’s icon, tossed in a soccer vector that Kenny had made and….dashed in some gradients.

And, to me at least, that was good enough: A screenshot of Elite Soccer Club's logo in Sketch.

Superwall Dogfooding

Finally, a new app was a great time to dogfood Superwall. Not much to say here, other than I’m going to test paywalls so hard: A screenshot of Elite Soccer Club's Superwall dashboard.

Final Thoughts

Launch day.

Nothing beats it, I’m thankful to have another one today. I look forward to serving soccer/football/futbol/futebol/sokker/etc coaches of all levels.

Until next time ✌️.


Spot an issue, anything to add?

Reach Out.