Today I’m going to mix it up a little bit to start of my ‘7 posts in 7 days’ target. Instead of a classic tutorial where I share some of my code and how that code can be applied in different scenarios, I wanted to share some of my experiences from releasing Plate-It version 3, the biggest app update I’ve done since I started creating iOS apps.
About the App#
Plate-It is an iOS app for license plate collectors, a hobby popular in North America and across the globe. Collectors often focus on plates from particular regions; the ‘50 state run’ is the most popular run that collectors first aim to complete.
The app was first launched in 2016 as ‘My License Plate Collection’, before it was rebranded as ‘Plate-It’ in 2018 with a user interface that made the app a much better iOS citizen. Users can add their plates, organize them into runs, and add plates to a wishlist. Since April 2020, the app’s backend has been hosted on AWS, using Cognito for authentication, DynamoDB for data storage, AppSync GraphQL for all my data synchronization and S3 for image storage. Before then, I had my own backend that I had written in PHP and hosted myself, but this was not ideal considering I was still living in the UK and most of the app’s users were in North America.
The app is free to download, but uses a subscription model with a lifetime option for those who don’t like
recurring payments. The lifetime option is just over 4 times a yearly subscription, which I believe is a
good trade off. I’ve heard from users that have said they didn’t use the app when it only had 3 month and
12 month options, but have started to use the app since a lifetime option became available. RevenueCat is
what makes all this popular, and I’d highly recommended checking them out if you want to work with subscriptions
without all the
New Features in v3#
This was the most requested feature from Plate-It users, however I also understood that this would not be for everyone. Some users use Plate-It to keep track of their own collections without worrying what other collectors were doing, so it was important that I make this an optional feature.
Users can now create a profile that is seen by other users, and make their plates, runs and wishlist public. Individual plates and runs can be made private if desired. Additionally, users don’t need to create a profile in order to explore other collections - they are prompted just once when opening Explore mode for the first time. This feature is also not behind a paywall; I saw this as an opportunity to show users who were on the fence about paying what Plate-It can do. I will find out over the next few months whether this decision pays off.
Again, a highly requested feature by users. I was previously using
a popular library for rotating and cropping images. However, it was only possible to rotate images by 90°,
and where Plate-It looks best when images are cropped into a perfect rectangle, users were frustrated that their
plates would look off unless the perfect photo was taken.
Mantis, a Swift package inspired by
had the image rotation features that I needed. Mantis was really easy to integrate into my SwiftUI app
despite being UIKit-based, and I was able to submit some feature requests that were actioned very quickly.
This was a big win for Plate-It.
Previous versions of Plate-It would allow users to see which states and provinces in the US and Canada they
didn’t have plates form. This data was displayed in a
UICollectionView, which did the job but didn’t look
great. After reading a tutorial from raywenderlich.com
MapKit and overlays, I was inspired to revamp this feature.
Now required states and provinces are shown on an interactive map of the world. At present this feature only supports the US, Canada, Mexico and Australia, though this is easily scalable as I add more geoJSON files to the app. Users can toggle individual countries off if they desire, so if they’re not interested in collecting Mexican plates for example, they’re not inundated with lots of red states on their map.
Not strictly a new feature, but just as important. I spent more time perfecting the user interface of existing features than I did on the new features combined. I wanted to ensure that all features were accessible and a pleasure for all users to use.
The biggest change occurred on iPad, where I moved from a tab-based UI to one with a sidebar. More apps are moving to this UI design, and I used the Photos app as an inspiration in this case.
I also shifted the color scheme for dark mode from a hard-black with an orange accent to a dark-blue with the same light-blue accent that I use in the light mode. I use my phone in dark mode all day, everyday, but found that I would switch to light mode when using the app. Why would others like this color scheme if even I won’t use it? This prompted the change in dark mode color scheme, inspired by Twitter’s dark mode color palette.
The SwiftUI Rewrite#
I’ve already talked about my switch from
Mantis in the previous section, however that
switch was fairly minor in comparison to everything else that changed.
To cut to the chase, Plate-It version 3 saw the app get the SwiftUI treatment. Version 2.8.4 - the last version on the App Store before this big update - was about 40% programmatic UIKit, 30% storyboards and 30% SwiftUI. A healthy cocktail of different ways of building iOS apps in 2021! Now we’re at approximately 95% SwiftUI, 5% programmatic UIKit.
There’s a good, ongoing debate about whether SwiftUI is production ready. I am of the opinion that it is almost production ready, in that there are some small, missing features that require falling back to UIKit or another hacky workaround (eg. first responder for text fields). My aim with Plate-It is to prove that complex apps with a backend making many network calls and views can be written in SwiftUI and still be great apps. Based on user feedback so far, I’d say I’m proving my point.
As this was the biggest app update I’ve made so far, it was inevitable that there would be some lessons that I can learn and take forward to my next big app update.
For the first time, I had a beta program with Testflight so that some of Plate-It’s users could test out some of the features before I pushed it out to others. These users had some very valuable feedback and reported some bugs that I’d missed, making Plate-It much more solid than it otherwise would have been. I will certainly be using Testflight for future updates and releases of all my apps.
This update wasn’t without its issues though. It’s been 3 days (at the time of writing) since the update, and the one issue that was reported was that those who had purchased a lifetime membership were being treated as unsubscribed. I was baffled by this issue for most of the weekend - my test users with lifetime memberships were working just fine. Then it hit me: my test users all had expiration dates 200+ years in the future (whether I did that or RevenueCat, I’m not sure). The users reporting this issue didn’t have an expiration date, and as the app was using the expiration date to determine whether a user was subscribed or not, it was treating them as unsubscribed. I feel silly that I let that slip through the cracks, and it was a flaw in my approach to handling paid vs unpaid users, but I know I won’t make that mistake again.
Plate-It is now well-placed for the new features I have planned. I learned a lot about good, accessible UI design that I am looking forward to applying to other projects that I work on.
This is the first time I’ve written about my experiences with a big app update. I intend on doing similar for future releases and updates of apps, so I hope this will help anyone who may be preparing to go through the same thing.
This post is also the first of 7 that I will write this week. The remaining 6 will be tutorials, sharing code that I used in Plate-It that you are free to use in your own apps.
If this post has inspired or helped you in any way, I’d really appreciate a share on Twitter. You can reach out to me via Twitter if you have any questions, or to show me what you’ve been up to!