Logging on iOS: Part 1

Android logging in mind

Javid Museyibli
The Startup
5 min readJan 9, 2021

--

One of the best things about working on iOS and Android development simultaneously is that you get to try various programming approaches, software patterns, tools, etc. While both platforms offer an interface for the development of mobile applications, the experience on each platform is significantly different.

Logging is one of the places where such a difference arises. If your expectation from logging is to simply debug while development you can use print and println in Swift and Kotlin respectively.

However, using println for logging in an Android project is a bit tricky, because the output will only appear in Run window of Android Studio, not in LogCat. It becomes tedious to use it for debugging purposes because System logs also appear here and the output gets lost among various other log outputs.

If instead of println statements you choose to use Log class, not only you will be able to find the output in LogCat window, but also you will be able to tag your output in one of the logging levels among Debug, Info, Warn, Error, and Verbose. Logging at different levels enables benefits like syntax highlighting and tagging makes it easier to find the output you’re looking for. For example, Log.e("AuthService", "The username and password do not match") statement will yield the output like the following in LogCat:

Figure 1. Accessing logs from LogCat

Logging on iOS

Unlike Android Studio, Xcode doesn’t fill the console output with system messages by default and hence it is easier to read the printed output. However, print statements do not let us achieve the same categorization that we can gain using Log class in Kotlin. They are fine for primitive use cases, but if you are working on a large project you may need to implement a better logging solution.

Starting from iOS 10.0, Apple provides a unified logging system through OSLog. Unified logging system offers some nice features that print statements lack, but it is relatively harder to set up than Log class in Android. To log the same error in the example above, we will first need to import os.log at the top of the Swift file and then call the os_log function like the following:

Now, this log output will reveal the message not only in the Xcode console but also in Console application. To view logs for your application in Console, search for your application’s main bundle identifier, select subsystem from options, and press enter. In our case the result will be like the following:

Figure 2. Access logs in Console application

In the above code, we have first defined which subsystem we would like to log the output to, we have created an OSLog instance specifying the subsystem and a category name, and finally, we have called os_log function with a message, OSLog instance, and logging type as arguments. Specifying logging type is optional here as it defaults to .default type. However, choosing one of .info, .debug, .error, .fault as a type is more recommended as leaving it to default does not produce any additional value.

In Figure 2 you may notice that I have LoggingDemo as one of the filters. You can easily create filters by saving search options and naming them.

Logging using runtime data

To log data retrieved at runtime using os_log, we will need to insert placeholders into the message using %@ format specifiers and pass the values for the placeholders into the last parameter an argument. Because the last parameter accepts variadic arguments we may pass more than one placeholder separated by commas. Let’s take a look at the following scenario:

Logging with OSLog requires some setup as demonstrated in the code sample above. In most cases, you will need to log a message using the main bundle identifier and different categories, messages, and error types. An extension may initially seem like a promising solution, however, we will not be able to benefit from using an extension on OSLog for 3 major reasons:

  1. We will not be able to log data using an interpolated string, because the os_log function accepts StaticString, not String. String interpolation takes place at runtime and thus StaticStrings won’t accept interpolated strings.
  2. os_log outputs contain information such as thread ID, line number, and some other information. Calling the function inside an extension will “fool” the system to believe that it is executed from the same line.
  3. You may try to pass variadic arguments as a parameter of to an extension function, but once it is passed down its type becomes[String], not String... and there is no way of converting it back to a CVarArg...

Starting from iOS 14, Apple introduced Logger struct which takes care of many things “flaws” of os_log, including support for string interpolation. Logger provides a similar, but simpler interface. If are planning to distribute your app to iOS 14 (and above) users, cross your fingers for Part 2 of this article. We will take a look at Logger implementation, compare OSLog and Logger in detail, and also cover privacy options tied to logging in iOS.

Other implementations

This article would be incomplete if we didn’t touch on the great work the community has done in the field. Thus before finishing this article, I find it important to include third-party libraries for logging. Although both of the logging interfaces (LogCat & OSLog) provide enough capabilities that most developers will ever need, here are a few other alternatives:

CocoaLumberjack

CocoaLumberjack is a fast & simple, yet powerful & flexible logging framework for macOS, iOS, tvOS and watchOS.

Timber

This is a logger with a small, extensible API which provides utility on top of Android’s normal Log class.

Both of these libraries provide benefits over native logging implementations and I strongly recommend checking them out for use in your projects.

Mention me on Twitter to share your feedback. Keep connected :)

--

--

Javid Museyibli
The Startup

Young enthusiastic software developer, currently full-time at PASHA Bank building Android and iOS applications.