How To Inspect an iOS App

In this article, we will learn how we can reverse engineer iOS apps using Xcode’s debugger. We will do so by exploring how Apple’s Maps app is built. In short, this is what you will know by the end of this tutorial:

  • How to debug view hierarchy to capture the current UI state and get the layout information.
  • How to debug memory graph to obtain object hierarchy and how a specific API is implemented.

Without further ado, let’s get started.

Let’s Start

Before we start debugging apps, we need to disable our computer’s System Integrity Protection. In short, this setting is enabled by default and prevents us from debugging any iOS app in Xcode except our own.

  1. Restart your Mac in Recovery mode: immediately after your Mac turns on, press Command(⌘) + R. Release the keys when Apple logo appears on the screen.
  2. Then, find the “Utility” and open the Terminal.
  3. Type csrutil disable; reboot. This will disable System Integrity Protection and restart your computer.

Finally, we are ready to get started with reverse engineering a sample app, Apple Maps, in Xcode.

Note: after completing the tutorial, don’t forget to restart your Mac in Recovery mode again and run csrutil enable; reboot in the Terminal. This will enable System Integrity Protection again and restart the computer.

First, launch Xcode and either create a new project or open the one you have:

On Xcode Simulator or your device, open Apple Maps app:

Next, go to the “Debug” tab and choose Attach to Process by PID or Name:

Now we can search for the “Maps” process and press “Attach”:

After some time the progress will change from “Attaching to Maps …” to “Running Maps …”. This means the debugger was successfully connected to the app. Let’s now see what we can do with this.

Debugging View Hierarchy

Having Maps open and debugger connected, tap on the Debug View Hierarchy button to reveal the UI:

We are interested to know how the bottom sheet is architected and how UI and behavior are implemented:

Let’s reveal the UI hierarchy and expand some views:

Here we can see that Apple uses container and child view controllers to divide one screen into smaller, reusable parts.

In addition, we can detect Auto Layout warnings just like with any of our own apps:

On top of that, seeing the names of each view controller or a view is very useful in cases when you joined a project and need to find the required class in the codebase:

Let’s now see what API is used to create the previously mentioned bottom sheet behavior. We will do so by examining the Memory Graph.

Debugging Memory Graph

First, tap on the Debug Memory Graph button:

This lets us see currently alive objects:

From view hierarchy, we found out that the bottom sheet view controller is called SearchViewController. Let’s search for it in the debug navigator:

After tapping on it, we can see the dependency graph on the right:

This lets us know that there is a class called UIFormSheetPresentationController that is responsible for the bottom sheet’s presentation. By tapping on it and showing a Memory inspector, we can see the detailed hierarchy:

As we can see, the object responsible for the bottom sheet is called UISheetPresentationController. This is the API that was added since iOS 15.0 and Swift 5.5. By examining the documentation, we find out that it uses the detents array to manage size configurations of the bottom sheet:

We also learn that at the moment, there are only two possible detents available to us, .large() and .medium():

However, the Apple Maps allows to have bottom sheet rest at different points than .medium and .large. We can see that it can rest at a much smaller height:

How is that possible? Let’s find out. By searching for “Detent” keyword in the debug navigator, we find the detents array:

Let’s print the array’s description as follows:

We can see that Apple used three .custom detents:

This lets us know that portion of the UISheetPresentationController is private and not available to developers yet. However, hopefully the API will be updated soon to allow the creation of custom detents.

We have seen how we can grab UI information using view hierarchy and expand object relationships using memory graph. In addition, you can use Instruments on any app to monitor network requests, catch memory leaks and much more:

Wrapping Up

Want to learn more about bottom sheet creation in iOS 15? Check out my article about UISheetPresentationController. Thanks for reading!