Logging on iOS: Part 1
Android logging in mind
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
:
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:
let subsystem = Bundle.main.bundleIdentifier!
let logInstance = OSLog(subsystem: subsystem, category: “AuthService”)
os_log(“The username and password do not match”, log: logInstance, type: .error)
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:
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:
let subsystem = Bundle.main.bundleIdentifier!
let logInstance = OSLog(subsystem: subsystem, category: “AuthService”)
os_log("The username (%@) and password do not match", log: logInstance, type: .error, "jmuseyibli")
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:
- 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. 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.- 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]
, notString...
and there is no way of converting it back to aCVarArg...
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 :)