NSUserDefaults
iOS Development
Swift Programming
Data Storage
Mobile App Development

Easy way to see saved NSUserDefaults?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Inspecting saved NSUserDefaults values is a frequent debugging task in iOS development. The easiest method depends on where you are working: inside app code, from Xcode debugger, or from simulator files on disk. Using more than one method is often best when chasing hard to reproduce state bugs.

Logging Defaults from App Code

A quick in app snapshot is often enough during development. You can print the entire persistent domain for your bundle identifier.

swift
1import Foundation
2
3if let bundleID = Bundle.main.bundleIdentifier,
4   let snapshot = UserDefaults.standard.persistentDomain(forName: bundleID) {
5    print("Defaults snapshot:")
6    for (key, value) in snapshot {
7        print("\(key) = \(value)")
8    }
9}

This approach is fast and safe for local debugging. Avoid leaving broad dumps in production logs if values may contain sensitive data.

Using LLDB During a Debug Session

When the app is paused in Xcode, LLDB can inspect defaults directly without changing source code. This is useful when you only need one key or current runtime state.

lldb
po UserDefaults.standard.dictionaryRepresentation()
po UserDefaults.standard.string(forKey: "session_token")

LLDB inspection is ideal for one off checks during interactive debugging sessions. It keeps debug instrumentation out of your codebase.

Reading Simulator Defaults Files

For deeper inspection, especially after app relaunches, examine simulator container files. The defaults command can read the preferences plist if you know the bundle identifier and simulator path.

bash
xcrun simctl get_app_container booted com.example.MyApp data
# then inspect Library/Preferences/com.example.MyApp.plist

You can also reset defaults to reproduce first run behavior:

swift
1if let bundleID = Bundle.main.bundleIdentifier {
2    UserDefaults.standard.removePersistentDomain(forName: bundleID)
3    UserDefaults.standard.synchronize()
4}

Reset flows are useful in UI tests and migration tests where startup state must be deterministic.

Automating Defaults Checks in Tests

Manual inspection is useful, but automated checks prevent regressions. In unit or UI tests, set known defaults, relaunch key flows, and assert expected values are present or removed. This is especially important when migrating preference keys.

swift
1import XCTest
2
3final class DefaultsTests: XCTestCase {
4    func testThemePreferenceRoundTrip() {
5        let defaults = UserDefaults.standard
6        defaults.set("dark", forKey: "theme")
7
8        let value = defaults.string(forKey: "theme")
9        XCTAssertEqual(value, "dark")
10
11        defaults.removeObject(forKey: "theme")
12        XCTAssertNil(defaults.string(forKey: "theme"))
13    }
14}

You can also assert migration behavior by writing old key names, launching migration code, then confirming new keys are populated and old keys are cleared. Automated defaults validation turns invisible state handling into testable behavior.

For simulator debugging from terminal, the macOS defaults utility can inspect plist values directly once you locate the app container. This is handy in CI style scripts where interactive debugger access is unavailable, and it gives a repeatable way to verify persisted state between launches.

When diagnosing production issues, ask users for app version and recent actions before inspecting defaults snapshots. Preference bugs are often migration related, and contextual steps help map persisted keys to specific code paths quickly.

If your app uses app groups, verify which suite name stores each value because debugging the wrong domain can hide the real issue. Explicit suite level logging helps avoid this confusion.

Keep a small troubleshooting runbook so support and engineering teams debug defaults issues consistently.

Common Pitfalls

  • Logging full defaults in shared logs that may include sensitive values.
  • Assuming defaults are written immediately without synchronization points.
  • Debugging wrong bundle identifier when multiple targets are installed.
  • Forgetting simulator and physical device store defaults separately.
  • Keeping stale keys after schema changes and misreading their effect.

Summary

  • Use code level dumps for quick local visibility.
  • Use LLDB for ad hoc runtime inspection.
  • Inspect simulator preference files for persistent state analysis.
  • Reset defaults explicitly when testing first run flows.
  • Treat defaults data carefully when handling sensitive values.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.