Debug (debug mode) — is a build configuration for mobile applications in which the compiler includes symbolic information, disables code optimization, and connects the debugger for step-by-step execution analysis. According to Android Developers, a Debug build contains debugging symbols, does not compress resources, and allows connecting a database and network request inspector. Debug mode is contrasted with Release build: in Debug, the developer sacrifices performance for code execution transparency.
Key Takeaways
Debug is not just a compiler flag but an entire set of settings that make the application transparent to the developer. In Debug mode, the compiler adds a symbol name table (DWARF) to the executable file, which maps machine code to source lines. Without this table, the debugger cannot show which line of code is currently executing.
Debugger is a program that runs your application in a controlled environment. You can pause execution at any line (breakpoint), view the values of all variables in the current scope, change them on the fly, and continue execution. For mobile platforms, the standard debugger is LLDB — an LLVM component used in both Xcode and Android Studio.
Debug mode also includes additional checks that are disabled in Release: assertions, array bounds checks, memory leak detectors, and enhanced logging. These checks slow down the application but catch errors early in development — before the code reaches the user.
The difference between Debug and Release builds is fundamental: they are two different sets of compiler flags, signing configurations, and packaging settings. Understanding these differences helps avoid situations where “it works in the simulator but not on a real device.”
| Parameter | Debug | Release |
|---|---|---|
| Optimization | Disabled (-O0) | Enabled (-Os or -O2) |
| Symbols | Full DWARF table | Stripped (removed) |
| Signing | Development certificate | Distribution certificate |
| Profiles | Debug provisioning profile | App Store / Ad Hoc profile |
| Logging | Full (all levels) | Disabled or minimal |
| Obfuscation | Disabled | Enabled (ProGuard/R8) |
| .apk/.ipa size | Larger (symbols + no compression) | Smaller (R8 + resources) |
Debug build is used at all stages of development and testing on local devices. Release build is assembled before submitting to App Store Connect or Google Play Console. Debugging on a Release build is technically possible but extremely inconvenient due to renamed methods (R8) and the lack of symbolication for crash logs.
One common problem is code that works in Debug but crashes in Release. The cause is UB (undefined behavior) in code that the compiler handles differently with different optimization levels. A typical example: reading an uninitialized variable or violating strict aliasing. To detect such errors, use a static analyzer (Clang Static Analyzer, ktlint) before each Release build.
LLDB is a high-performance debugger built on LLVM, supporting C, C++, Objective-C, Swift, and Kotlin/Native. LLDB provides a REPL interface where you can execute arbitrary expressions, change variable values, and call functions in the context of a paused application.
Breakpoint is a key debugger tool. You set a point on a line of code, and the application pauses when execution reaches that line. LLDB supports several types of breakpoints: conditional (trigger only when a condition is met), symbolic (on function calls), and one-shot (trigger once and are automatically removed).
Watchpoint is a point that observes variable changes. You specify a memory address, and the debugger pauses execution on any write to that address. This tool is indispensable for finding data races and incorrect mutations of shared objects. To view the UIKit hierarchy, use the UIView Inspector available in Xcode.
// Set conditional breakpoint
(lldb) breakpoint set --name "viewDidLoad" --condition "self.isViewLoaded == false"
// Watchpoint on property
(lldb) watchpoint set variable self->_loadingState
// Execute code in stopped context
(lldb) expr self.view.backgroundColor = UIColor.redColor
Both IDEs provide graphical inspectors on top of LLDB. Android Studio includes Layout Inspector (View hierarchy), Network Inspector (HTTP request tracing), and Database Inspector (real-time SQLite). Xcode provides Debug Memory Graph (memory leak analysis) and View Debugger (3D view of UIKit layers).
Starting with Android 11, debugging over Wi-Fi works without a USB connection: just scan the QR code from Android Studio. iOS has supported Wi-Fi debugging since Xcode 9+ — the device connects once via USB, after which debug sessions can run over the network. Wi-Fi debugging is not suitable for CI servers due to unpredictable latency and packet loss, so automated pipelines always use USB. However, for local development, Wi-Fi debugging is noticeably more convenient — the developer is not tied to a cable and can test the application on a device at the other end of the room.
Android Debug Bridge (ADB) is a universal tool for interacting with an Android device from the command line. Through ADB, you can install an application, start debugging, copy files, execute shell commands, and view logs. Android Studio uses ADB under the hood for all debugging operations.
Android Studio supports two debugging modes: Run (normal launch) and Debug (launch with the debugger attached). In Debug mode, you can set breakpoints directly in the editor, inspect variables in the Debug Tool Window, and evaluate expressions in Evaluate Expression. To debug background processes (Service, BroadcastReceiver), use Attach Debugger to Android Process.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Breakpoint here will pause execution
val button = findViewById<Button>(R.id.btn_debug)
button.setOnClickListener {
startDebugProcess()
}
}
private fun startDebugProcess() {
val data = fetchDataFromApi()
Log.d("Debug", "Data loaded: $data")
}
}
ADB shell commands provide access to the device file system without root privileges. You can view the contents of the databases directory, copy the .db file to your computer, and open it with any SQLite client. Android Studio Database Inspector automates this process: you see live database data in real time and can execute SQL queries directly from the IDE.
Xcode provides an integrated debugging environment based on LLDB. The developer can run the application on a simulator or physical device, set breakpoints, and use the Debug Navigator to control execution threads. Unlike Android, iOS does not allow running two Debug builds simultaneously on the same device without special configuration.
Simulator runs the application as a native macOS process, providing the fastest debugging cycle. On a physical device, debugging happens over USB or Wi-Fi (starting with iOS 16), and LLDB communicates with debugserver on the device. Debugging performance on the device is lower due to limited USB 2.0 bandwidth, but only a physical device allows testing real-world scenarios: push notifications, camera, sensors.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
let label = UILabel()
label.text = "Debug Mode"
label.textColor = .systemBlue
view.addSubview(label)
}
}
Xcode Organizer collects crash logs from testers' devices via Crash Logs. Symbolication (converting addresses to function names) requires the .dSYM file, which is generated with each Debug build. In a Release build, dSYM is also created, but crash logs from the App Store need to be uploaded to Organizer manually or through the bitcode service.
Frequently Asked Questions
Technically yes — via Ad Hoc distribution with a Debug certificate, but Apple and Google do not recommend doing this. A Debug build contains debugging symbols and reduced performance, which degrades UX and increases the application size by 2–3 times.
The reason is disabled compiler optimization (-O0). The compiler does not inline functions, does not remove dead code, and retains all intermediate variables. Additionally, Debug includes assertion checks and array bounds checks that are absent in Release.
In Xcode choose Window → Devices and Simulators, check “Connect via network” for your device. The device and Mac must be on the same Wi-Fi network. After connecting via USB once, debugging will work over Wi-Fi on subsequent launches.
Attach to process allows you to connect the debugger to an already running process without restarting the application. This is useful for debugging Services, BroadcastReceivers, or processes that start on a system event, where standard Debug Run is not applicable.
NSLog and print by default output logs only in Debug configuration. For Release, use os_log with the OSLogType.default flag — it saves messages to the Unified Logging System and is accessible through Console.app on Mac.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also