Making an iOS app simply by prompting (2)

- Posted in apps build-in-public

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.

Leave a Reply

Your email address will not be published. Required fields are marked *