One of the most-read posts on this blog is this one about typical iOS take-home coding exercises. The post has 3,797 views at time of writing, and several readers have privately thanked me for writing it. But, in my experience, the application process for many companies involves not a take-home coding exercise but rather a live-coding exercise. The candidate typically has forty-five minutes to implement an app from scratch that is similar to the app described in the post mentioned above but without unit tests or dependency injection.

The live-coding exercise is a different beast. Much of the knowledge required for a take-home coding exercise is applicable to a live-coding exercise, but the extreme time constraint of a live-coding exercise means that success is unlikely without extreme practice, preparation, and time-saving. Worse, the competitiveness of the job market means that, even if you complete 80% of the requirements of a live-coding exercise, you will be rejected in favor of another candidate who completes 100%.

In this post, I describe practice, preparation, and execution that make success in a live-coding exercise more likely. In an accompanying YouTube video, I apply this knowledge and complete a live-coding exercise within forty-five minutes.

This post is not about preparing for and succeeding in data-structure-and-algorithm interviews. Learning materials for those interviews are available elsewhere.

Briones Reservoir in Orinda, California

Briones Reservoir in Orinda, California

Typical Live-Coding Exercise

Live-coding exercises typically have instructions like the following:

There is an endpoint with information about dog breeds. The URL of the endpoint is: https://api.thedogapi.com/v1/breeds?api_key=TO_BE_PROVIDED. Create an app that shows all dog breeds. For each breed, show the name of the breed, the breed group, and a small photo of the breed. When the user taps a breed, show another screen with the name of the breed, a larger photo of the breed, breed lifespan, and breed temperament.

These instructions often have a hidden requirement: image caching. In my experience, an interviewer may fail a candidate who implements a List or UITableView with Images or UIImages and no caching. Even if there is no such hidden requirement, concern for performance can only earn you points with an interviewer.

Instructions sometimes have explicit requirements not mentioned above. Here are some I have seen:

  • hitting an endpoint multiple times in parallel
  • paging through data because the endpoint doesn’t return all data at once
  • implementing a specific UI shown in a screenshot
  • implementing a button that launches Safari with URLs from the endpoint

The applicant is usually free to choose (UIKit or SwiftUI) and (GCD or Swift Concurrency). In past live-coding exercises, I have chosen SwiftUI and Swift Concurrency in order to demonstrate my dedication to learning the latest and greatest.

Preparation

The two keys to preparation are making a plan and practicing the execution of that plan.

I can’t overstate the importance of planning. If, at any point during an interview, you have to think about where to start or what to do next, you will run out of time and fail the interview. The pressure cooker of an interview is no place to be making a plan.

What is a good plan? I’ll share mine and discuss aspects of it, but what I want you to glean from this post is how to make a plan. The how is simple. Complete an exercise like the one described above. Take your time. When you’re done, think about how you would generalize the steps you took to other coding exercises. Write down these steps.

That said, I share here the steps that I came up with. These are my steps, and yours will differ, but knowing the reasonings for mine may help you plan yours.

In these steps, the word Foo is a placeholder for the domain of any given challenge. For example, in a challenge using a dog-breeds endpoint, Foo would become Breed.

These steps assume SwiftUI and will substantially differ if you intend to use UIKit in live-coding exercises.

I advise printing your steps and taping them to your monitor or elsewhere in your workspace in case your brain freezes during an interview, as mine sometimes does.

0. Before the interview, make a SwiftUI app with folders named Models, Views, Helpers, and ViewModel. Put ContentView in the Views folder. Delete the Preview Content folder and its build setting because Preview Content is unlikely to be used during an interview, and the folder is distracting.

I find that putting files in folders makes accessing the files I want quicker and easier. Creating folders before the interview saves precious time. The name of the app doesn’t matter during practice but, for a real interview, the name of the company works as the app name.

If, for some reason, you intend to use UIKit and programmatic layout, ahead-of-time app creation is even more important because UIKit/programmatic-layout apps require setup, for example deleting the storyboard and modifying Info.plist.

1. Get JSON from the endpoint using a Web browser and then inspect the JSON using a tool like JSONFormatter.

The goal of this inspection is to understand what exactly the endpoint returns, mentally mapping what is in the JSON to what is required for the UI.

2. Generate a rough draft of the models using QuickType.

QuickType is a huge time-saver for generating Decodable models from JSON. Just one of many fantastic features of QuickType is that it detects which keys are sometimes not present and makes the properties representing those keys Optional. I’ve recommended use of QuickType in interviews elsewhere and have heard the well-founded objection that an interviewer might not approve of its use. I acknowledge that there is some risk in use of QuickType. Here are two responses to the objection. One, an interviewee may be able to assuage disapproval by explaining the code that QuickType generates. You should be able to do so. Two, though the risk of QuickType use is real, the risk of not having enough time to finish the live-coding exercise in forty-five minutes is ever-present and huge. QuickType reduces this risk and is therefore, in my view, worth using.

3. In Xcode, rename ContentView to BrowseFoosView.

4. Add the models generated by QuickType to the app.

Change Codable to Decodable and delete unused properties.

If a model will be used in a List, add Identifiable conformance and a computed property that looks like this:

var id: String { name } // name uniquely identifies the row.

Here is an example of a model that I modified after generating it using QuickType.

5. Create FooLoader in the Helpers group.

I make this an enum since, in live-coding exercises, it is stateless. Coding this from scratch requires memorization and practice. Here is an example of a FooLoader.

6. Invoke FooLoader.loadFoos() using a Task attached to BrowseFoosView, printing the results.

This ensures that you have coded the models and FooLoader correctly. Debug and fix if needed.

7. Create BrowseFoosViewModel, calling FooLoader.loadFoos() within it.

Coding this from scratch requires memorization and practice. Here is an example of a BrowseFoosViewModel.

My use of BrowseFoosViewModel is inspired by this video by Vincent Pradeilles. I like how the view model takes loading and loading-state logic out of the View, simplifying it.

8. Add an instance of BrowseFoosViewModel to BrowseFoosView.

9. Call BrowseFoosViewModel.loadFoos() using a Task attached to BrowseFoosView.

10. Modify BrowseFoosView to use the view model.

Modifying the View to populate a List requires memorization and practice. Here is an example of a complete BrowseFoosView. My implementation borrows heavily from that of Vincent Pradeilles.

11. Implement ImageLoader.

Adapting an approach shared by Donny Wals, I use an Actor for caching and thread safety. Coding this from scratch requires memorization and practice. Here is an example of a complete ImageLoader.

12. Modify BrowseFoosView to use ImageLoader.

13. Implement FooDetailsView and modify BrowseFoosView to invoke it.

Coding FooDetailsView from scratch requires memorization and practice, though this view is mercifully simpler than a BrowseFoosView. Here is an example of a FooDetailsView.

14. If time permits, improve the model names.

QuickType often generates unintuitive model names like Welcome. If time permits, I fix them.

Practice

Makes Perfect

I mentioned that, for many of the steps above, practice and memorization are required. For practice, I coded ImageLoader again and again until I could type the entire file’s content without hesitation. I started this practice by copying an existing implementation. On each iteration, I consulted the existing implementation less and less. I observed that there are twelve steps to coding an ImageLoader. Wary of brain freezes, I wrote down these steps and taped them to my monitor.

Once you have a plan and are able to regurgitate all the code needed for a typical live-coding exercise, practice making a live-coding-exercise app over and over using a variety of endpoints. Here are some practice apps I made before recording the video that accompanies this post. Overcoming the quirks of different endpoints will make you a better developer and candidate. For example, the Disney API, somewhat unusually, returns pages of data, not all data at once. While coding a practice app, I had to figure out how to accommodate that, and I’ll be ready if a live-coding exercise ever requires paging. I would definitely not have been able to figure out paging quickly enough if I had first encountered it in the context of a forty-five-minute live-coding exercise.

Mnemonics

As you regurgitate code during practice, you may find certain aspects of the code difficult to remember. I certainly did. For example, I had trouble remembering these three modifiers that Images needed:

Image(uiImage: image)
  .resizable()
  .aspectRatio(contentMode: .fit)
  .padding()

For situations like this, I recommend that you use a mnemonic, an easily recalled word or phrase whose letters or words remind you of the code you need to type. My mnemonic for the code above is RAP. R represents .resizable(), A represents .aspectRatio(contentMode: .fit), and P represents .padding().

Another aspect of the code I had difficulty remembering was how to implement drill-down navigation. The snippet below shows the implementation:

var body: some View {
  NavigationStack {

// code omitted for clarity

func list(of breeds: [Breed]) -> some View {
  List(breeds) { breed in
    NavigationLink {

The NavigationStack, NavigationLink combination consistently eluded my recall during practice. To remember these two APIs and their order of appearance, I used this website to generate the unforgettable phrase Nervous Smurfs Nominated Leopards.

Execution

My advice for execution of a live-coding exercise is to avoid wasting time. I’ve already described three techniques for avoiding time wastage: creating a skeleton of the app ahead of time, using QuickType to generate models, and recalling code with mnemonics. Here are three more.

Keep Your Intro Short

Interviewers rarely launch into the live-coding exercise at the start of an interview. Instead, they typically introduce themselves and ask candidates to do likewise. Memorize a short, punchy introduction for yourself. This introduction must be shorter than one you would use in a free-wheeling, non-coding interview. The two-minute difference between your punchy introduction and the longer one you would use in a less time-constrained interview could be the difference between failing and passing a live-coding-exercise interview.

Use Snippets Judiciously

Most interviewers expect candidates to code largely from memory, not consulting existing code or other references. “What does coding from memory have to do with my ability as a software developer?”, you might ask. In many cases, the software-development interview is a test of the candidate’s desire for the job, as well as a mechanism for shrinking the pool of candidates, not an exploration of the candidate’s software-development ability. I don’t have a more-plausible explanation. That said, some interviewers do invite candidates to consult StackOverflow or official documentation for APIs they can’t remember. Though often, I suspect, sincere, this invitation can lead a candidate astray in that a candidate might waste precious minutes perusing unhelpful or irrelevant search results. I have done so. As I mentioned, some requirements come up rarely, and you may not happen to memorize how to implement them. Three examples for me are using withTaskGroup for parallelism, paging of endpoints using a view model, and opening a URL in Safari using a Button. Instead of Googling these during interviews when they come up, I use Xcode snippets that I have created. Here, for example, is my URL/Button snippet:

// Add these properties to View:
private let foo: Foo
@Environment(\.openURL) var openURL

// Add this to body:
if
  let urlString = foo.urlString,
  let url = URL(string: urlString)
{
  Button("Open URL in Safari") {
    openURL(url)
  }
}

These snippets should be used only for discrete, uncommon requirements whose implementations you are unwilling or unable to memorize. You shouldn’t put an entire BrowseFoosView implementation, for example, in a snippet and paste that during an interview because your interviewer will perceive disrespect for the memorization-hazing ritual and will fail you. But relying solely on memorization is impossible, at least for me. Consider withTaskGroup. Though I understand how it operates in practice, that API, unlike its antecedent, DispatchGroup, is so unintuitive that I can’t, for the life of me, completely memorize its use. Worse, an invocation of withTaskGroup with an Array differs substantially from an invocation with a Dictionary. My two withTaskGroup snippets, one for Array and one for Dictionary, give me comfort and confidence, notwithstanding any risk their use involves.

For reference, here is my live-coding-exercise snippet library at time of writing.

Josh Adams's Snippets

Josh Adams's Snippets

(Mostly) Don’t Think Aloud

I’ve often heard advice that, in a coding interview, the candidate should share with the interviewer the candidate’s thinking about every step the candidate is taking. This advice works in data-structure-and-algorithm interviews because the actual amount of code required to solve the problem is small. There just isn’t much typing. Talking about each step before taking it won’t prevent a candidate from finishing the problem. Moreover, one of the goals of these interviews is for the candidate to demonstrate computer-science knowledge to the interviewer, and talking helps demonstrate the candidate’s knowledge. Talking can even prompt the interviewer to set the candidate on the right path when the candidate takes a wrong turn.

But this advice is inapposite to live-coding-exercise interviews. Those interviews require candidates to type a (relatively) massive amount of code in forty-five minutes. Time spent talking is time not spent typing. Calling out or commenting on each granular step of implementation could easily prevent a candidate from completing the exercise.

I used the word “granular” in the preceding paragraph advisedly. I do not recommend that a candidate remain completely silent during a live-coding exercise. Rather, the candidate should briefly describe each high-level step before taking it, remaining silent while typing. I would say the following before taking step 2:

I will now use a tool called QuickType to generate rough-and-ready models.

I would then use QuickType to generate rough-and-ready models. I would not say:

I’m pasting the JSON into QuickType.

or

Some of the property names that QuickType generates are suboptimal. I’ll improve those later if time permits.

or

Not all of the properties in the generated model are needed for this exercise. I’ll delete those later.

None of these last three statements provides value, and each therefore frustrates the goal of timely completion.

Invitation and Observations

I hope that readers find helpful the advice in this post. For a real-world example of putting this advice to use, watch this video. I implemented the app in this video without having implemented an app using The Dog API, the endpoint specified in the instructions. Instead, I practiced implementing an app ten times using The Cat API. I did not practice using The Dog API because I wanted the video to simulate the endpoint unfamiliarity of a real interview. Because I hadn’t practiced using The Dog API, I did make a couple mistakes during implementation. One mistake was initially omitting a property from the breed model. But because of the time savings that resulted from my preparation and practice, I had plenty of time to fix those mistakes.

The reader of this post may infer, correctly, from my references to hazing rituals, regurgitation, and the Spanish Inquisition that cynicism and resentment color my perception of the current state of iOS-developer interviews. But I concede that the expectations of many interviewers are not divorced from the day-to-day reality of software development. Enough are, however, that I was motivated to write this post.