Yesterday I took the idea of prompting to make an iOS app much further than with my experiments of last week. I have this web-based version of a currency conversion app – which you can try here – and I converted most of the logic to an iOS app.
I wanted to see how far I could take things. After an intense 7-ish hours of working on the app (and at least 3 coffees) I had something that I almost consider releasable to a general public.
This is a video of the almost-final result, where I was testing some non-phone cases:
When reading this post, keep in mind I am not a developer by trade, I am a UI/UX designer that likes to make front-end prototypes, but most of the time sticks to designing in Figma.
The only apps that I (mostly) coded myself are Screenshot to Layout, a Figma plugin and Obra Icons, my icons project. My day job is design.
After recording this, I fixed a few more bugs and called it a day. About the app itself:
- I first implemented the basic functionality: calling the API, making sure you could convert between 4 static currencies
- Then I implemented the ability to change currencies. I was impressed how the default
List
in SwiftUI handled item deletion without needing any custom implementation. - I then implemented how to hide the API key secret in a proper way, using a
Secrets.xcconfig
file - I implemented a custom logic for the cursor and text field to work in combination with the virtual keyboard, so that the keyboard can stay on the screen whenever the fields have a value.
- I learned how to work with
Localizable.xcstrings
to provide a dictionary for localisation. The app is currently working in 7 languages and I can easily add more. - I tested the app for accessibility with VoiceOver and it worked fairly well.
- Then I went on to make things look nicer. I learned more about SwiftUI layout (
VStack, HStack
,Spacer
, and their modifiers). Also, using the system colours (Color(.systemGray6)
and font logic (.font(.caption)
) - Finally I tested the app for several cases like dark mode, iPad usage and horizontal usage
- Using the app on my real device led to some findings, for example when you kill the app, the chosen currencies should persist in some sort of user storage. I fixed that.
In the workflow atmosphere, here’s my notes about how I worked:
- I used Claude in the browser until I ran out of credits, at one point it was 1PM and it said I didn’t have credits until 4PM. I then switched to using Cursor Pro using Claude Sonnet 3.5 in the background. (I actually cancelled my ChatGPT subscription recently)
- I used a single large
ContentView.swift
(936 lines now) so I could easily paste that into Claude. - When I switched to Cursor, I would have it just survey the whole codebase using ⌘+Enter.
- I never used the composer feature in Cursor, not sure if I am missing out. This feature can work across multiple files. Might be useful if I restructure the code.
- In general, I could copy paste the answers of Claude into my code to provide the fix. In the Cursor workflow, I would not have Cursor apply the changes but I would do it manually so I could read what the code was actually doing, to get a better sense of my program instead of blindly copy-pasting.
- I sometimes found myself on the Apple docs to find out how certain APIs behaved or what was the expected input, but this was rare.
- Sometimes, proposed changes would lead to a compile error, after which I would put both the code state after the provided change, and the error as a separate artifact in Claude. This would usually lead to Claude understanding how to fix the problem. This problem happened less in Cursor since it understood my whole codebase and a compile error is usually the result of multiple files not matching up.
- Sometimes I would have to backtrack on my working code, so I kept a git repository in the background with checkpoints. In general, there was not that much backtracking.
- I sometimes asked Claude how to fix things with screenshots where I would put clear red or blue lines or arrows on, and then asked with a prompt how to fix the “alignment here”. Claude understood that I was talking about the area marked in the screenshot.
- I also manually edited some parts in XCode for clarity, or when I knew how to continue coding myself and it made more sense to change it myself instead of prompting.
All in all I am extremely impressed with this workflow.
It was fast because I didn’t have to bother with implementation details like the exact shape of API calls or how fetching data actually works.
I didn’t have to bother thinking about syntactical details like for example why there is a backspace in this code .environment(\.locale, Locale(identifier: appSettings.language.rawValue)
or why the last step in storing data after retrieving it from the API is a .store(in: &cancellables)
. All of this was handled by prompting and putting some functions logically together (fetchAvailableCurrencies
/ fetchExchangeRates
etc.)
I am sure you can learn to get good at all of that stuff and learn each API by hand, but really, at some point it’s maybe better if a machine talks to a machine than that I as a person need to worry about matching 2 data shapes.
I feel like I’ve gotten some app creations superpowers, enabling me to not get stuck at the point that I used to get stuck (the data part).
There’s still much more to do to make the app the ultimate quality and have something shippable that I am proud of, but I am so happy with the progress so far.