Logging and Build Configurations With Swift

| | 0 Comments| 9:32 AM
Categories:

Editor’s Note: This post came out shortly after the release of Swift in 2014. It is not Swift 2.0-compliant, your mileage may vary!

Well, that didn’t take long. Mere days after Apple introduced Swift someone has posted what they ostensibly hope to be a replacement for Cocoa Lumberjack. Not to be outdone, we’re introducing our Swift logging API, but a confessional: ours is primitive and only useful if you typically use the Xcode console output for debugging. The reality is that is what we do 99% of the time, so it should suffice for now.

You can download our file Logging.swift from Github. We mean it, this is about as basic as it gets. Variadic arguments to the log routines isn’t supported, i.e., use the Swift \(variable) syntax to get at a variable’s value. Don’t expect to be able to log a file (maybe we’ll get there).

But, hey, this is pretty easy:

The normal “logging rules” apply: the lowest logging level is .None, that is, nothing will get logged. The highest level is .Verbose, everything will get logged. In between are .Error, .Warning, and .Info. If the log level is set to, say, .Warning, both SLogError() and SLogWarning() will result in a log.

An aside: We are using Swift’s new enumeration type and syntax in this post. If you are unfamiliar with this syntax please see Apple’s documentation.

Two convenience “macros” are also provided: ENTRY_LOG and EXIT_LOG. These are set to only emit a log at .Verbose level, and they include printing out the name of the function in which they were called. The intent is to instrument your function calls as such:

2014-06-14 18:29:45.574 buildconfigurations[3618:1291681] VERBOSE - ENTRY multiply(_:by:)
2014-06-14 18:29:45.577 buildconfigurations[3618:1291681] VERBOSE - 6 multipled by 7 is 42
2014-06-14 18:29:45.579 buildconfigurations[3618:1291681] VERBOSE - EXIT multiply(_:by:)multiply(_:by:)

Note that the EXIT log currently shows the signature of the function twice. This is a known bug in XCode 6 Beta 1 (see here).

Our logging example is merely a means to end, however, which is demonstrating how to use build configurations for conditionally compiling in Swift code. Unlike Objective-C, Swift does not have C-style macros and other preprocessor features. Note that we said C-style, not that it didn’t have any of those features at all. It does, they are just quite a bit different. Apple puts it this way: “The Swift compiler does not include a preprocessor. Instead, it takes advantage of compile-time attributes, build configurations, and language features to accomplish the same functionality. For this reason, preprocessor directives are not imported in Swift.” So, whereas we used preprocessor macros and conditionals for all manner of “tricks”, in Swift we use other means to accomplish the same objectives.

Our objective is to use build configurations to support multiple “types” of builds. For example, we often need a build configured to communicate with a staging server, or a build configured with full debug logging. You could even use this mechanism to create “branded” builds, i.e., the same functionality with different logos, color schemes, etc. If you are unfamiliar with build configurations, see our post here.

In this example we are going have two build configurations: DebugStaging and ReleaseProduction. For debug builds we’ll set our logging level to .Verbose, and for our release builds we’ll set it to .Error. For staging builds we want to use our staging webservices server at https://staging.webservice.net, and of course for production we’ll use the production services at https://production.webservice.net.

To enforce the immutability of the webservice URL, we’ll create a Swift file called Constants.swift and add

If neither Staging or Production is defined the project will not build as gWebServiceUrl will be unresolved.

In our application(_:didFinishLaunchingWithOptions:) function we place

This will set the logging level global flag (which defaults to .None) based upon whether we are building a debug or release build.

Now, how to set the flags. Easy. In Xcode, click on your Project file in the Navigator pane (far-left). The project file is the one with the blueprint icon. Click the Info label first and create two new configurations for DebugStaging and ReleaseProduction. To create a new build configuration, click on + under Configurations and select either Debug or Release (the two default build configurations created) and duplicate. Then, you can rename by clicking on the name and changing the text. Here’s what the Info pane should look like:

swift_build_configuration

Once that is complete go to the Build Settings pane and in the search box type Swift to pull up all of the various Swift-related settings. We are interested in the Swift Compiler – Custom Flags, where you can specify -DDebug -DStaging for your DebugStaging configuration and -DRelease -DProduction for your ReleaseProduction build. Once completed your Build Settings pane should look a bit like this:

swift_build_defines

Finally, which build configuration is used to build is driven by the Scheme you have selected, and how that scheme is configured. Go to your schemes and create ones for DebugStaging and ReleaseProduction and set them to use the build configurations you’ve just created. Now when you want to switch among them you can by simply changing your scheme. For more information on build schemes, see our previous post on the topic.

swift_build_scheme

As always, the source code for this blog post is available on Github.

Leave a Reply

Your email address will not be published. Required fields are marked *