iAchieved.it

Software Development Tips and Tricks

By

Quickly Adding Multiple Frameworks to an Xcode Project

There are a few popular third party iOS frameworks such as AFNetworking and Parse that require you to add a number of Apple frameworks to your Xcode project. For example, if you add the Parse framework to your project you’re looking at also adding:

  • AudioToolbox.framework
  • CFNetwork.framework
  • CoreGraphics.framework
  • CoreLocation.framework
  • libz.dylib
  • MobileCoreServices.framework
  • QuartzCore.framework
  • Security.framework
  • StoreKit.framework
  • SystemConfiguration.framework

Adding each of these one at a time (with the pane closing after each addition) is annoying, so thankfully Xcode provides a quicker way using the Apple command key. To add multiple frameworks at once hold down the command key when making a selection in the Choose frameworks and libraries to add pane. After making all of your selections click Add.

add_multiple_frameworks

By

A Technique for Displaying iOS Build Versions (in Swift)

It is a universally recognized best practice to visibly expose your iOS application’s version number somewhere on the screen. Often times you will see it on the application’s splash screen or can get to it by selecting the About menu item (assuming the application has a menu). No matter where it’s location (I have my preference as to where it should be, which I will describe momentarily), it’s a good idea to have it easily viewed by both your beta testers and your users.

To be clear, in this post we’re not going to talk about techniques for automatically incrementing the version or build numbers, or strategies for injecting git commit-ish or branch identifiers into your application. There are plenty of resources on these topics on the Web. What we will discuss are the differences between CFShortVersionString and CFBundleVersion and how you can combine the two to provide a straightforward and adequate version number for your application.

I have always used the major.minor.build version number format, where major.minor are typically referred to as marketing versions, because they are heavily influenced by marketing departments and their desire to articulate to customers “how much new content” is in a given version compared to a previous one. For example, it’s pretty safe to assume that version 2.0 of some given software has a lot of new features compared to version 1.0. It’s also pretty safe to assume that version 1.1 is not radically different from version 1.0.

So we can agree that the marketing and product management teams are walking around talking about version major.minor, and that’s fine. For the purposes of distributing and tracking versions of our application, however, we need one more field, the build identifier. Ideally, this number should always be increasing, and no two different builds that are distributed to beta testers should have the same build number. (Note: we say “ideally” but in reality the CFBundleVersion, which we will use as our build identifier, must be incremented when posting a new version to iTunesConnect).

It turns out that our Xcode target comes with Identity settings such as Bundle Identifier, Version, and Build. We will leverage those settings, and in particular refer to the combination of Version and Build as our application version.

appversion_initial

The Xcode Version is prepopulated with 1.0 for a new project, and this will serve as our major.minor marketing version. It also happens to be the version number that people will see on the iTunes store. The Xcode Build version will be the build value in our major.minor.build tuple. Again, let’s take a moment to stress that for this post we aren’t going to talk about how to automatically increment that value and will increment it by hand every time we build a version that we want to distribute.

Now that we know what values we want to treat as major.minor.build let’s look at how we can access them in our application. It turns out these two values have names in our application’s main bundle, which is loaded from the contents of the applications Info.plist. We can extract the values by referring to them by their names: CFBundleShortVersionString and CFBundleVersion. Here’s the Swift code for doing so, and it should be placed in your application(application:didFinishLaunchingWithOptions:) routine in the AppDelegate class.

let appInfo = NSBundle.mainBundle().infoDictionary as Dictionary<String,AnyObject>
let shortVersionString = appInfo["CFBundleShortVersionString"] as String
let bundleVersion      = appInfo["CFBundleVersion"] as String

Note: We’re leveraging Swift’s as keyword, which allows us to specify that appInfo is a Dictionary of AnyObjects with keys of type String. Furthermore, we apply as again to tell Swift that the objects we are retrieving for the keys CFBundleShortVersionString and CFBundleVersion are Strings. Don’t make the mistake in thinking that our appInfo dictionary consists of only of String-String key-value pairs. Refer to Apple’s documentation for details of the types of values stored.

We can combine the CFBundleShortVersionString and CFBundleVersion together as:

let applicationVersion = shortVersionString + "." + bundleVersion

Okay, now that we’ve established we can create a version string from CFBundleShortVersionString and CFBundleVersion, where should we display it? I am a fan of placing it in the application’s iOS Settings page. This is page created automatically by iOS for your application, and it can be found by going to the iOS Settings application and scrolling down to the bottom of the default page. For complete details of an application settings page, see Apple’s documentation.

To create a settings page you will need to create a Settings Bundle for your application. In Xcode select File – New – File and when presented with choosing a template for your new file, select iOS – Resource – Settings Bundle, like so:

add_settings_bundle

Select Next and ensure that your settings bundle is included in your application target (it should be by default). Click Create and you will see Xcode has included Settings.bundle in your project (I also place my Settings.bundle in the Supporting Files group):

settingsbundle_added

We are interested in the file called Root.plist, where we will add a Group section called About, which will contain both our copyright line as well as version number. Click on Root.plist to bring up the default contents:

rootplist_default

Delete Item 1, Item 2, and Item 3 by clicking on the row for the item to highlight it, and then right-click and choose Cut. When you delete one item the remaining items will renumber themselves, so simply ensure only one item remains, Item 0, which by default is a Group.

Click on the disclosure triangle for Item 0 and you will see Title and Type. Leave the Type as it is (a Group), and change the title to About. Your panel should look like this:

rootplist_about_only

Now for the trickier part, and it’s only trickier because the Xcode plist editor can be a pain. Click on the Preference Items row and right-click and select Add Row. You will probably notice that our Group got moved to Item 1. We don’t want that, so click and drag the Item 1 row back to be the first entry. Add another row, and again, rearrange the entries such that our About group is the first. It should look something like this:

rootplist_withabout_and_fields

Now, let’s set the two fields. The first will be our copyright statement. Click on the disclosure triangle for Item 1, and then right-click on Item 1 and Add Row. The default row should have Default Value as its type. Put your copyright statement as the default value, change the title field to Copyright, and then for Identifier put application_copyright.

The second field will be our application version number. Click on the disclosure triangle for Item 2, and then right-click on Item 2 and Add Row. Set the default value to 0.0.0, change the title field to Version, and then for Identifier put application_version.

Your settings Root.plist should now look like

rootplist_completed

If you ran the application as is and then navigated to the iOS Settings page for the application, you would see your copyright statement and 0.0.0 as the version number. Of course we want to get the actual version number in there. Here’s the code for doing so (again, place it after your extract the values from appInfo in your application delegate’s application(application:didFinishLaunchingWithOptions) routine):

let defaults = NSUserDefaults.standardUserDefaults()
    defaults.setObject(applicationVersion, forKey: "application_version")
    defaults.synchronize()

NSUserDefaults.standardUserDefaults() gives us access to our application’s settings, to which we write our applicationVersion to the entry given by the key application_version. defaults.synchronize() persists the updated information to storage such that other applications (such as iOS Settings) can retrieve the value.

Run the application again, browse to the iOS Settings for the application, and find

appversion_final

To increment your build number, go to your project target and simply change it, rebuild the application, and see the new number available in the settings page.

Again, we’ve tiptoed around the topic of how and when to update your build version. In the past I’ve written elaborate scripts which ran under CruiseControl, extracted the SVN version number, injected that number into the Info.plist using PListBuddy, generated fancy build records that could be used independently to package other components together, etc. In the end I found that it was often more trouble than it was worth, and if you are running a development team with only a handful of folks, simply appoint one to be responsible for building the version that gets tested and eventually posted to Apple for review. They can either build the application by hand (using Xcode) or develop automated tools for it; the important thing is the information is unique for each build distributed and can easily be viewed!

By

Using Property Lists for API Keys in Swift Applications

Xcode 7.3 Swift 2.2

You’ve written an open source application for demonstration or illustrative purposes, and your application requires REST API keys for services such as Facebook, Twitter, Wunderground, or Parse or… You get the idea. How do you submit your application to Github for others to use without exposing your own API keys? I can’t take credit for the original idea, but here’s a Swift take on technique described by Mike Lazer-Walker.

We’ll be using iOS property list files and a Swift wrapper. The highlights here are that we are not checking our property list into source control (Git), but our project will reference a file called ApiKeys.plist. If you download the project from Bitbucket and try to run you’ll get an error because the file ApiKeys.plist is missing. This is where you create your own ApiKeys.plist and put in your own API keys.

Note: This technique we are using here is specific to git. If you are using a different revision control system look up the feature for how to ignore certain files in your source tree (for example, in Subversion you would use the svn:ignore property).

The general technique is to add a .gitignore file to the root directory of our project and add the following line:

ApiKeys.plist

and then, to create a ApiKeys.plist local to your machine with something similar to the following content:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>API_CLIENT_ID</key>
	<string>MyAPIClientID</string>
        <key>API_SECRET</key>
        <string>MyAPISecret</string>
</dict>
</plist>

Once we have that we need a clean mechanism for obtaining the values for our keys. This is done through a utility file in Swift (we call it ApiKeys.swift for simplicity) with the contents:

The code is straightforward and meant to be called like this:

The routine looks for the ApiKeys.plist file in the application’s resource bundle, loads it as an NSDictionary and then looks up the value for the given key as a String.

It should be pointed out that what this code does not do is in any way obfuscate the contents of the plist. That is, your iOS binary will have the keys included in the resource bundle and could be extracted by someone intent on getting at them.

Now, let’s look at the specifics on how to use this technique in your Swift projects.

Step 1. Add ApiKeys.plist to your .gitignore file

This is important; failure to do so will cause Xcode to try to automatically add your plist file to revision control. We don’t want that.

Step 2. Create ApiKeys.plist

You can either create ApiKeys.plist by hand or use Xcode to create it. Just make sure that you’ve added ApiKeys.plist to your .gitignore file!

To create ApiKeys.plist use File – New – File in Xcode and create a Resource file of type Property List. Select Next and name the file ApiKeys (the plist file type will be created automatically). If you name it something other than what you put in your .gitignore, it will get added to revision control, which is not what you want..

createPropertyList

Also ensure that the file is included in your application target.

addApiKeysFile

Now add the API keys you need for your application to your ApiKeys.plist file. Select the ApiKeys.plist file in Xcode and click on the Root dictionary line, then right-click and choose Add Row. Double-click on the text that says New item and rename it to API_CLIENT_ID. Double-click on the Value column and type in your actual string. For our purposes I’ve just put MyAPIClientID.

SetAPICLIENTID

Step 3. Create ApiKeys.swift

We want a simple wrapper for obtaining our keys, so create a new file called ApiKeys.swift and in it place the following:

Step 4. Use the valueForAPIKey Routine

And that’s it! Once you have your clientID you can add it to whatever REST calls you need to (or if you are dealing with an annoying API add it as a special HTTP X- header).

Now, I will confess, it is unclear why there isn’t a more elegant way to do this in Xcode. Although this technique is serviceable it isn’t very DRY (don’t-repeat-yourself), but then again, how many times have started a new project only to repeat the same “bootstrap” steps such as adding AFNetworking, CocoaLumberjack, and all of the other popular frameworks? What would be handy is the ability for Xcode to obtain API keys and other confidential information from an application like Keychain and compile them in a secure manner. Que será, será I suppose.

By

Managing iOS Application Icons


Editors Note: This article was originally published on January 26, 2014. The included automator script needs to be expanded to include icon dimensions for the latest lineup for iPhones, but you can easily do that with OS X Automator.

You might be interested in checking out our new post on quickly creating Apple Watch icons!

I will admit that I’m a terrible graphic artist. Any icons, logos, graphics, etc. coming out of iAchieved.it LLC most certainly did not come from me. Yet, I do have to deal with taking the artwork created for us and importing it into our iOS applications, and although it has gotten easier, Apple hasn’t yet made it trouble-free. Trouble-free in my opinion would be supplying one file (the original master) to XCode and letting it take care of the rest. But, we aren’t there yet so let’s look at the next best thing.

In this blog post I’ll look at how to take your application icon and create all the right files to import into your XCode project. Now, there will be a few assumptions. They are:

  • you have an original 1024×1024 PNG file of your application icon
  • you are using XCode 5 and are ready to explore the joys of Images.xcassets
  • you’ve at least heard of OS X Automator

If you don’t have a 1024×1024 PNG of your application icon, I suggest you go get one. You will at least need it to create your application entry in iTunesConnect. Every other year Apple seems to be raising the resolution that the AppStore icon must be in. Suffice it to say, this year its 1024×1024.

As the iPhone and iOS operating system have evolved over the years, so have the icon sizes required in your application bundle. On a “non-retina” iPhone (such as the 3GS), you could get by with supplying a 57×57 PNG and be done with it. Unfortunately on an iPhone 4 (retina display), the 57×57 PNG looked like, well, less than crisp. So, you had to begin bundling a 114×114 (note that 114 is 57 times 2) icon with your application. If you read up on Apple’s documentation you could supply on a single icon labeled @2x that was 114×114 and the OS would display the image correctly regardless of the phone.

With iOS 7 and XCode 5 it gets even crazier. You can supply all of the following dimensions for your application icon:

  • for an iOS 7 launchpad icon, supply a 60pt icon in 2x resolution (that is 120×120)
  • for an iOS 5 or 6 launchpad icon, supply a 57pt icon in 2x resolution (that is 114×114)
  • for an iOS 7 Spotlight icon (the miniature icon that displays when you search for an application), supply a 40pt icon at 2x resolution (that is, 80×80)
  • for an iOS 5 or 6 Spotlight icon, supply a 29pt icon at 2x resolution (58×58)
  • for an iOS 5, 6, or 7 Settings icon, supply a 29pt icon at 2x resolution (58×58)

And for any phone that has a target earlier than iOS 7 you have to supply 1x versions for the Spotlight and Launchpad icons, 29pt and 57pt, respectively. Yikes!

I don’t know about you, but I get tired of resizing icons and all that. So, before we get to using Images.xcassets, let’s take a look at an Automator script that we can add to our Mac as a Service. This will allow us to take our source application icon (the one at 1024×1024 resolution), and generate all of the little icons we need. Then its drag-and-drop into XCode and done.

Enter Automator, and better, enter Automator Services. We’ll save how to create Automator scripts (actually, I think they are called “workflows”) for a later date. For now, we’ll just give you our Automator Service Workflow CreateXCodeIcons. To get it, simply download CreateXCodeIcons.zip, unzip, and then right-click on the CreateXCodeIcons file. The application is currently unsigned, so you will get a prompt requesting whether or not to open a file from an unknown developer. Click Open.

A prompt that looks like this will ask whether or not you want to install the CreateXCodeIcons service.

want_to_install_service

Click Install, and now you will have a new menu item in your Finder called CreateXCodeIcons.

workflow_installed

Okay, now take your 1024×1024 icon (which in our screenshots we’ll call icon.png), place it in its own folder (this is important to keep everything organized, otherwise the Service will scatter icons in whatever location you had your original in) and right click on it, scroll down to Services and select CreateXCodeIcons. The automator service will begin creating copies of the original and resizing them to the dimensions that will be loaded into your XCode project.

ding_finder_icons

Notice the original icon.png followed by

  • 60pt@2x.png
  • 57pt@2x.png

and so on. These are the files that you will drag into the XCode Images.xcassets folder to provide icons for your application. The names we created are arbitrary, when you add them to your XCode project they will actually be copied and embedded into the project. But, there is a method to our madness. The names line up with where you will place them on the AppIcon canvas. For example, the 60pt@2x.png icon would be dragged to the location that indicates “2x 60pt”, as shown below:

ios760pt

Adding Icon Assets to Your XCode Project

Now that we can easily create all of the icons we need for our application, let’s look at adding them and some of the nuances involved. For a quick start, download our ding project from GitHub. Select the master branch which does not contain any app icon assets.

Open the XCode project, select the ding target, and go to the General tab of the target. Scroll down to the section that says App Icons and notice it probably looks like this:

asset_catalog_panel

To get started with using an asset catalog for your app icons first select the text that says “Don’t use asset catalogs” and then you must click on the button that says “Use Asset Catalog”. A dialog indicating that there currently isn’t an asset catalog will pop up, and you will want to click Migrate.

migrate_app_icons

You will now see a new folder in your project named Images.xcassets.

new_images_xcassets

Once you’ve added the Images.xcassets folder you will want to add a LaunchImage as well, otherwise you will receive the error Asset Catalog Compiler Error: None of the input catalogs contained a matching launch image set named "LaunchImage".. This is simple to fix: click on the Images.xcassets folder and then beneath the list (of one) that contains AppIcon, click the plus sign and choose New Launch Image. The asset catalog compiler error will go away.

You can build the application at this point, you will just get the default iOS “pencil-sketch” icon. Let’s add some icons. Take the 60pt@2x.png icon that was generated and drag-and-drop onto the AppIcon canvas in the spot for iPhone App iOS 7 60pt. If your target is for iOS 7.0 and higher you can stop right here. This is the only icon you actually are required to supply, but since we already generated the Settings and Spotlight icons, you might as well add them, they are highly recommended in the Human Interface Guide from Apple.

iOS 6 and Earlier

By default our ding target is set to deploy to iOS 7.0 and higher. It turns out that iOS 7 is available only for iPhone 4 and up, and those models do not use a 57×57 “low-resolution” icon. However, if you want to target the iOS 6, that OS runs on the iPhone 3GS which does use the lower resolution icons. To see what happens when you don’t provide all of the required icon assets for your Deployment Target, go back to the Deployment Info panel of the ding target application and change 7.0 to say, 6.1. XCode won’t recognize right away that you haven’t included all of the proper icons, but if you then run the application (or build it), you will see a warning along the lines of A 57x57 app icon is required for iPhone apps targeting releases of iOS prior to 7.0, as well as a warning similar indicating that a @2x icon is required. To fix, drag-and-drop the 57pt@2x.png and 57pt.png icons onto the AppIcons canvas and XCode will automatically create entries for the devices/targets they support. The final canvas should have these entries:

final_icons

Remember, Apple recommends you supply icons for Settings and Spotlight, and the CreateXCodeIcons service from above creates the right icons for these too!

Conclusion

Managing all of the various dimensions of application icons required for an iPhone application can be a real pain. Fortunately, we can automate and streamline a lot of the drudgery. Using the OS X service CreateXCodeIcons and the Asset Catalog feature of XCode 5 projects, app icon management overhead can be kept to a minimum.

The final ding project, with application icons added, can be viewed by switching the GitHub ding project to the with_app_icons branch.

By

Playing a Simple Sound in iOS

Many of the best tutorials on the web are how to do simple things. Application development in the 21st century is often taking a number of building blocks and creating something one block at a time. Hopefully this tutorial helps add another building block to your iOS application development arsenal.

All we want to accomplish is to play a simple sound in our app – we’re not streaming music here or layering the sound of exploding zombies, just the straightforward chime or “jet” sound when sending an e-mail on the iPhone. Okay, less talk more do.

You might want to go ahead and clone the example repository from GitHub, we have some sample files for you to work with.

Get an MP3 file, say ding.mp3 that you can find in the folder ding/assets. We’re going convert it to a CAF (Core Audio Format) file with afconvert. Core Audio is the “native” format for iOS. To get the highest quality possible we want to review the source audio quality and ensure our conversion takes full advantage of what we have. To do so, run the file through afinfo first:

Note the 44100 Hz. This is the sample rate of the audio contained in the file. Let’s convert to CAF and retain our sample rate:

The “magic string” LEI32@44100 is read as “Little Endian Integer 32-bit at 44.1 kilohertz”. The resulting CAF file will be added to your XCode project. The mp3 file can be kept around as the source reference.

The remaining instructions assume you’ve created an XCode project that looks like the one you can clone from GitHub. We called our application ding, and it’s a basic single-view application with a Play Sound button in the middle of the view. Using XCode we created an IBAction such that when you pressed the button it calls a method on the ViewController. Once wiring up the button and view controller has been completed, proceed with importing the sound file into your project.

Using the Finder, drag and drop the file into your XCode project. We typically place audio assets under Supporting Files. If you have a large number of audio assets you may consider creating a group for them. Ensure that Copy items into destination groups’ folder is selected and ensure that the file is added to your project target. If it is not added to the target it won’t be a part of your application bundle, thus when you go to ask iOS to play the file it will silently (no pun intended) fail.

In our sample application found at Github you will see a single method on the view controller:

- (IBAction)playSound:(id)sender {
}

This is where we’ll play our sound, but we first need to add the requisite header to the project.

At the top of the view controller, go ahead and add

#import <AVFoundation/AVFoundation.h>

This imports the AV (audio/visual, audio/video, whatever) headers. You should not have to add any additional libraries to a default project.

Before we present the final project, let’s look at a method that will not work. Why show you something that won’t work? Well, trust us, you’re going to do this soon or later, and then forget about the incorrect implementation and scratch your head when your sound won’t play.

- (IBAction)playSound:(id)sender {
  NSLog(@"playSound");  
  NSString* path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"ding.caf"];
  NSError* error;
  AVAudioPlayer* player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
  [player play];

}

Your log will print playSound, but you won’t hear a thing. Why? Notice that your playSound: method allocates and initializes an AVAudioPlayer, and then calls the play method on that object. Do you think iOS stops what its doing and plays the sound, and then resumes? Hardly. That play method queues a routine up to play the sound while the OS keeps on moving. Unfortunately, by the time it gets around to playing, your code has moved on and lost the reference to the player object, so ARC (automatic reference counting) is going to release it. The end result? No sound.

Well, let’s fix that, by creating us an AVAudioPlayer that sticks around. Our view controller will now have a strong reference to an AVAudioPlayer, and will also serve as an AVAudioPlayer delegate, so we can receive updates about the playback of our sound, if we choose to do something when the playback completes (or errors). At the top of the ViewController.m file:

@interface ViewController () <AVAudioPlayerDelegate>
@property (nonatomic, strong) AVAudioPlayer* player;
@end

Now, in our playSound: method:

- (IBAction)playSound:(id)sender {
  
  NSString* path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"ding.caf"];
  NSError* error;
  
  self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
  self.player.delegate = self;
  [self.player play];
  
}

To receive information about playback completion, go ahead and implement (in the ViewController of course, which is your AVAudioPlayerDelegate):

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
  if (flag) {
    NSLog(@"audioPlayerDidFinishPlaying successfully");
  }
}

Note that there are more delegate methods, and you should review the AVAudioPlayerDelegate documentation.

And that’s really about it for playing a simple sound! Run your app and press the Play Sound button and you should get a nice little ding!

In the XCode project on Github you will notice the following at the top of the ViewController file:

// Comment this line out to do a test run with an incorrect implementation
// that causes the AVAudioPlayer to be lost to ARC
#define CORRECT_IMPLEMENTATION

If you comment out the line #define CORRECT_IMPLEMENTATION you’ll experience the joys of getting no sound when you press the Play Sound. Give it a try as a reminder that your AVAudioPlayer object needs to be retained by your view controller.

By

[UIColor colorWithRed:green:blue]

If you’ve done web development and needed to add that perfect splash of color, you’ve come to appreciate websites like Color Hex or W3Schools for grabbing an HTML code like #8A2BE2 (it’s blue violet if you were curious) or an RGB code like 138,43,226 (it’s blue violet too). Or, perhaps you’ve used DigitalColor Meter on the Mac or something equivalent for Windows. What you probably haven’t seen is a tool that displays the color code in the format that for some inexplicable reason Apple chose to use with UIColor. Per the UIColor Class Reference, if I wanted to use blue violet, I would need to code up

UIColor* blueViolet = [UIColor colorWithRed:0.541176
                                      green:0.168627
                                       blue:0.886275 alpha:1.0];

From the UIColor class reference, the first parameter (what you give to colorWithRed:) should be “The red component of the color object, specified as a value from 0.0 to 1.0.” Hmm… I’ve always seen red given as hexadecimal or decimal, but never as a “value between 0.0 to 1.0” So what ends up happening is I find the color I want (usually with the DigitalColor Meter tool), pull out a calculator, and type in the R value, divide by 255, and then repeat for G and B. And here we thought computers were supposed to make our lives easier. Or better, if someone gives me a hexadecimal code, I convert to base 10 and then divide by 255.

This is all a bit too annoying, so we present our own UIColor Picker. You know how to use this tool… it’s pretty straightforward. Find the color you want, copy and paste the code. Done. Going for that teal (#008080) look? Type it in and you’re done

UIColor* teal =[UIColor colorWithRed:0.00000
                               green:0.501961
                                blue:0.501961 alpha:1.0];

As a bonus, you can slide the opacity bar to select an alpha channel value. The possibilities are endless. Well, okay, the possibilities are finite but you get the idea.

By

Creating Attractive Flat Buttons on iOS 7

Update! – If you are interested in learning more about Apple’s new Swift programming language, we have a version of using FlatButton with Swift over here. Enjoy!

Well, it’s certainly been a while since our last post.  We’ve been busy working on a variety of projects, but things have slowed down a bit as the 2013 holiday season approaches so here we are with a simple tutorial on creating attractive “flat” buttons on iOS. For the impatient that want the code, everything below is in an XCode project hosted on Github.

While many of our tutorials assume you’ve done no iOS programming in the past, this one will assume you are familiar with how to create a new iOS application, add 3rd-party frameworks and code, etc.  We’ll be using Jason Everett’s FlatButton class, which you can download from Github.  You only need to include FlatButton.m and FlatButton.h in your project, but make sure and link to the QuartzCore framework.

After creating your project and including FlatButton.h and .m (and link to QuartzCore!), add the following to your ViewController.m file:

#import "FlatButton.h"

and

-(void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  UIButton* flatButton = [[FlatButton alloc] initWithFrame:CGRectMake(20,350,280,40)
                                             withBackgroundColor:[UIColor colorWithRed:0.521569
                                                                                 green:0.768627
                                                                                  blue:0.254902 alpha:1]];
  flatButton.layer.cornerRadius = 10;
  [flatButton setTitle:@"Sign In" forState:UIControlStateNormal];
  flatButton.titleLabel.font = [UIFont fontWithName:@"Avenir-Black" size:20.0f];
  [flatButton addTarget:self action:@selector(flatButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:flatButton];
}

-(void)flatButtonPressed:(id)button {
  NSLog(@"flatButtonPressed");
}

A brief discussion on what’s going on here. First, rather than drag-n-drop of your button from XCode onto the view controller canvas, you’re programmatically creating the button. FlatButton inherits from UIButton so you can assign directly to a UIButton*. When creating UI elements programmatically you have to explicitly set things that XCode did for you when using NIBs or storyboards. For example, above we declare the where and what of the button layout with CGRectMake, as well as the details of the background color of the button.

To add additional appeal we use the cornerRadius attribute of the UIButton‘s layer object. Setting this to 10 as shown above will give the button’s corners a nice rounded effect.

Finally, we set the font name and size of the button label, add the method that we want called when the button is pressed (UIControlEventTouchUpInside), and then add the button to our view controller’s view!

flatbutton

That’s really all there is to it. The “hard part” is programming the button placement on an iPhone 4 vs. an iPhone 5 (perhaps another blog post or we’ll extend this one). Everything else is handled nicely by the FlatButton class. For more details on FlatButton see Jason Everett’s blog.

Don’t forget you can follow us on Twitter, where you’ll get the latest updates on our iOS tutorials.

By

A Pesky Cocoa Lumberjack Bug

The latest release of iOS 6 has “introduced” a pesky Cocoa Lumberjack bug. If you recall, Lumberjack is our favorite enhanced logging API for use with iPhone development. If you don’t recall, well… Lumberjack is our favorite enhanced logging API for use with iPhone development.

If you’ve done any web service development you may be familiar with the technique of starting a query on a asynchronous thread, like this:

dispatch_queue_t reattemptQueue = dispatch_queue_create("reattempt", NULL);
  dispatch_async(reattemptQueue, ^{
    [self reattemptLogin];
  });

In short, we’re trying to log in to a service and the initial attempt failed, so we’ll create a GCD queue called reattempt and then dispatch the call [self reattemptLogin]; on that queue. Our [self reattemptLogin] code does the following:

-(void)reattemptLogin {
  ENTRY_LOG;

and that’s really all we have to show you because, boom, it crashes right there! The error you will mostly see is EXC_BREAKPOINT against the code

dispatch_queue_t currentQueue = dispatch_get_current_queue();

inside the Lumberjack source. Here’s the break where the exception was generated:
crash

And here is the crash traceback.

crashtrace

There is actually a ticket opened in Github for this issue. As hinted at by the ticket, the workaround here is to, rather than create a new queue, get the global queue:

dispatch_queue_t reattemptQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

Your application can still successfully dispatch an operation to a queue and not block the UI thread, but this time it won’t crash if you are doing logging.

What you may find is that your application works fine in a release mode since there is no logging with Lumberjack, but when you start your debug builds up again things start crashing for no apparent reason! Take a look and see if you are creating named queues with dispatch_queue_create and replace it with dispatch_get_global_queue!

By

Managing Multiple Schemes and Build Configurations

I will confess, getting a mental grip on XCode schemes and build configurations took a bit of time. In the end I came to use schemes as managing the different types of builds I may want to generate at any given time. For example, the iAchieved.it Lewis and Clark app has five schemes:

  • adhoc production build
  • adhoc development build
  • debug production build
  • debug development build
  • release

Why all these builds?  Let’s break it down.

We have two distinct operating environments in “the cloud” (everything is in the cloud these days), one labeled as production and one labeled as development.  The production environment is carrying “live” traffic from our user community, and has specific webservice endpoints that are exposed to our iOS app.  These endpoints are distinctly different from those that are available in the development environment.  In fact, the development environment is a separate set of servers and applications, and often the development environment is running the latest development code.

Depending on the context we may want to build Lewis and Clark against the production or development environment.  Bleeding edge feature development typically happens on the (you guessed it) development servers.  Features or bug fixes that don’t require server-side changes are often tested against the existing production servers.  It is handy to be able to compile for different environments simply by flipping a switch (or in this case, scheme).

But wait, there’s more!  Any seasoned software developer knows that you should turn off or minimize excessive logging when ready to ship your application, but often times turning off logging can introduce subtle race conditions that you may have not noticed before.  Thus, even during the test phase it is beneficial to test with logging both on and off.  Or, perhaps, you are have been doing testing with the production-level app and your logs have sensitive information in them such as API keys.  You are getting ready to send the application out to your freelance testers and don’t necessarily want them to be able to access your log data.  Rather than changing any code, simply create a scheme that doesn’t include logging (we call it an adhoc scheme).

As the old saying goes, there are a number of ways to skin a cat, and there are countless ways to organize your schemes and build configurations, and you don’t have to have a one-to-one relationship between scheme and build, but this is what has worked for us.  We’re going to create an application that utilizes the same concepts and also casually toss in some techniques for accessing webservice data.

Our app is going to be called darkskyClient and it’s going to make use of the JSON API for obtaining storm data through DarkSky.  You’ll need to register for an API key at https://developer.darkskyapp.com/register, so go do that now.  Once you have your API key, go ahead and create a single-view iOS application, and also add the Lumberjack logging framework.  See our blog post on Lumberjack for step-by-step details on how to add Lumberjack logging to your project.

Here’s a power tip when working with XCode projects: if you already have a project open with files you want to add to your new project, simply create a New Group in the file explorer (left-pane) and then select, drag, and drop the files from one project into your new project, placing them in the new group you’ve created. The group is just there to make some sort of logical sense of things. Here we take the files from our initial Lumberjack project:

lumberjackExample_png

and drag-and-drop them into our new darkskyClient project (note we created a group called Lumberjack to accept the new files into):

darkskyProject_png

making sure to Copy items into destinations group’s folder and Add to targets!

add_lumberjack_to_project_png

Now, create a new header file called Logging.h and add the following contents, or drag-n-drop Logging.h from the lumberjackExample project.

#ifndef lumberjackExample_Logging_h
#define lumberjackExample_Logging_h

#import "DDLog.h"

#define ENTRY_LOG      DDLogVerbose(@"%s ENTRY ", __PRETTY_FUNCTION__);
#define EXIT_LOG       DDLogVerbose(@"%s EXIT ", __PRETTY_FUNCTION__);
#define ERROR_EXIT_LOG DDLogError(@"%s ERROR EXIT", __PRETTY_FUNCTION__);

#endif

Create a new header file called Constants.h, and then add your API key (obviously below you would have an actual key rather than useless dashes). We’ll also add a #define to the Interesting Storms API provided by DarkSky.

#ifndef darkskyClient_Constants_h
#define darkskyClient_Constants_h

#define DARKSKY_API_KEY @"--------------------------------"

#define DARKSKY_INTERESTING_STORMS @"https://api.darkskyapp.com/v1/interesting"

#endif

We’re going to use the Interesting Storms API call to the DarkSky webservice, and just log the resulting JSON. Additional tutorials will take up where we will leave off, the intent of this tutorial is to highlight schemes and build configurations.

Go to your AppDelegate.m and add the following code after importing AppDelegate.h but before the @implementation:

#import "Constants.h"
#import "Logging.h"
#import "DDTTYLogger.h"

#ifdef DEBUG
static const int ddLogLevel = LOG_LEVEL_VERBOSE;
#else
static const int ddLogLevel = LOG_LEVEL_ERROR;
#endif

and then put the following as your application:didFinishLaunchingWithOptions: body:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  [DDLog addLogger:[DDTTYLogger sharedInstance]];
  
  ENTRY_LOG;

  NSString* interestingStorms = [NSString stringWithFormat:@"%@/%@", DARKSKY_INTERESTING_STORMS, DARKSKY_API_KEY];

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSError* error;
    
    NSString* interestingStormData = [NSString stringWithContentsOfURL:[NSURL URLWithString:interestingStorms]
                                                encoding:NSUTF8StringEncoding
                                                   error:&error];
    DDLogVerbose(@"API returned:  %@", interestingStormData);
  });
  
  EXIT_LOG;
  return YES;
}

If you have never worked with blocks before (the ^{ } business inside the dispatch_async call), don’t worry, this tutorial doesn’t require you to understand them (though you will need to understand eventually).

If you now run the application you should see something like:

in your output log.

As a brief aside, notice that the ENTRY and EXIT logs were both displayed well before the API returned call, even the the code shows the DDLogVerbose statement in the middle. The secret here is in the dispatch_async function, which if you are itching to read about, visit Apple’s Grand Central Dispatch reference for now.

Now, we have an application worthy of adding some schemes and build configurations to! Let’s say we are developing our Dark Sky client application and we want to build a version that doesn’t contain any debug logging in it. How are our logs even currently displayed in the first place? Recall that at the top of our AppDelegate.m we have:

#ifdef DEBUG
static const int ddLogLevel = LOG_LEVEL_VERBOSE;
#else
static const int ddLogLevel = LOG_LEVEL_ERROR;
#endif

So, somewhere DEBUG is being defined, but where? It could be defined somewhere in a header file, but in this case it comes included as a part of the Debug build configuration that XCode generated when you created the project. Go to your darkskyClient target and under Build Settings search for DEBUG. Notice under Apple LLVM compiler 4.2 – Preprocessing there’s an entry called Debug and next to it DEBUG=1.

debug_flag_png

This is how DEBUG is being defined. But how did XCode decide to use the Debug build configuration when you ran your client? That’s determined by the scheme you are currently using in XCode.

scheme_png

If you click on the darkskyClient text in the scheme menu, you’ll probably find that it is the only scheme available. But before we add additional schemes, let’s look at the default scheme. Click on the scheme menu again (the scheme menu is the left side of the bar above ‘Scheme’, whereas the right side of the bar is used to select which device or simulator you want to run the application on) and select Edit Scheme. You should see a dialog box like this:

darksky_scheme_png

Now, it might start to make a little sense! Notice the list of actions can perform: Build, Run, Test, Profile, Analyze, Archive. And notice that under each of these tags is a build configuration: Debug or Release. Here’s what this is saying: if you are using the darkskyClient scheme, and you select the Run action, it is going to use the Debug build configuration. Period. Now, of course you can change the build configuration for the Run action. But why would you want to do that when you can manage everything easily through schemes?

What we would like is two initial schemes: one for building a debug client and one for build a non-debug client. While one can argue the term for “non-debug” should not be simply “adhoc” it turns out that we release adhoc builds to our test community with debugging explicitly turned off. So in this example you are going to see two initial build configurations:

  • adhoc
  • debug

We are also going to name the schemes “production” to differentiate from the fact that we may have two additional types of builds: adhoc development and debug development.

Let’s change the default scheme first (darskyClient) and turn it into a DebugProduction scheme. Go to scheme menu and select Manage Schemes… and click on the darkskyClient name and it should highlight so you can edit it. Change it to DebugProduction.

name_debugproduction_png

Click OK and now notice that your scheme menu has changed:

debugproduction_scheme_png

Currently our DebugProduction scheme needs no further changes, but now let’s create our AdhocProduction scheme. Go back to the scheme menu and select Manage Schemes, but this time go down to the gear wheel and select Duplicate Scheme. Name the new scheme AdhocProduction and change the Build Configuration of the Run action from Release to Debug. Click OK and then OK again, and now notice you have two schemes: DebugProduction and AdhocProduction.

Select the AdhocProduction scheme and run your application. You should notice that you no get any log output! This is exactly what should expect from the way we configured our logging:

#ifdef DEBUG
static const int ddLogLevel = LOG_LEVEL_VERBOSE;
#else
static const int ddLogLevel = LOG_LEVEL_ERROR;
#endif

as well as the way the Release build configuration is set, recall that it does not define DEBUG. Of course, let’s say in the AdhocProduction build you wanted to see at least Info logs. Simply replace the above with:

#ifdef DEBUG
static const int ddLogLevel = LOG_LEVEL_VERBOSE;
#else
static const int ddLogLevel = LOG_LEVEL_INFO;
#endif

and then rerun your AdhocProduction build. You should still not see the ENTRY and EXIT logs, but you will see the interesting storm data JSON (assuming there are interesting storms somewhere!)

Before we leave this topic and blog post altogether, let’s create another scheme called DebugDevelopment. The purpose of the DebugDevelopment scheme is to build our client against, say, a development set of resources. Let’s say for arguments sake that the DarkSky service include development servers that it allowed webclients to test against. Rather than hit https://api.darkskyapp.com/v1/interesting you should hit https://dev-api.darkskyapp.com/v1/interesting. Let’s update our Constants.h to:

#ifdef DEVELOPMENT_ENVIRONMENT
  #define DARKSKY_INTERESTING_STORMS @"https://dev-api.darkskyapp.com/v1/interesting"
#else
  #define DARKSKY_INTERESTING_STORMS @"https://api.darkskyapp.com/v1/interesting"
#endif

Now, we want to create a DebugDevelopment scheme that makes use of the development environment. In this case, go back to Manage Schemes and duplicate DebugProduction and call it DebugDevelopment. Uh-oh. We have a problem! There is only a Debug build configuration, so how do we distinguish between the two? Easy. Create a new build configuration. This feature is a little bit more hidden then the rest. Select on your project file, and then in the project viewer, select on the project file. A picture is worth a thousand words in this case.

add_build_config_png

Once you find the build configurations (there will be one called Debug and one called Release), select the plus button for configurations and duplicate the Debug configuration and name it DebugDevelopment. Now, we need to set our #define that we put in Constants.h. Go over to Build Settings tab of the project view and scroll down to the Apple LLVM compiler 4.2 – Preprocessing section, edit the DebugDevelopment defines and put DEVELOPMENT_ENVIRONMENT=1.

debugdevelopment_png

Now, that we have DebugDevelopment build configuration, go back and update your DebugDevelopment scheme to use the DebugDevelopment build configuration when running.

You might want to add something like:

DDLogVerbose(@"Using DarkSky API Endpoint:  %@", interestingStorms);

prior to the dispatch_async routine (which will dispatch the routine to call the webservice). Doing so and building the DebugDevelopment scheme you should see the following log output:

Switch over to the DebugProduction scheme and you will see:

And there you have it, a simple and effective technique for managing debug vs. adhoc builds, production vs. development environments. Note that there are other mechanisms by which one can toggle back and forth between production and development environments, perhaps include a toggle switch in your app, but then, you would want some mechanism available to compile that switch out. We’ve found through our development that maintaining separate schemes for the environments served our purposes well.

A complete example project is available in Github. Remember to obtain a DarkSky API key or you will be greeted with

when running the project!

By

Improved iOS Logging with Lumberjack

Edit: Lumberjack in it’s current form isn’t compatible with Apple’s new Swift. If you’re playing with Swift and looking for some logging alternatives in the meantime, see our latest post.

For our very first blog entry, we’re going to introduce one of our favorite tools for debugging iOS applications, and that’s a logging framework known as Lumberjack.  If you’ve used NSLog thus far to debug or provide trace statements for your applications, you are in for a treat.

We’ll walk you through adding Lumberjack to your XCode project one step at a time, but feel free to take a detour and check Lumberjack out at https://github.com/robbiehanson/CocoaLumberjack.

We’ll assume that you have little experience with XCode projects, but you should at least have XCode installed and should have familiarity with using the File menu to create a new project.  So go ahead and do that now, and create a Single View Application:

singleviewapp_png

For your project options, if you don’t have an Organization Name or Company Identifier, you can easily make something up here.  Once you are ready to start your real application, you’ll want to put some additional thought here.  Of course we’ve used our LLC name, but you can use Example Company and com.example. if you like.  For the Product Name something like lumberjackExample will suffice, and of course you will want to Use Storyboards and Use Automatic Reference Counting (unless you don’t want to use them).

project_options_png

And finally, find a parent folder where you want to create your project folder.  We’re going to put everything under blogging, and XCode will create a folder underneath called lumberjackExample.  Although you see Create local git repository for this project checked in the dialog below, we actually unchecked it because at the end of this tutorial you will be able to download the entire example project (actually you don’t have to wait until the end) from GitHub.  Until you start using GitHub to store your projects, we recommend you leave it checked.

project_folder_png

Whew, all that work and we haven’t even gotten to the good part yet.  Before we go further with XCode, let’s go ahead and download Lumberjack and add it to our project.  First, go to the GitHub for Lumberjack in your browser:  https://github.com/robbiehanson/CocoaLumberjack, and then hit the Zip button.

github_lumberjack_png

This is going to download a zipped-up version of the framework into your Downloads folder.  Click on the zipfile, let your Mac uncompress it, and you will find a folder called CocoaLumberjack-master in your Downloads directory.  There are three directories inside (well, at least there were three at the time of this writing), and only one of them is worth our interest at this point:  Lumberjack.

Now, with the Finder window open showing you the Lumberjack folder, and with your XCode project in sight next to it, drag and drop that Lumberjack folder into the XCode project, in the left-hand pane where your project files are displayed.  We prefer to create a group called 3rdParty Libraries and place everything under there, but for this example, just drag and drop under the lumberjackExample folder.  XCode is going to bring up a dialog, and its important to get these right:

add_lumberjack_png

You definitely want Copy items into destination group’s folder (if needed) selected, and you probably want Create groups for any added folders, and you definitely want to add the files to the lumberjackExample target.

Now, you’ll see that the files have been added to your project:

lumberjack_example_files_png

You can even go ahead and build the application (under the Product menu, choose Build).  Of course, it won’t do anything just yet, and we need to add a few helper files to make our life easier.

We’re going to create some nice macros to help us out while logging, so go ahead and create a new file calling Logging.h.  To create a simple header file, when presented with the bewildering options of types of files, choose iOS C and C++ files, and then Header file.  Note that when you are prompted to indicate the location to save the file, XCode puts you in the project directory, which contains your .xcodeproj file as well as another folder named after your project, in this case lumberjackExample.  If you are lazy you can save the header file right in the top-level project folder, but purists might frown, so go ahead and and double-click on the lumberjackExample folder and save it there.  Remember to name it Logging.h, and since this is a header file you do not have to add it to a target.

select_h_file_png

Now, find the file in your project explorer (the left-hand pane), and let’s add a bit of code to it:

#ifndef lumberjackExample_Logging_h
#define lumberjackExample_Logging_h

#import "DDLog.h"

#define ENTRY_LOG      DDLogVerbose(@"%s ENTRY ", __PRETTY_FUNCTION__);
#define EXIT_LOG       DDLogVerbose(@"%s EXIT ", __PRETTY_FUNCTION__);
#define ERROR_EXIT_LOG DDLogError(@"%s ERROR EXIT", __PRETTY_FUNCTION__);

#endif

Okay, that looks good, and we’ll use it in a moment, but let’s take a look at our AppDelegate.m class and configure Lumberjack. If you’ve never written an iOS application before, there is a lot to learn for sure, but for the sake of this tutorial, let’s look at only a couple of methods in the application delegate. Again, the point of this tutorial isn’t to teach you everything you need to know about iOS development, but to provide you with a tool to debug as you begin to explore.

Before we dive into the AppDelegate methods, we’re going to add a property to the application delegate which is a reference to a file-based logger we’re going to create. Before the @implementation AppDelegate line, add the following:

@interface AppDelegate()

@property (strong, nonatomic) DDFileLogger* fileLogger;

@end

If you’re not familiar with this syntax, don’t worry, it will become more clear later on.

The first method we are going to add code to is

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    return YES;
}

This is the method that is called by iOS when your application has launched. It’s a great place to bootstrap third-party frameworks (which we consider Lumberjack to be), so let’s do just that:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  // Lumberjack
  [DDLog addLogger:[DDTTYLogger sharedInstance]];
  self.fileLogger = [[DDFileLogger alloc] init];
  self.fileLogger.rollingFrequency =  60 * 60 * 24; // 1 hour rolling
  self.fileLogger.logFileManager.maximumNumberOfLogFiles = 7;
  self.fileLogger.maximumFileSize = 384 * 1024; // 384K
  [self.fileLogger setLogFormatter:[[DDLogFileFormatterDefault alloc]init]];
  [DDLog addLogger:self.fileLogger];
  return YES;
}

Now, you’re first question might be, gee, did I really need all that code for logging? And the answer is no, you didn’t, but with this code not only did you get a logger that logs to the console (that first line, [DDLog addLogger:[DDTTYLogger sharedInstance]];), but you also got one that logs to a file (which in future posts we’ll show you how to mail that file to yourself, how handy is that?), and more the file logger does all sorts of nice things like rolls once an hour, manages the maximum number of log files, the maximum file size of the logs, etc. There are oodles of options available, and we suggest you read the Lumberjack site in Github for details.

If you tried to compile the application right now (you tried that didn’t you), it will probably fail, unless you were clever enough to go ahead and add the proper import statements in your AppDelegate.m. You can either add them there, or if you plan on using logging in a lot of files, go ahead and add them to your .pch (precompiled header) file and the compiler will automatically include it in all other source code modules. Our .pch contains:

#import "DDTTYLogger.h"
#import "DDFileLogger.h"
#import "Logging.h"

Our application is still boring, and doesn’t even log anything yet! Now, let’s add some logging statements. Try replacing the method -(void)applicationDidEnterBackground:(UIApplication *)application with the following:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
  ENTRY_LOG;
  EXIT_LOG;
}

didEnterBackground_png

Oh boy, the compiler complains straight away with nonsense about use of an undeclared identifier called ddLogLevel. You would think this is all becoming more trouble than its worth, but au contraire. ddLogLevel is what allows use to have different log levels set for different modules (.m files). This is incredibly useful, particularly when you have debugged and isolated a given module and you no longer want it generating logs, even during debug mode. On a per-file basis you can set these levels. But first we need to set it altogether, so back to the top of our AppDelegate.h file, and add the following code between the @interface and @implementation blocks in AppDelegate.m:

#ifdef DEBUG
static const int ddLogLevel = LOG_LEVEL_VERBOSE;
#else
static const int ddLogLevel = LOG_LEVEL_ERROR;
#endif

This is pretty straightforward – if you are compiling a debug load, turn the log level to verbose, otherwise, turn it to error. The default log levels in Lumberjack are Verbose, Info, Warn, and Error. When Verbose is enabled you’ll get all four levels; when Info is enabled you’ll get Info, Warn, Error, and so on and so forth. See https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomLogLevels for more details on the levels, how to customize (we at iAchieved.it have never used the customization facility, the four levels has suited us just fine!).

Now, try running the application either on your phone or in the simulator. If you’ve done everything correctly thus far, you’ll still see a bunch of nothing. What kind of tutorial is this any way? Well, we only added logs to the applicationDidEnterBackground method, so to see anything, we’ll have to send the app to the background! Do so by hitting the Home button on either your device or simulator. You should have seen the XCode log output window show you some goods!

first_logs_png

Now, you’ve got a good grasp of what’s possible with Lumberjack, and remember that ENTRY_LOG and EXIT_LOG are just macros to add at the beginning and end of your methods. To really get cooking you’d add a variety of statements to your code, like:

DDLogVerbose(@"Starting up application...");
  
  // Perform some actions
  for (int i = 1; i < 5; i++) {
    DDLogInfo(@"Performing startup action %d", i);
  }
  
  DDLogVerbose(@"Startup actions complete");

Note that the DDLog statements can use positional argument placeholders like %@ and %d.

Our output (we added this to the didFinishLaunchingWithOptions: method):

Well, that’s all the time we have for this tutorial, and we’ve just scratched the surface. Our next tutorial will be how to add and manage multiple schemes and build configurations to aid in managing the various types of builds (debug vs. release), etc.

The final project code can be downloaded from GitHub at https://github.com/iachievedit/lumberjackExample.