NSUserDefaults
app uninstall
simulator
iOS development
debugging

NSUserDefaults not cleared after app uninstall on simulator

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If NSUserDefaults appears to survive an app uninstall in the iOS Simulator, the first step is to verify what is actually being persisted and where. In many cases the real issue is not ordinary app defaults, but simulator state, shared defaults suites, app-group storage, reinstall behavior, or another storage mechanism such as Keychain.

What NSUserDefaults normally does

NSUserDefaults stores small preference values for an app:

swift
UserDefaults.standard.set(true, forKey: "hasSeenOnboarding")
let value = UserDefaults.standard.bool(forKey: "hasSeenOnboarding")
print(value)

For normal app-scoped defaults, the data lives in the app container’s preferences area. In a clean lifecycle, removing the app should remove the container data associated with that installation.

If data still seems to be present later in the simulator, something else is often involved.

Confirm you are using the standard app domain

One common source of confusion is using a suite name or app-group container:

swift
let defaults = UserDefaults(suiteName: "group.com.example.shared")

That is not the same storage domain as UserDefaults.standard. Shared containers can behave differently from ordinary app-specific defaults because they are meant to be visible across related targets such as extensions.

If you expected uninstalling one app target to clear the values, but the values live in a shared suite, the observed behavior may not match that expectation.

Make sure you are actually uninstalling, not just rerunning

During development, it is easy to think "I removed the app" when the simulator really reused state or Xcode simply redeployed the same bundle.

A cleaner check is:

bash
xcrun simctl uninstall booted com.example.MyApp

Then reinstall and rerun the app. This avoids ambiguity about whether the uninstall action really happened for that bundle identifier.

If you still want a full reset of simulator state, erase the simulator:

bash
xcrun simctl erase all

That is heavier, but it removes cached simulator data broadly.

Reset defaults explicitly during debugging

If the goal is simply "start with no defaults while testing," resetting the defaults domain directly is often easier than relying on uninstall behavior.

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

This clears the standard defaults domain for the current app. It is a useful debugging tool, especially when you want deterministic first-launch behavior during development.

Do not run that unconditionally in production code. It is for controlled reset flows or test-only logic.

Check whether the data is actually somewhere else

Developers often blame NSUserDefaults when the persistent data is really stored in:

  • Keychain
  • a file in Documents or Library
  • Core Data or SQLite
  • an app-group shared container

Keychain is especially important here because it commonly persists across uninstall and reinstall cycles. If credentials or tokens survive deletion, Keychain is often the real cause, not UserDefaults.

Inspect behavior with a simple repro

A minimal check helps separate simulator quirks from app logic:

swift
UserDefaults.standard.set("hello", forKey: "debug_value")
print(UserDefaults.standard.string(forKey: "debug_value") ?? "nil")

Run the app, uninstall it explicitly, reinstall it, and inspect only that key. If the key still appears, then you can investigate simulator state or storage domain issues with a narrow repro instead of debugging the entire application.

Common Pitfalls

The biggest mistake is assuming every persistent value comes from NSUserDefaults. Keychain and shared containers are frequent sources of "it survived uninstall" reports.

Another issue is using UserDefaults(suiteName:) or an app-group suite and then expecting the exact lifecycle of UserDefaults.standard. The storage scope is different, so the cleanup behavior can feel surprising.

Developers also trust manual simulator clicks too much during debugging. Using simctl uninstall or simctl erase is more explicit and easier to reproduce.

Finally, if you only need a clean first-run test during development, explicit domain reset is often more reliable than repeatedly uninstalling and reinstalling the app.

Summary

  • Ordinary app defaults and simulator-visible persisted values are not always the same problem.
  • Verify whether you are using UserDefaults.standard, a suite name, an app group, or another storage system.
  • Use simctl uninstall or simctl erase when you need a cleaner simulator reset.
  • 'removePersistentDomain is a practical debugging tool for first-run testing.'
  • If values survive uninstall, check Keychain and shared storage before blaming NSUserDefaults alone.

Course illustration
Course illustration

All Rights Reserved.