iOS 10 doesn't print NSLogs
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Starting with iOS 10, many developers discovered that NSLog output was no longer reliably appearing in the Xcode console. This was not a bug but a deliberate consequence of Apple introducing the Unified Logging System (os_log). Understanding what changed and how to migrate your logging will save you hours of confused debugging.
Why NSLog Stopped Showing Up
Apple replaced the legacy ASL (Apple System Logger) backend with the Unified Logging System in iOS 10 and macOS 10.12. NSLog still works under the hood, but its output is now routed through this new system. Several factors combine to hide its messages:
- Xcode console filtering changed. Xcode 8 and later versions changed how they display log output. The console sometimes filters messages by subsystem, making
NSLogoutput invisible unless you adjust the filter settings. - Log level suppression. The Unified Logging System assigns log levels (default, info, debug, error, fault). Messages at the
debuglevel are suppressed by default and only appear when explicitly enabled for a specific subsystem. - Simulator vs. device differences.
NSLogoutput may appear in the Simulator console but not on a physical device, because the device's logging daemon applies stricter filtering.
If you open the Console.app on macOS and filter for your app's process name, you will usually find the NSLog messages there. They are being generated; they are just not reaching the Xcode console panel in the way you expect.
The Unified Logging System
Apple's replacement is the os_log family of functions, introduced in iOS 10. This system provides structured logging with subsystems, categories, and log levels, giving you much finer control than NSLog ever offered.
The key log levels are:
| Level | Purpose | Persisted? |
| default | General information | Yes |
| info | Helpful but non-essential detail | No (until collected) |
| debug | Developer-only detail | No |
| error | Error conditions | Yes |
| fault | Critical failures (captures backtraces) | Yes |
Using os_log (Objective-C)
In Objective-C, import os/log.h and create a log object scoped to your subsystem and category.
Note the %{public}@ and %{private}@ format specifiers. By default, dynamic string values are redacted in logs for privacy. Mark values as public when you need them visible in Console.app on release builds.
Using Logger (Swift)
Starting with iOS 14, Apple introduced the Logger struct, which provides a more Swift-friendly API with string interpolation.
For projects that still need to support iOS 10 through iOS 13, use os_log directly:
Viewing Logs
There are three main ways to read os_log output:
- Xcode console. Make sure the console filter is not hiding your messages. Click the filter bar at the bottom and ensure your subsystem is included.
- Console.app. Open Console.app on your Mac, select your connected device or simulator, and filter by subsystem (for example,
com.myapp.networking). Enable "Include Info Messages" and "Include Debug Messages" from the Action menu. - Command line. Use the
logcommand in Terminal:
Common Pitfalls
- Assuming NSLog is broken.
NSLogstill writes to the Unified Logging System. The messages are generated but may be filtered out in Xcode. Check Console.app before concluding that logging is not working. - Forgetting to mark values as public. Dynamic values in
os_logare private by default. If your logs show\<private>instead of actual values, add%{public}@(Objective-C) orprivacy: .public(Swift) to the format specifier. - Using debug level in production testing. Messages logged at
os_log_debugorlogger.debugare not persisted and only appear when streaming in real time. Usedefaultorinfolevel for messages you need to retrieve after the fact. - Mixing up os_log and print/NSLog. Using
print()orNSLog()alongsideos_logcreates inconsistent behavior. Standardize on one logging approach across your codebase to avoid confusion about where messages end up. - Not specifying a subsystem and category. Logging to
OS_LOG_DEFAULTwithout a custom subsystem makes it nearly impossible to filter your messages out of the thousands of system logs.
Summary
- iOS 10 introduced the Unified Logging System, which changed how
NSLogoutput is routed and displayed in Xcode. NSLogstill works but its messages may be filtered out of the Xcode console. Use Console.app or thelogCLI to verify they exist.- Replace
NSLogwithos_log(Objective-C) orLogger(Swift, iOS 14 and later) for structured logging with subsystems, categories, and log levels. - Mark dynamic values as
publicexplicitly, becauseos_logredacts them by default for privacy. - Always define a custom subsystem and category so you can filter your logs efficiently in Console.app and the Xcode console.

