My App Crashed, Now What?
In this tutorial, you’ll learn what makes your app crash and how to fix it when it does. By Ehab Amer.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
My App Crashed, Now What?
25 mins
- Getting Started
- Tools to Help You Fix and Resolve Crashes
- Breakpoints
- Console Log
- Variables View
- The Infamous nil
- Exhibit A: Dark Force – Force Unwrapping
- Proving Your Case
- Finding the Right Solution
- Exhibit B: Weak Grip — Weak References
- Understanding the Crash
- Exhibit C: Unexpected Updates — Invalid Table Updates
- A Wider View of the Problem
- Narrowing Down the Problem
- Assertions
- Writing Your Own Reusable Code
- Changing Your Build Configuration
- Where to Go From Here?
Proving Your Case
Don’t take this as a fact; question it and make sure that’s really what caused the crash. When you fix a crash, you don’t want to do it by trial and error. You want to be 110% sure you’re fixing what’s broken.
To test your theory, type this command in the Console Log:
po Int(item)
The po
command you entered before the expression stands for print object, which is an LLDB command to print the description of an object. You can also use p
, but the result in the console will look slightly different.
The console output will be nil:
So Int(item)
is nil
, and when you execute po Int(item)!
you get some additional information.
This result is the same as the error written on the crash, so you’re definitely right about the source of the crash.
But wait! How does it work for the other values?
Add a breakpoint on the same line that caused the crash and restart the app. Remember to write ,two
before you calculate the sum.
The value of item
on the breakpoint is 4
and the result of Int(item)
gives a value instead of nil
.
Finding the Right Solution
Int(_:)
worked when the value of item
is 4
, but it won’t work when it’s two
. In other words, it works when the value is a string
with numeric digits, but not with alphabetical letters, even when they form the name of the number.
To fix this crash, replace the following line of code in calculateSum(items:)
:
finalValue += Int(item)!
With this block of code:
if let intValue = Int(item) {
finalValue += intValue
}
The code above checks that the result of Int(item)
isn’t nil
before using it, which makes it safe from crashes.
Disable the breakpoint by clicking on the blue arrow and it will become semi-transparent blue. Build and run and add any kind of text you want in the text field after the numbers.
It doesn’t crash anymore, but is it completely fixed? Instead of adding the numbers, delete the last one and try again.
The app crashed again in ForceUnwrappingViewController.swift on line 58.
The relevant message from the log is:
Could not cast value of type 'Swift.String' (0x7fff879c3f88) to 'Swift.Int' (0x7fff879c1e48).
The crashing line is forcing a cast on result
as an Int
, while the value you provided is a String
. That means valueToShow
is nil
and when you force unwrap it, the app crashes, similar to the crash above that you have already fixed.
calculateSum(items:)
will only show the sum if the total is more than 100. Otherwise, the message should be “Sum is too low”.
This is a straightforward fix. Replace the code inside showResult(result:)
with this block of code:
if let intValue = result as? Int {
sumLabel.text = "\(intValue)"
} else if let stringValue = result as? String {
sumLabel.text = stringValue
}
Here, you check if you can cast result
to an Int
, then create a String
of its value and add it to the label. You use the value as it is if you can cast it to a String
.
Build and run. You’ll see the error message, “Sum is too low” when the total is below 100.
Exhibit B: Weak Grip — Weak References
The second crash you’re going to fix involves an unusual way of showing and hiding views.
The Weak References screen is a simple form with two steps, where the second step is only active if the answer to the first question is “yes.”
When you turn off the switch, the second question disappears, but when you turn it on again… there’s the crash.
The app crashed in WeakReferencesViewController.swift line 37.
WeakReferencesViewController
has three items:
- An
IBOutlet
to thestackView
. - An
IBOutlet
to thesecondQuestionView
. - An
IBAction
toswitchValueChanged(_:)
, where you change the value of the switch to remove thesecondQuestionView
or to add it back at the bottom of thestackView
.
There are two ways to figure out why Xcode is showing nil
: Explore the values from the Variables View or check the values of the two variables found on the crashing line from the Console Log.
From what the debugger output says, the value of secondQuestionView
is nil
, but why? Add a breakpoint on the first line of switchValueChanged(_:)
and restart the app to start investigating.
Build and run.
secondQuestionView
isn’t nil
when you turn off the switch. However, when you turn it on again after the view disappears, it’s already nil
.
Understanding the Crash
The reason for that is because of the reference chain in UIKit
. Each view has a strong reference to the subviews presented inside it. As long as secondQuestionView
is in the on-screen view hierarchy, something will be holding a strong reference to it.
So when you removed the secondQuestionView
from its superview, you broke that tie. And looking at the IBOutlet
definition of secondQuestionView
, you’ll find it’s marked as weak
. Thus, it deallocated from memory and its reference changed to nil
since no one was holding it to prevent it from doing so.
Once you remove the weak
keyword from the secondQuestionView
declaration, the crash will disappear. You can do the same for stackView
as a precaution, but it will have no effect on the crash since you never remove the stackView
from the superview.
Remove the weak
keywords, then build and run to try the scenario again.
You’ll see that the form works fine now. The view appears and disappears as required.
Exhibit C: Unexpected Updates — Invalid Table Updates
The third crash is slightly different from the previous ones. It’s more of a data mismatch.
Open the third item, called Invalid Table Updates, on the gallery screen to start your investigation.
This screen has a table view with four cells. Each cell has its number written on it. There’s also a small button in the top-right corner to add more cells.
Go ahead and press that button. As you may have expected, there’s a crash. But… which line is crashing? And what’s all that in the log?
Xcode stopped in AppDelegate.swift on line 32.
Add an exception breakpoint to your project, then build and run to see the difference.
This time, Xcode stopped in InvalidTableUpdatesViewController.swift on line 37. The log is empty and has no information provided because the breakpoint stopped right before the exception happened. This is a different kind of crash than the previous ones.
When you press the Continue button, Xcode will return to the class declaration line in AppDelegate.swift and the log will have the crash information.
The log contains information about the crash and the stack trace information for when the crash happened. Most of the time, you won’t need the stack trace information when you are debugging from Xcode and have your exception breakpoint enabled. Take a look at the crash information.
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to insert row 4 into section 0, but there are only 4 rows in section 0 after the update.