Chapters

Hide chapters

UIKit Apprentice

Third Edition · iOS 18 · Swift 5.10 · Xcode 16

My Locations

Section 3: 11 chapters
Show chapters Hide chapters

Store Search

Section 4: 13 chapters
Show chapters Hide chapters

40. Refactoring
Written by Fahim Farook

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Things are looking good in StoreSearch, but there are still a few rough edges to the app.

If you start a search and switch to landscape while the results are still downloading, the landscape view will remain empty. You can reproduce this situation by artificially slowing down your network connection using the Network Link Conditioner tool.

It would also be nice to show an activity spinner on the landscape screen while the search is taking place.

You will polish off some of these rough edges in this chapter and cover the following:

  • Refactor the search: Refactor the code to put the search logic into its own class so that you have centralized access to the search state and results.
  • Improve the categories: Create a category enumeration to define iTunes categories in a type-safe manner.
  • Enums with associated values: Use enumerations with associated values to maintain the search state and the search results.
  • Spin me right round: Add an activity indicator to the landscape view. Also add a network activity indicator to the app.
  • Nothing found: Update the landscape view to display a message when there are no search results available.
  • The Detail pop-up: Display the Detail pop-up when any search result on the landscape view is tapped.

Refactor the search

So how can LandscapeViewController tell what state the search is in? Its searchResults array will be empty if no search was done, or the search has not completed yet. Also, it could have zero SearchResult objects even after a successful search. So, you cannot determine whether the search is still going or if it has completed just by looking at the array object. It is possible that the searchResults array will have a count of 0 in either case.

You need a way to determine whether a search is still going on. A possible solution is to have SearchViewController pass the isLoading flag to LandscapeViewController, but that doesn’t feel right to me. This is known as code smell, a hint at a deeper problem with the design of the program.

Instead, let’s take the searching logic out of SearchViewController and put it into a class of its own, Search. Then, you can get all the state relating to the active search from that Search object. Time for some refactoring!

The Search class

➤ If you want, create a new branch for this in Git.

Move code over

Let’s make the changes to SearchViewController.swift. Xcode will probably give a bunch of errors and warnings while you’re making these changes, but it will all work out in the end.

Add the search logic back in

The app itself doesn’t do much anymore because you removed all the searching logic. So let’s put that back in.

The SearchComplete closure

Let’s create your own closure!

Closure types

Whenever you see a -> in a type definition, the type is intended for a closure, function, or method.

Improve the categories

The idea behind Swift’s strong typing is that the data type of a variable should be as descriptive as possible. Right now, the category to search for is represented by a number, 0 to 3, but is that the best way to describe a category to your program?

Represent the category as an enum

There are only four possible search categories, so this sounds like a job for an enum!

Use the Category enum

➤ Change the method signature of performSearch(for:category:completion:) to use this new type:

Convert an Int to Category

You still need to tell SearchViewController about this, because it needs to convert the selected segment index into a proper Category value.

Enums with associated values

Enums are pretty useful for restricting something to a limited range of possibilities, like what you did with the search categories. But they are even more powerful than you might have expected, as you’ll find out…

Consolidate search state

You can improve upon things by giving Search an explicit state variable. The cool thing is that this gets rid of isLoading, hasSearched, and even the searchResults array variables. Now there is only a single place you have to look at to determine what Search is currently up to.

Use the new state enum

Let’s see how this works.

Update other classes to use the state enum

That completes the changes in Search.swift, but there are quite a few other places in the code that still try to use Search’s old properties.

Spin me right round

If you rotate to landscape while the search is still taking place, the app really ought to show an animated spinner to let the user know that an action is taking place. You already check in viewWillLayoutSubviews() what the state of the active Search object is, so that’s an easy fix.

Show an activity indicator in landscape mode

➤ In LandscapeViewController.swift, add a new method to display an activity indicator:

I tcafbox uwfesuxes u xiuyyf az mqiss buhutk wlera
E jvajjob izcoruhov u maizls ay vdutn qalomx scepa

Hide the landscape spinner when results are found

This is all great, but the spinner doesn’t disappear when the actual search results are received. The app never notifies the LandscapeViewController when results are found.

Nothing found

You’re not done yet. If there are no matches found, you should also tell the user about this if they’re in landscape mode.

Pat, putxivw luaph lefa uemyig
Kam, menbewz geigv gapi uegvoj

The Detail pop-up

The landscape view is that much more functional after all the refactoring and changes. But there’s still one more thing left to do. The landscape search results are not buttons for nothing. The app should show the Detail pop-up when you tap an item.

Show the Detail pop-up

➤ First, still in LandscapeViewController.swift add the method to be called when a button is tapped:

Kbo lvebkkaadp ebqug kehmebdejw vmu Mepygsepu suef qu sxu Zupeoh vez-am
Rga xnumgpiuzy ussit gebgudmamc ygo Leppcjeyo seet jo jxi Lufooq mah-ej

Gda sit-ax ip jelcytipa nuji ix dae vola
Sna tor-ur ud xitwbriqa xidu ok qao ceva

Fix the detail pop-up

Hmm … that’s not quite what you were expecting, was it?

Pme bexlqbeazh ilanek
Vva kobwqvauss eguhag

Ycu muzuewauv ismeetn
Pge puliufiog epciarx

Zxo yol yariomeov rovee
Nci dor qeteanuog caqiu

Zja peyub yag-ub iy qadzpduho fetu
Mso qocuq xos-ih uc xarnhhovi riva

Hide the pop-up on rotation

Cool! But what happens when you rotate back to portrait with a Detail pop-up showing? Unfortunately, it sticks around. You need to tell the Detail screen to close when the landscape view is hidden.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now