I built an iOS App with ChatGPT
The chat bot supercharges the abilities of a non-coder (while getting a lot wrong)
ChatGPT pretty much always occupies a browser tab at this point. I’ve enjoyed finding where it excels and where it falls short. So a couple weeks ago, I decided to see how far ChatGPT could take me in the world of iOS development. I am not an engineer by any standard, and came to this side project with nothing more than a foundational knowledge of Swift syntax (the language of iOS apps) and the functionality of Xcode (Apple’s developer environment). And I love hyper-focusing on side projects that take up inordinate amounts of my time. Sign me up!
I wanted something simple by app standards, but complicated enough to where it involved multiple user interface views and a decent amount of logic. I have been tinkering with different focus hacks lately, and decided that I would create a Pomodoro-style timer app. This type of timer cycles through alternating periods of work and rest, in the following sequence:
Focus - Break - Focus - Break - Focus - Break - Focus - [Long Break] — Repeat
I searched for existing apps with similar capabilities and found an app called Flow. The user interface is clean and the app functionality is minimally complex, a great candidate for this non-coder to (try) to reverse engineer. See here—
Flow is a legitimate company and therefore has functionality beyond the scope of this project, but my app would have three core screens: the main view showing the timer, start / pause button, reset button, and a method of tracking the user’s position in the Pomodoro sequence; a statistics view that tracks the user’s progress through the Pomodoro cycle in addition to any interruptions (i.e. the user pauses the timer prematurely); and a settings view where the user could change the focus, break, and long break durations. There is a navigation bar across the bottom of each view.
*drumroll please* Ladies and gentlemen, meet the much-hyped and venture-backed Silicon Valley darling…
Complete with the obligatory misspelling of an otherwise normal word, the reverse engineering of Flow into a pseudo-startup surpassed my expectations. Let’s dig in to it!
What Worked Well
Before I even opened Xcode or ChatGPT, I sketched out a basic user interface in Figma. Using the real Flow app as my guide didn’t make much sense as 1) it had more functionality than my version would have, and 2) the interface had branding that I wasn’t trying to emulate. I came up with a color scheme and made a fun logo in Canva because no one wants to get sued over their side project, right?
Once I had a basic vision for each screen, the process went something like this—
Code the user interface, not paying attention to most logic just yet (I regret this approach, more on that later).
Connect the three views (i.e. ensuring relevant statistics incremented as necessary, and that the timer responded to the settings).
Figure out the timer logic and visual tracking feature (the 4 circles below the timer).
ChatGPT is quite proficient at generating code for user interfaces. I found UI prompts didn’t need much detail to produce a workable output, but providing as much detail as possible improves results. See below for my first prompt when building out the home page (where the timer would live).
The UI implementation was an iterative process of taking ChatGPT outputs, editing the results myself in Xcode, running into a problem, giving ChatGPT my code to ask for help, and repeating that cycle until I was satisfied. It was during this phase that I realized for the sake of the app’s functionality and my own sanity, I should separate these views into separate Swift files rather than coding them all in “ContentView” (which technically you could do, if you’re a masochist). It made sense to keep ContentView short and clean, and bring SettingsView, StatsView, and TimerView into their own files, calling them as necessary to render the app. You could (and maybe should?) divide these even further but I settled on this arrangement.
With the aesthetic aspects of the user interface largely complete, getting a working app required linking the three screens and coding the timer logic.
The hard part.
Where ChatGPT fell short
Notice how I blamed it all on ChatGPT just now? This is the world we live in. Blame the AI.
First off, I made the mistake of not creating a map of the entire app prior to development. Were I to start over, I would wireframe the user interface *and* the logic, mapping how the screens need to connect to each other. Figuring this out while half way through the process added unnecessary frustration.
Second, 90% of the app functionality stems from TimerView. This file contains the following—
A class containing TimerStats (variables that are incremented as needed and reflected in the Settings screen).
The TimerView struct (too long to include a screenshot)—
A view controlling the user interface of the timer.
Three functions—
startTimer
stopTimer
resetTimer
CircleFill struct containing logic that changes the four circles below the timer, tracking the stages of the Pomodoro cycle.
The hardest part of this project—and where ChatGPT seemed to fall short—was figuring out the relationship between the circles tracking the Pomodoro cycle and the timer moving from focus to rest intervals. There are eight intervals in a Pomodoro cycle, meaning each of the four circles must fill halfway at the start of a focus session, and fully at the start of a rest session.
There are multiple variables to which you could theoretically tie the circle progress - total sessions, focus sessions completed, break sessions completed, and current cycle index (the variable tracking the 8 intervals in the Pomodoro cycle). I tried everything and reworded my prompts to ChatGPT countless times.
The breakthrough happened when I landed on the strategy of creating a CircleFill struct containing a view that responded to several variables—
Index (for each of the four circles)
totalSessions (for the eight Pomodoro intervals)
A bool variable (either true or false) recording whether a circle should be half filled, on the condition of [totalSessions - 1 == Index * 2] - basically, on the intervals 0, 2, 4, 6.
With this logic finally correct after a billion other failures, all I had to do was call the CircleFill view in the main timer UI view—
This code creates a horizontal stack (HStack) of 4 circles, spaced evenly. It uses a ForEach loop to create each circle.
The index variable is used to represent the current iteration of the loop, starting at 0 and going up to 3 (since there are 4 circles in total). For each iteration of the loop, it creates a new CircleFill view, passing in the index and totalSessions values.
The resulting HStack of circles is used to represent the progress of the user's Pomodoro sessions. Each completed focus session is represented by a half-filled circle, each completed Pomodoro session (focus-break) is represented by a fully filled circle, and each incomplete session is represented by an empty circle.
What Now?
The Focus.d team is happy to announce we are raising our first seed round. As a pre-revenue, pre-product unicorn, we are excited for what’s to come.
lol.
The app is (mostly) functional. The timer view works as intended, moving through the pomodoro cycle while tracking the progress via the circles, and then resetting automatically at the conclusion of the long break. Alternatively, the user can reset the cycle at any time via the button in the upper right corner. The settings allows the user to change the focus, break, and long break intervals and toggle the autoflow function which just controls whether the app flows automatically from focus to break without needing the user to press start. The settings view tracks the statistics as incremented in the timer logic and the user can reset the stats at any time.
The app has a major bug, however. The timer functions normally so long as the user stays on the timer screen and doesn’t navigate to either settings or statistics, or closes out the app. Instead, the timer should run irrespective of what the user does unless 1) the pomodoro cycle finishes, or 2) the user pauses or resets the pomodoro cycle. Experienced iOS devs probably have an easy fix for this problem.
Lessons
Map out the entire app, LOGIC INCLUDED. Major headaches for not doing this from square one. This is just good product development practice, nothing to do with ChatGPT.
Break up prompts to ChatGPT. The smaller your questions and code snippets, the better the ouput. I will say that pasting the entire code from a view into the chatbot is great to give it context for where a code snippet exists. But after that, isolate problems to the extent possible. ChatGPT is easily overwhelmed.
ChatGPT knows every piece of Swift syntax, but logic is not a strength. How you communicate the intended logic to the chatbot matters. My breakthrough with the circle progress feature came only after I “visualized” the behavior in a spreadsheet and gave it to ChatGPT. The chatbot had a lightbulb moment of sorts, and soon after we devised the CircleFill struct as a strategy and it worked.
ChatGPT is wrong. A lot. For how much it was instrumental in bolstering my knowledge gaps particularly around syntax, I was often correcting it because it either completely misunderstood my prompt, or made outright mistakes in the code. Humans: 1, ChatGPT: 0.
I’ll continue building out the app functionality and (hope) to launch it on the App Store sometime this year. I’ve learned an incredible amount in the last 2 weeks, so if nothing else, ChatGPT is *the* way to jump into development of any kind. It won’t make you an expert overnight, but it helps build foundational knowledge in a way that, six months ago, was not possible.
The barrier to entry has never been lower. Wild times. Github project linked here for any engineers who want to chime in.
Cheers,
Ryan