Logging is very useful in all languages to allow debug applications and to help to solve bugs. In iOS development, we can use NSLog
for logging apps written on Objective-C and print
also in Swift. Basically, these solutions write a line of text in console. If we wanted to send logs of our iOS app, we would have to save them in a file first, for this purpose we can use a third party library as CocoaLumberjack
.
Use NSLog
for debugging is very easy, it is just like a create a NSString
. It has the following declaration:
void NSLog(NSString *format, ...);
The main characteristics of NSLog
are:
NSLog
is slow.- It is synchronous.
- It outputs log to ASL and Xcode console.
- It has not log levels what makes more difficult to tell the difference between errors, warnings, and other information.
- It only works when the device is attached to the computer, then it is not a solution for remote access.
Examples of using:
NSLog(@"Hello world"); // Writes in console: Hello world
NSLog(@"Hello world %@", @"Tim"); // Writes in console: Hello world Tim
NSLog(@"Tim is %i years old", 43); // Writes in console: Tim is 43 years old
For more information about String
format specifiers.
In many cases, it would be useful to print in console a complete object. If we try to print it directly, we will show the name of the class and its memory address in console.
Example:
// Header file
@interface Person: NSObject
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, copy, readonly) NSString *surname;
- (id)initWithName:(NSString *)aName andSurname:(NSString *)aSurname;
@end
// Implementation file
#import "Person.h"
@implementation Person
- (id)initWithName:(NSString *)aName andSurname:(NSString *)aSurname
{
if (self = [super init]) {
_name = aName;
_surname = aSurname;
}
return self;
}
@end
// Using NSLog
Person *person = [[Person alloc] initWithName:@"Tim" andSurname:@"Cook"];
NSLog(@"%@", person); // Writes in console: <Person: 0x137641b00>
If we want to show a better log, we can override the property description
with a proper string.
// Header file
@interface Person: NSObject
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, copy, readonly) NSString *surname;
- (id)initWithName:(NSString *)aName andSurname:(NSString *)aSurname;
@end
// Implementation file
#import "Person.h"
@implementation Person
- (id)initWithName:(NSString *)aName andSurname:(NSString *)aSurname
{
if (self = [super init]) {
_name = aName;
_surname = aSurname;
}
return self;
}
- (NSString*)description
{
return [NSString stringWithFormat:@"Name: %@, Surname: %@", self.name, self.surname];
}
@end
// Using NSLog
Person *person = [[Person alloc] initWithName:@"Tim" andSurname:@"Cook"];
NSLog(@"%@", person); // Writes in console: Name: Tim, Surname: Cook
A good practice is not to show logs in your release version, in some cases can be useful but most of them should not be printed. Defining a macro DLog
in the .pch
file and using instead of NSLog
, it will allow you to print logs only in debug mode but it will never print them on release mode.
Macro:
#ifndef DLog
#ifdef DEBUG
#define DLog(_format_, ...) NSLog(_format_, ## __VA_ARGS__)
#else
#define DLog(_format_, ...)
#endif
We can use NSLog
in Swift also, but it is a better practice to use print
. Basically, print will add a newline at the end of its content as NSLog
. Here, we can find a good comparative between print
and NSLog
(Note: the article compares NSLog
vs println
instead of print
but apply also to print
. println
is deprecated since Swift 2.0).
Examples of using:
let name = "Tim"
let age = 43
print("Hello world"); // Writes in console: Hello world
print("Hello world \(name)"); // Writes in console: Hello world Tim
print("Tim is \(age) years old"); // Writes in console: Tim is 43 years old
In many cases, it would be useful to write in console a complete object. If we try to print it directly, we will show the name of the class in console.
Example:
class Person {
var name: String
var surname: String
init(name: String, surname: String) {
self.name = name
self.surname = surname
}
}
print(person) // Writes in console: Person
If the class implement the protocol CustomStringConvertible
for other purposes, we will this string in console.
Example:
class Person {
var name: String
var surname: String
init(name: String, surname: String) {
self.name = name
self.surname = surname
}
}
extension Person: CustomStringConvertible {
var description: String {
return "Name: \(name) - Surname: \(surname)"
}
}
print(person) // Writes in console: Name: Tim - Surname: Cook
But if we do not want to implement this protocol but you want to have a better description of the object in console, you can implement CustomDebugStringConvertible
protocol.
Example:
class Person {
var name: String
var surname: String
init(name: String, surname: String) {
self.name = name
self.surname = surname
}
}
extension Person: CustomDebugStringConvertible {
var debugDescription: String {
return "Debugging --> Name: \(name) - Surname: \(surname)"
}
}
print(person) // Writes in console: Debugging --> Name: Tim - Surname: Cook
Although print
and NSLog
are very useful options and it is very easy to use them, they are quite simply. Moreover, these options only write in console or ASL but they do not save a log in a file then we are not able to send to us if we need it. For these reasons, there are some third party libraries which are more powerful and more customizable than the Apple ones.
- Define different log levels.
- Choose the output: Xcode console, Apple System Logs or/and file.
- Define rolling frequency and maximum file size.
- Faster than NSLog.
For more information about the installation, first steps and advanced configurations CocoaLumberjack GitHub
- Define different log levels.
- Choose the output: Xcode console, file or/and cloud.
- Colored output depending on log level.
- AES256 encryptation to send logs to cloud.
- Easy configuration for each output.
- Mac app to analyze logs sent from your app.
For more information about the installation, first steps and advanced configurations SwiftyBeaver GitHub
- Define different log levels, marking a special tag "log severity".
- Make easier to find the exact line of code where is printing the log.
- Has a function
trace()
which shows the exact execution path. - Is extensible, it allows to define custom implementations of different functions that could be used during the logging process.
- Define rolling frequency.
- Choose the output: Xcode console, Apple System Logs or/and file.
For more information about the installation, first steps and advanced configurations CleanroomLogger GitHub
Articles
- Debasis Das (March 2015). Swift print, println, NSLog
- String format specifiers. String format specifiers
GitHub