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.