Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How Apple Killed my App

Avatar for Leeds Mobile Leeds Mobile
November 21, 2025
4

How Apple Killed my App

Join Colin as he tells you the tale of building his first native iOS app and how following Apple’s recommended development approach left him unable to achieve his vision, leading to him killing his app

Colin Wren is a Senior Software Engineer by day and wannabe Product Engineer at night. He helps run the LeedsJS and MoT Leeds meet ups and when not writing code can be found failing to merge Chiptune and Doom Metal into some horrible hybrid that no one but him would ever want to listen to

Avatar for Leeds Mobile

Leeds Mobile

November 21, 2025
Tweet

Transcript

  1. About Me I’m a Senior Software Engineer here at Genio

    but the story I’m going to tell you today is from an ever evolving side-hustle I have of trying to create my own product. The latest iteration of which, has been a self-reflection journal app for iOS called Garner that ultimately ended being killed off due to Apple’s nonsense.
  2. My mobile developer journey I’m primarily a web developer but

    I’ve built mobile apps in the past: 2012 - Native iOS app for St George’s Hospital 2013 - Cordova app for Somerset County Council 2014 - Native iOS & Android apps for St George’s 2019 - React Native app for The Silph Road 2020 - React Native app called JiffyCV 2024 - Native iOS app called Garner Highlight of my career was having my hand featured in the local newspaper
  3. Garner - Who keeps a log of the projects they

    work on, skills they learn and business value they deliver at work? - When you have career progression meetings, reviews or look for a new job this information is key - It’s really easy to focus on “doing things” and not reflect on what those things mean for you
  4. Garner - Garner is a self-reflection career journal for iOS

    - Users are in control of how they reflect and can update questions they answer as needs change - Users can export the data captured in their reflections to put into tools that help them create CVs or performance review evidence
  5. Garner - Garner is a self-reflection career journal for iOS

    - Users are in control of how they reflect and can update questions they answer as needs change - Users can export the data captured in their reflections to put into tools that help them create CVs or performance review evidence
  6. Garner - Garner is a self-reflection career journal for iOS

    - Users are in control of how they reflect and can update questions they answer as needs change - Users can export the data captured in their reflections to put into tools that help them create CVs or performance review evidence
  7. My first product - Silph Road had me working on

    an app that never got released, this annoyed me massively - I thought I can build my own app, so I made JiffyCV - JiffyCV was born out of a pain I had when a start up I was working at collapsed and I had to find a job - I wasn’t prepared to sell myself - I couldn’t remember what I’d done in those years - I had a range of positions I was applying for and needed a bespoke CV to play up my strengths for each role - JiffyCV was a CV generator - you could have multiple representations of your experiences to cater for different jobs - You could generate a CV, in a jiffy
  8. My first product failure - JiffyCV took 7 months to

    build and in the first two months that it was out it made a whopping £14! - It turns out that I knew how to write code but not build a product - I burned out pretty bad after shipping JiffyCV and ended up killing it off because I hated the idea of working on something no-one liked - I spent the next 3 years trying to not think about it
  9. Learning from my failure - JiffyCV had some good ideas

    - They were just poorly executed - Checking people can use your app isn’t validating the idea behind it - Focus on learning more about the problem you’re solving, not verifying your solution
  10. Seeing opportunity - One piece of feedback from JiffyCV was

    that creating a CV was only useful for people looking for a job - How would a person in work get value from the app? - We explored adding a journal to record what they did in their current job, ready for their future CV - We got to the prototype stage but then I had burned out
  11. Getting back on the horse - In 2024 there was

    no longer a need for a CV building app - AI can generate CVs without needing structured data - I used Ballpark to conduct user research - Open questions gave me qualitative data - Closed questions gave me quantitative data - From 20 people I found: - 75% thought keeping a log was a good idea - 55% already kept a log of things they did at work - 60% wanted to use their logs to reflect on their career progress - These results gave me the confidence to build Garner as I was already reinforcing existing behaviours
  12. Scoping my MVP - I thought about the system around

    self-reflective journaling - Reflect - User talks about what they’ve done and the impact - Grow - User makes a plan to improve impact of what they did - Thrive - User makes use of the personal growth and the log of what they’ve done to get promotion or a new job etc
  13. Scoping my MVP - I had my MVP - Setting

    up the questions to ask during reflection - Adding a reflective journal entry - Exporting the data in order to use it - I had goals for further understanding - Would users update their questions and how often? - Would users use the reminder function or do ad-hoc reflections? - How would users share their exported data?
  14. Choosing my tech stack - JiffyCV was a React Native

    app - iOS was the only platform we made money on - React Native is meh, web or native app is better - For Garner I wanted: - To mitigate the risk of dealing with user data - The option of having multiple apps share data - Using AI to format the text from the journal entries into CVs, performance reviews etc
  15. Choosing my tech stack - I recreated JiffyCV using: -

    SwiftUI - SwiftData - App Groups to share data between JiffyCV and another app - PDFBlocks for creating PDFs using ResultBuilder pattern - Xcode Build for Continuous Delivery - This worked well enough so I felt confident that the technology would help me get my MVP out fast and then enable further development - I really liked ResultBuilders, it reminded me of CSS
  16. Hacking together my MVP - The app had 3 screens

    - Settings to set up the questions and set a reminder - Reflections list to see the reflections added - Reflection entry form - Intents were used for the creation and editing of the Settings and Reminders
  17. Hacking together my MVP - Apple suggested new apps use

    - SwiftData - SwiftUI with @Query hooks - This would future-proof the codebase for future iOS updates - This also give me the option to add new platforms as I grew my app - Building my MVP was really quick - I had was really energised by it
  18. My MVP was hacky - I booted up my app

    - The data was out of sync - I’d add a question and it wouldn’t be in the reflection entry screen - Relational data would shift position - The questions could be ordered, but the order would change at random in the reflection entry screen - Typing in an input in the reflection entry screen would crash the app! - All that energy I had dissipated
  19. Unhacking my MVP - The MVP was easy to build

    but hard to debug - I had no definition of state transitions within the app - I had no way to prove the app acted correctly when I did things - I had no way to assess the risk and flag edge-cases - I set up X-Ray and created manual test cases that helped to define my app’s behaviour - I looked into unit testing in Swift with SwiftTesting - In order to unit test SwiftData I needed better control over the ModelContext, MVVM helped - I finally had a means to ensure everything worked!
  20. Unhacking my MVP - The MVP was easy to build

    but hard to debug - I had no definition of state transitions within the app - I had no way to prove the app acted correctly when I did things - I had no way to assess the risk and flag edge-cases - I set up X-Ray and created manual test cases that helped to define my app’s behaviour - I looked into unit testing in Swift with SwiftTesting - In order to unit test SwiftData I needed better control over the ModelContext, MVVM helped - I finally had a means to ensure everything worked!
  21. Unhacking my MVP - The MVP was easy to build

    but hard to debug - I had no definition of state transitions within the app - I had no way to prove the app acted correctly when I did things - I had no way to assess the risk and flag edge-cases - I set up X-Ray and created manual test cases that helped to define my app’s behaviour - I looked into unit testing in Swift with SwiftTesting - In order to unit test SwiftData I needed better control over the ModelContext, MVVM helped - I finally had a means to ensure everything worked!
  22. Unhacking my MVP - The MVP was easy to build

    but hard to debug - I had no definition of state transitions within the app - I had no way to prove the app acted correctly when I did things - I had no way to assess the risk and flag edge-cases - I set up X-Ray and created manual test cases that helped to define my app’s behaviour - I looked into unit testing in Swift with SwiftTesting - In order to unit test SwiftData I needed better control over the ModelContext, MVVM helped - I finally had a means to ensure everything worked!
  23. Unhacking my MVP - The MVP was easy to build

    but hard to debug - I had no definition of state transitions within the app - I had no way to prove the app acted correctly when I did things - I had no way to assess the risk and flag edge-cases - I set up X-Ray and created manual test cases that helped to define my app’s behaviour - I looked into unit testing in Swift with SwiftTesting - In order to unit test SwiftData I needed better control over the ModelContext, MVVM helped - I finally had a means to ensure everything worked!
  24. So what went wrong? - My data model wasn’t “simple”

    so SwiftData’s saving “magic” didn’t work consistently - Relational fields aren’t ordered, even though you set them with Arrays - Apple’s tutorials on List & Detail apps in SwiftData have you creating an item, then editing it, with updates saving to the model - Updating fields with an inconsistent ordering will lead to re-renders that break the pointers SwiftUI uses and cause a crash Question Reflection Configuration Reflection Answer Pulls questions from config Creates answer to question Changes to questions create new config
  25. Releasing my MVP - Refactoring my code to use MVVM

    meant that I could unit test my app’s data models, views and logic and be sure they were working as expected - Using my test cases I could manually end-to-end test my app and be sure that the user experience was smooth and bug free - My test cases informed my analytics strategy with PostHog - I decided it was in good enough state to publish to the app store and start using it to get feedback from users - Xcode Build made publishing to TestFlight and to the store really easy for me
  26. After releasing my MVP - I talked to people and

    had them use the app - I asked open questions to get qualitative feedback without leading them to the answers I wanted - I iterated over the issues people were having with it - I built up a plan for version 1.1 - This would expose the “grow” part of the system - Users would be able to create actionable goals during a reflection - They would then tick off the goals as they completed them in subsequent reflections
  27. It seemed promising - My initial spike branch for the

    data model changes seemed promising, I could add an action item during a reflection and then have a separate screen showing all the action items - I then wrote out my tests cases, wrote my unit tests across as I had done before and everything pointed to it working Question Reflection Configuration Reflection Answer Pulls questions from config Creates answer to question Changes to questions create new config Action Adds Action against Question
  28. Version woe.woe - I started to see inconsistencies - My

    unit tests would say the relational fields were saved - The running app would show the relational fields as not saved
  29. Version woe.woe - I got very annoyed with SwiftData and

    started looking at alternatives like GRDB or even CoreData - There’s no documentation on doing this - I didn’t want to jeopardise the data already in production - I didn’t want to spend time having to re-architect
  30. The impact this had on me - I didn’t want

    to work on my app - Everytime I was in Xcode I was debugging SwiftData badness - I wasn’t working on features - I wasn’t having fun - I figured I needed to take some time off - I took one month off, that then turned into 2 months off - I finally felt ready to try again but within days I was back to being frustrated
  31. Death. Kill. Deploy - I took a step back and

    looked at what my goals were with the app - I wanted to validate my idea - I didn’t want to be on-the-hook for accounts and personal data - I wanted to have fun - I realised that I had let learning a new tech stack cloud my judgement - I killed the app and took it off the App Store - I’m taking the time to focus on building my product development platform
  32. Colin’s final thought - Focus on learning more about your

    user and have that focus embedded into your tech choices - Don’t let your tech choices shape the way you receive feedback - It’s hard to wear many hats, you’ve got to get good at balancing them or find someone to wear each one - It’s good to build a product that solves your problem, it will keep you engaged - Writing test cases will get you 80% of the way towards understanding the technical approach you need to take without writing any code - Take care of yourself, and each other