Swift: print() vs println() vs NSLog()

i0S Swift Issue

Question or problem with Swift language programming:

What’s the difference between print, NSLog and println and when should I use each?

For example, in Python if I wanted to print a dictionary, I’d just print myDict, but now I have 2 other options. How and when should I use each?

How to solve the problem:

Solution 1:

A few differences:

  1. print vs println:

    The print function prints messages in the Xcode console when debugging apps.

    The println is a variation of this that was removed in Swift 2 and is not used any more. If you see old code that is using println, you can now safely replace it with print.

    Back in Swift 1.x, print didn’t add newline characters at the end of the printed string, whereas println did. But nowadays, print always adds the newline character at the end of the string, and if you don’t want it to do that, supply a terminator parameter of "".

  2. NSLog:

    • NSLog is slower;

    • NSLog adds a timestamp and identifier to the output, whereas print will not;

    • NSLog statements appear in both the device’s console and debugger’s console whereas print only appears in the debugger console.

    • NSLog uses printf-style format strings, e.g.

      NSLog("%0.4f", CGFloat.pi) 

      that will produce:

      2017-06-09 11:57:55.642328-0700 MyApp[28937:1751492] 3.1416

  3. Effective iOS 10/macOS 10.12, there is a third alternative, os_log, part of the “unified logging” system (see WWDC 2016 video Unified Logging and Activity Tracing).

    • You must import os.log before using os_log function:

      import os.log 
    • Like NSLog, os_log will output messages to both the Xcode debugging console and the device console, too

    • You can now control the “subsystem” and “category” fields available in the Console app. For example:

      let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "network") os_log("url = %@", log: log, url.absoluteString) 

      When you observe the app via the external Console app, you can not only add these columns to the main view, but you can filter on the basis of these. It’s very useful when wanting to differentiate your debugging messages from (a) those generated by other subsystems on behalf of your app; or (b) messages from other categories or types.

    • You can specify different types of logging messages, either .info, .debug, .error, .fault (or .default):

      os_log("web service did not respond", type: .error) 

      So, if using the external Console app, you can choose to only see messages of certain categories (e.g. only show debugging messages if you choose “Include Debug Messages” on the Console “Action” menu). These settings also dictate many subtle issues details about whether things are logged to disk or not. See WWDC video for more details.

    • You cannot use string interpolation when using os_log. For example you cannot do:

      os_log("foo \(url.absoluteString)") 

      You would have to do:

      os_log("url = %@", url.absoluteString) 
    • One of the reasons for the above limitation is to support data privacy. Primitive data types (e.g. numbers) are public by default and objects (e.g. strings) are private by default. In the previous example where you logged the URL, if the app were invoked from the device itself and you were watching from your Mac’s Console app, you’d see:

      url = <private>

      If you wanted to see it from external device, you’d have to do:

      os_log("url = %{public}@", url.absoluteString) 
    • Note, NSLog now uses the unified notification system behind the scenes, but with the following caveats:

      • You cannot control the subsystem or category or log type;

      • It does not support privacy settings.

Bottom line, print is sufficient for simple tasks, but NSLog is useful because it includes timestamp information for you.

The power of os_log comes into stark relief when debugging iOS apps that have to be tested outside of Xcode. For example, when testing background iOS app processes like background fetch, being connected to the Xcode debugger changes the app lifecycle. So, you frequently will want to test on physical device, running app from device itself, not starting the app from Xcode’s debugger. Unified logging let’s you still watch your iOS device os_log statements from the macOS Console app.

Solution 2:

If you’re using Swift 2, now you can only use print() to write something to the output.


Apple has combined both println() and print() functions into
one.

Updated to iOS 9

By default, the function terminates the line it prints by adding a line break.

print("Hello Swift") 

Terminator

To print a value without a line break after it, pass an empty string as the terminator

print("Hello Swift", terminator: "") 

Separator

You now can use separator to concatenate multiple items

print("Hello", "Swift", 2, separator:" ") 

Both

Or you could combine using in this way

print("Hello", "Swift", 2, separator:" ", terminator:".") 

Solution 3:

Moreover, Swift 2 has debugPrint() (and CustomDebugStringConvertible protocol)!

Don’t forget about debugPrint() which works like print() but most suitable for debugging.

Examples:

  • Strings
    • print("Hello World!") becomes Hello World
    • debugPrint("Hello World!") becomes "Hello World" (Quotes!)
  • Ranges
    • print(1..<6) becomes 1..<6
    • debugPrint(1..<6) becomes Range(1..<6)

Any class can customize their debug string representation via CustomDebugStringConvertible protocol.

Solution 4:

To add to Rob’s answer, since iOS 10.0, Apple has introduced an entirely new “Unified Logging” system that supersedes existing logging systems (including ASL and Syslog, NSLog), and also surpasses existing logging approaches in performance, thanks to its new techniques including log data compression and deferred data collection.

From Apple:


The unified logging system provides a single, efficient, performant API for capturing messaging across all levels of the system. This unified system centralizes the storage of log data in memory and in a data store on disk.

Apple highly recommends using os_log going forward to log all kinds of messages, including info, debug, error messages because of its much improved performance compared to previous logging systems, and its centralized data collection allowing convenient log and activity inspection for developers. In fact, the new system is likely so low-footprint that it won’t cause the “observer effect” where your bug disappears if you insert a logging command, interfering the timing of the bug to happen.

Performance of Activity Tracing, now part of the new Unified Logging system

You can learn more about this in details here.

To sum it up: use print() for your personal debugging for convenience (but the message won’t be logged when deployed on user devices). Then, use Unified Logging (os_log) as much as possible for everything else.

Solution 5:

There’s another method called dump() which can also be used for logging:


func dump(T, name: String?, indent: Int, maxDepth: Int, maxItems: Int)

Dumps an object’s contents using its mirror to standard output.

From Swift Standard Library Functions

Hope this helps!