Future features

This is a list of the features that we’ll be looking to add in future. There’s no delivery time assigned to them because that can only be done after analysis and design to find out the true extent of development required. If there’s something that you’d like which isn’t shown here then drop us a line.

  • Video support
  • More reports about your attendance and trends
  • Improved Apple Watch capabilities
  • Improved iPad layout
  • Mac version
  • Android version
  • Windows version

Music Licences

Or why you will see tags such as #ppca, #ppl, #ppca-free, #ppl-free, #stream in the app.

Most countries have licensing systems in place that require a fee to be paid when music is played in a public space.  This includes cafes, bars, nightclubs, and gyms.  There are usually two payments – one for the actual playing of the music (the performing rights) and one for the authors of the songs (the publishing rights).  They are generally standard rates with the former being higher than the latter.  

The entity in Australia is called the Phonographic Performance Company of Australia which is usually abbreviated to PPCA. In the UK it is the Phonographic Performance Limited (PPL). The USA appears to have American Society of Composers, Authors and Publishers (ASCAP), BMI (Broadcast Music Inc.), and Society of European Stage Authors and Composers (SESAC).

In Australia in 2010 the group licensing the performing rights (PPCA) decided to massively increase their chargeable rate.  After court cases they were permitted to do so.  It meant that for each class using licenced music (such as a Les Mills one) a gym would see its music licence fee rise from about $2.50 for the performing rights to at least $15.00 – a minimum sixfold increase.  Multiply that by 30-100 classes a week depending on the size of the gym and understandably there was some consternation over this.  The general view was that it would become necessary to cut the majority of group fitness classes using original music in order to stay financially viable.

Recognising that Australia was one of its biggest markets Les Mills quickly started to release cover versions of the music in their releases there.  That meant gyms still had to pay the publishing rights fee (which stayed sensibly acceptable) but they now didn’t have to pay the performing rights fee.

So if you’re teaching in a gym that pays the performing rights fee then you can play the original music (these have tags such as #ppca and #ppl).  Otherwise you should play the cover versions (these have tags such as #ppca-free and #ppl-free). The latter ones are the equivalent to what used to be known as royalty-free music where you would purchase a CD of cover versions, usually all set to the same bpm and blending in to each other.

So who benefitted from the original spat all those years ago?  Well the PPCA lost some income as a lot of gyms switched over to using cover music.  It also showed other industries that was an option so damaged the PPCA’s reputation and influence.  Les Mills have to pay more because they need to make cover versions although they seem to have fallen on their feet with that in recent years as they can now use them as part of their LMOD product.  As someone who suffered through their introduction they’re competent tracks but rarely exceed the original so the participants lose out.  Overall, someone originally got greedy and everyone ended up losing.  Except the lawyers, of course.

As a consequence of this Les Mills stopped making the cover versions of BODYJAM after eleven releases because it was not financially viable.  That would have killed the programme in Australia.  However, a concerted social media campaign convinced Fitness First – the largest chain in the country with that programme – into negotiating a special licence with the PPCA only for that programme. That continues to this day with the original music while all other programme classes use the cover versions.  Fitness First comes in for a degree of (sometimes well-justified) criticism but in this particular case they deserve credit.

Building the app – The New Version

A discussion of the first version of the app can be found here.

This involved a lot of consideration of users and business aspects before I even started thinking about the design.  The first thought on deciding to create a new version was whether to upgrade the existing one or create a new product.  I decided on the latter because the amount of change I was intending to make made it an entirely new entity with a different user interface, different internal data structures, new data synchronisation, and a new means of charging.  Given that I couldn’t see an easy way of migrating existing data across it made sense to treat it as an entirely different product.

I gave a lot of thought to the method of charging users while I was building it and came up with a number of possibilities over the years.  They all kept circling back to that it had to be a subscription as I needed to be able to cover the hosting and bandwidth costs.  They happened on a regular basis and there was no way I’d attempt to pick a one-off cost and then amortise that over the lifetime of the user.  If I had the skills to do that I’d be working as an actuary.  Since I prefer to treat people in the manner how I’d like to be treated I decided to make the subscription relate only to media transfer.  The meant if they stopped subscribing all of their music would still play on their devices.  I believe people should not be locked in to paying for what they themselves created.  If my product is good and useful people will pay me.  They should not be restricted if they elect to do otherwise.  I can imagine a few companies reading that and going ‘well we don’t want to employ his sort here with those type of ethics’.

Sorting out how to implement that also meant that it simplified development as I didn’t have to worry about adding in large amounts of code to handle conditions about which features are available to the user.  It also allowed a free trial not limited by time to be made available to all users.  

I decided that the existing version of the app will be removed from the App Store when the new version is released.  This is because I do not want to support two versions, especially when one of them is so old, loaded with technical debt, and it is becoming increasingly difficult to generate the ongoing data for it.  The data for it will be updated probably for fifteen months afterwards.  At that point the app will continue to run but no new release data will be made available for it.  No doubt there will be complaints but I consider it a necessary action given my available time resources against the fact that once it has been purchased I receive no further income from that user.  For those who have recently purchased the existing app I will ask them to contact me and I’ll provide a free extension to their purchased subscription on the new version.  I’ll be working on a trust basis with this as adding extra steps for people to prove they have purchased something would only sour relationships built over the years with users.  Plus I believe that most people are fundamentally fair and honest.

I’ll no doubt end up rewriting the above few paragraphs as the events unfold after launch.

So having gone through that the non-people part was far less stressful.  Rather than having a data file pulled down from a server I decided to centralise things on a database.  Eventually I want the app running on Android so that ruled out using iCloud.  In the end I decided on what I’m familiar with from writing share-trading systems at Citibank and went for SQL Server.  After looking through cloud providers Azure seemed to provide the best cost-benefit implementation.

The initial version of the server was running on a local PC with the free version of SQL Server.  I have written SOAP in the past but as anyone who has done the same will tell you: for the sake of our sanity we do not speak about that time in our lives.  For this I decided to use web services which meant implementing a JSON interface.  The main reason for this is the incredibly simple way Visual Studio allows you to build them at the server and the graceful manner in which Swift allows you to interact with them at the client.  The automatic mapping to a structure makes programmers gurgle happily.  Once they had been defined it was simple to build a class which ran in the background and transferred the fixed data from the server down to each client.  Processing the user data was a little more complex since it can be created on multiple devices and there’s the need to synchronise it across those devices while also ensuring the chance of duplicated data is reduced.

The database design required different ways of looking at data storage.  In relational databases such as SQL Server you try to ensure your data is as normalised as possible.  This means having minimal duplication and designing index values in such a way that you can access your data in all the ways you require without needing to add additional processing.  It’s a craft – like woodwork – that takes time to learn as there are some required basics but each implementation is different.  The final result reflects that particular set of requirements plus your own experience.

There’s an old maxim in cricket that if you win the toss then 99% of the time you choose to bat.  For the other 1% of the time you carefully consider all factors and then you choose to bat.  The same goes for using Core Data on iOS devices and Macs.  The original version of the app used an SQLite library because it was close to the SQL & ODBC products I’d used in prior times.  This time I decided to use Core Data as it appeared to have become a more mature and stable product since when I’d last looked at it.  It required some work to understand the concepts as it’s really an object graph rather than a relational database.  Working out how to map the data between Core Data and SQL Server and also how to generate the object graphs for use by the app took time.  Data storage structures tend to remain fixed as changing them can cause knock-on effects through the rest of the system.  That’s the reason we think very long and hard about them and spend a great deal of effort to ensure they serve the best purpose before starting to build the rest of the app.  Measure twice and cut once applies here as well as woodworking.

Once I was relatively happy with the data structures I started coding the screens.  I’d originally started with the extremely high-tech approach of sketching some boxes on a notepad and thinking like a user.  What would the screen do, how would it look, and how would I easily interact with it?  That led to a version I wrote using UIKit.  Which was followed by thinking I could improve things a little bit in certain areas and so going back and changing them.  This was repeated quite a number of times as seeing something working always gave better feedback than simply looking at a static image.  It was progressing reasonably well.

Then March 2020 arrived.

You’d think working from home would be perfect for an indie developer but the whole uncertainty of those first months of lockdown killed all motivation, especially as we worried over family and friends who were far more at risk.  But eventually the mind craves exercise and so I decided to learn SwiftUI.  It was the hot new method from Apple of writing user interfaces.  As is fairly common the first version of it had been flaky and sluggish but it had improved significantly by the second year.  After being in the Apple development environment for a while you begin to recognise which tools they are pushing as to being the long-term preferred ones.  Swift quickly became one of those and it was obvious SwiftUI would rapidly follow.  So I sat down, read a lot of tutorials, watched a number of WWDC videos, grabbed a lot of sample code, and wrote my own bits of it until I felt I understood how it worked.  The slightly annoying part was in my first UIKit-based attempt at this version I’d written a load of code that generated notifications regarding what pieces of data had changed on screen.  This allowed the screen to update only those sections instead of refreshing the entire panel which gave a far better experience for the user.  The following year Apple released a set of APIs which did exactly the same thing.  SwiftUI then implemented the same thing in an even simpler and more concise manner.  You could consider what I’d written as wasted effort but it showed I was on the right track and I was now able to produce a more compact app as a major part of the complexity was now inside the operating system.  With most code less is generally better and perhaps only 15-20% of all code ever written for an app ends up in the final version.

After completing the app to a point where I felt comfortable showing other people I received feedback from some instructors.  Most of it was positive although the more critical responses proved of greater benefit as it improved the app.  I then asked users of the existing version if any of them would like to try a TestFlight version of the new app.  A few did.  A small percentage of them stuck around to provide ongoing feedback.  The worth of that cannot be underestimated.  I told them first up that I wanted their actual views no matter how critical.  And they took to that approach with alacrity.  It was a little bruising to the ego when they almost unanimously pointed out that the way I’d carefully and logically implemented one feature was completely and utterly confusing and they’d like it changed to a way I’d rejected early in the design process.  I explained the reasons for the implementation and how I thought it was better than what they were suggesting. 

A few days later I changed it to what they suggested because they were right.  They have real-world experience whereas I’ve only been observing and talking to instructors and building my view of their world off that.  It’s not fully matching reality and if that doesn’t occur then the product will never be as good as it should be.

Developers can be sensitive over criticism because software is created directly from the thoughts of the programmer.  Therefore some of them treat criticism of their product as direct criticism of themselves.  Learning to separate those two aspects was one of the best things I learned decades ago.

One issue with building it over time is that the underlying technology will change. Recently Apple implemented await / async in the Swift language which is a far better approach for coding when having multiple parts of the app running simultaneously. My decision was whether to switch over to using it or keep with the existing approach. I decided to go for the latter as (a) I judged it was not a good use of my time to rewrite existing working code and (b) because it was a new feature and I’m now at the stage of my career where I prefer using what I know works reliably. I’ve worked with bleeding-edge operating systems, frameworks, and products in the past. They were always interesting and sometimes fun but the time for them is not when trying to finish a project. That said await / async seems to have been a very good implementation (it’s worked well in C# for years) and it’s something I’ll use for new developments and when refactoring in future.

Currently the app is being used in classes.  I do still get requests from my TestFlight people but these are now more minor issues and most of them seem happy.  I’m also not seeing any crash logs appearing which gives both users and me confidence that we’re on the right track.  I just implemented RevenueCat to handle the payment charging (I’ve looked through the StoreKit documentation, blinked, shaken my head, shuddered, and then decided I’ll use a highly-rated third-party product instead). The bits that still need doing are fleshing out the contents of this site, refining the operational processing that will comes with (hopefully) an increase in users, and adding in the last few bits of functionality for the app.

Building the app – The First Version

The app you see on this site is a redesign of the original one. That initial version was written back in 2010 when I first decided I wanted to finally learn how to develop for iOS. That meant learning Objective-C at the same time. Which you soon discovered should be more accurately called [[[[[[Objective-C] the] one] with] all] the] brackets]. Since the best way to learn is to actually create something I decided to combine it with my other love of Les Mills group fitness programmes.

Being honest the first version was a little flaky. The Cocoa frameworks weren’t at the level of stability and polish that they are today. Especially the audio playlist one which was a bit of a problem since the app used it heavily. A helpful instructor used it in a few classes that I attended and we discovered issues such as switching on airplane mode before class prevents her mother calling and interrupting a series of jump kicks in the middle of a track. Phones then had nowhere near the level of ubiquity they do today so it was also learning about how best to use them in the environment of the time.

Eventually I put it out for public use and was surprised to get even a few downloads. I made it free because I wasn’t completely happy with how it turned out. Despite that it picked up a bit of traction through word of mouth. I could tell that because I was getting a few support emails. That was mostly split between “it’s not finding my music” and “it stopped playing during class”. The latter was the most serious and came from trying to use the built-in AudioPlayer class. While it worked for most things it had trouble working reliably with the continual checking I needed to do to track things during a class. So I ended up rewriting that component using AVMutableComposition. That turned out to be far more reliable and the problems playing in class tailed off.

The matching was an issue in the early days and it remains one with the current version all these years later. The problem was how to detect which song on a user’s device matches the one in a Les Mills release. Back then phones had nowhere near the processing power they do now. That meant you couldn’t do the obvious way of today in generating a SHA256 hashcode on each file as that would have taken an unacceptably long time and chewed through the phone battery very quickly. So I decided on ‘fingerprinting’ a CD (there were no music downloads at that time) which involved getting the running time and order of the tracks then comparing that data layout against the albums in a user’s library to see which ones matched. In most cases it picked up quite a lot. The advantage was that as long as the same album title was on all tracks and they were numbered consecutively it didn’t mind what the song titles or artists were. That naming was a major concern back then as no-one used the same format for those fields when ripping from a CD.

There were a few issues because most people simply let the ripping software pull the data down from the internet and it wasn’t always correct. Even today I still get BODYBALANCE 50 albums that state they are BODYATTACK 50. Getting clean data from lots of people is not an easy task so there’s a fair bit of pre-processing that goes on before I can add it into the system.

The matching had two sources of problems. The first was when two releases had the same fingerprint. This was something that became increasingly common as the number of releases increased with time. It was affected by the need to have a 5-second leeway on each song as CD-ripping software back then varied with some adding a few seconds before and/or after a track. So the matching had to be a little fuzzy. Around that time there was a fight between the gym and music industries in Australia which led to a lot of gyms switching to use cover music (see here for the full details). That now meant two versions of a release could easily have the same fingerprint. It meant adding in a workaround where the user had to select which album matched against a release if there was a conflict. It did work but I still remain unhappy at the implementation.

The second and main issue with matching was that it checked against the music in the iOS Music Library. Most people already had their music in there and given that devices then had storage space that was still very small you couldn’t really add more data such was music files into your own app storage area. Assuming people put the whole album into the music library things worked fine. But some people only put a few songs because they’d never be teaching the others again. So my support emails were mostly explaining this to them.

It was at this point in 2013 that I had a bad day and having got another support email decided to put a price on the app just to stop too many more people using it. Bizarrely this increased the number of downloads. I can only assume it was on the basis that if it costs something then it is viewed as having a greater inherent value over something that is free. I wasn’t going to knock back the income even though this does show that as a businessman I’m a reasonably good developer.

Things ran fine after that. Some Les Mills staff even gave me a mention which helped with sales. Then Apple stepped in with their cloud music. That product was a complete disaster with how they handled people’s music libraries. Many remember their live albums were suddenly half-filled with studio versions. It was even worse for group fitness instructors as their purchased versions which had been specially remixed to fit the choreography were replaced by something which had the same name but didn’t have the required extra bridges and chorus. Which made it useless to be played in class. So I had to find out what was happening, add features into the app to minimise the issues it generated, and then communicate it to all the affected users. While the severity of it has reduced over time I’m still having to occasionally do this today – eight years after the issue first started.

Despite the problems I see it continues to do the most important role of all which is providing users with a product which helps them in their life. These are some of the reviews that have been added to the App Store over the years:

It was always my intention to produce the next generation of the app because like most people who have built something and see it every day we can spot the flaws and where it chafes most for the user. So I finally started doing it.

A discussion of the new version of the app can be found here.