Active Filters: Tools

Topics

#90: Custom Xcode Project Templates 🔨

Topics

Picking up where we left off in Bite #89: Today we'll look at adding our own custom project templates to Xcode. When Xcode 6 debuted at WWDC 14, one of the things that had seemingly gone missing was the "Empty Application" project template. This template was great because it made no assumptions about what kind of app or project we were creating. Let's add it back as a custom project template.

Much like we did when creating file templates, we'll begin by creating a folder for our custom templates to live in. This will be: ~/Library/Developer/Xcode/Templates/Project Templates/Custom. We'll create a new folder in here called Empty Application.xctemplate.

Then we'll need to create a TemplateInfo.plist to describe our template.

Most of this is boilerplate, except that last bit in TemplateInfo.plist, the Definitions dictionary. This where we'll specify the content of our base AppDelegate file. The full source of the Definitions block of the plist file is shown below. Note how the key informs where to inject the code, and value contains the code itself.

<key>Definitions</key>
<dict>
<key>AppDelegate.swift:implementation:methods:applicationdidFinishLaunchingWithOptions:body</key>
  <string>self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
    // Override point for customization after application launch.
    self.window!.backgroundColor = UIColor.whiteColor()</string>

<key>AppDelegate.swift:implementation:methods:applicationdidFinishLaunchingWithOptions:return</key>
  <string>self.window!.makeKeyAndVisible()
    return true
  </string>
</dict>

Just like before we'll an icon for our new template. We'll add a TemplateIcon.tiff to our template folder. Now we can press ⌘N in Xcode to try out our new template.

Note: Keep in mind when using this template, that the UIWindow will need a root view controller set on it before our app can do anything useful.

The "Empty Application" project template we created in this bite can be downloaded here.

Topics

#89: Custom Xcode File Templates 🔨

Topics

Xcode ships with some great starter templates for projects, files, and even targets. Today we'll look at how to add our own custom file templates to Xcode's built-in set. We'll be adding a new template for easily creating new table view controllers powered by Static (a static table view library we covered in Bite #74).

We'll begin by creating a folder for our custom templates to live in. This will be: ~/Library/Developer/Xcode/Templates/File Templates/Custom. We'll create a new folder in here called Static Table View Controller.xctemplate.

Then we'll need to create two files: One called TemplateInfo.plist to describe our new template, and another where our actual template content will live.

Let's start with TemplateInfo.plist. We'll create this using Xcode by selecting File > New > File... and then Resources > Property List. We'll call it TemplateInfo.plist (the name is important) and give it this content:

Then we'll select File > New > File... and select Source > Swift File. We'll name it __FILEBASENAME__.swift to match the value in our TemplateInfo.plist.

The template's content almost looks like normal Swift code, but contains template tags (which will get replaced and "filled in" at run time):

//  Created by ___FULLUSERNAME___ on ___DATE___. ___COPYRIGHT___

import UIKit
import Static

class ___FILEBASENAMEASIDENTIFIER___: TableViewController {
  override func viewDidLoad() {
    super.viewDidLoad()

    dataSource.sections = [ Section(rows: [ Row(text: "Hello World") ]) ]
  }
}

Lastly, we'll add an icon for our new template by adding a TemplateIcon.png our template folder.

We can now press ⌘N in Xcode to see our new template. Success!

The example template we made here is available for download here.

Update: We cover custom project templates in Bite #90.

Topics

#68: Time Profiler Basics 🕑📈

Topics

Measuring the performance of an app is the first step in optimizing it. Lucky for us, Apple's developer tools have plenty of profiling features. Let's take a look at one, Time Profiler. It's claim to fame is helping us fine tune performance by tracking down slow functions.

We start by opening our project in Xcode, and selecting Product > Profile from the menu.

   

This will build our app, and launch Instruments. When it does, we'll select the Time Profiler Instrument and click Profile. Now the fun part: We click 🔴 in the top left and Instruments will launch our app, and begin measuring its performance. One common way to use Time Profiler is to discover any under-performing functions that might be bogging down our main thread.

Remember, a bored main thread with nothing do is a happy one. Once our app is running, we'll turn on all the Call Tree options. This will clean up the data that's collected making it easier to sift through. We can flip these on and off after profiling to learn about different parts of the code being executed. From here we simply interact with our app, and after a few seconds we'll see data starting to appear. We expand the Main Thread, and see that a function called insertNewObject is at the top of the list, meaning of all the functions that were called, this one caused our main thread to block the most. We'll double click on it to see the exact spot in our code calling the sluggish function, ready for us to optimize:

Topics

#65: Xcode Scheme Tips 🔨

Topics

There's a ton of power tucked away inside of Xcode Schemes. Let's take a look at just a few of the helpful things we can do by clicking on our current Scheme and selecting Edit Scheme...

Default Simulated Locations

We can configure a default simulated location when our app is run by selecting Run in the left column and then Options.

Test Multiple States

Don't be afraid to go Scheme-crazy. Why not make one that runs with an application data bundle of a logged in user, and one of a logged out user? Now we can easily switch between developing in the two states.

Walking Dead Objects

Selecting Run in the left column, then Diagnostics to enable Zombie Objects. This will help you track down those pesky EXC_BAD_ACCESS crashes by holding on to deallocated objects (ding) and logging a message when something tries to interact with them.

Add Carthage Support

Carthage (covered in Bite #11) support is easy to add. We just need to make sure we share the Scheme that builds our framework.

Easier Profiling

We can Duplicate our original Scheme again and choose an Instrument under Profile > Instrument. Now we can easily launch straight into a particular kind of Profiling with a single click.

Test Background Fetch

We can easily test our app when launched from a background fetch by editing our original Scheme then clicking Duplicate Scheme. On this new Scheme, we'll enable Launch due to a Background Fetch under Run > Options.

Topics

#60: Creating a CocoaPod 🍩

Topics

CocoaPods are an extremely popular way to publish and use third-party libraries on iOS and OS X. Let's create and publish one of our own. Our Pod will provide shortcuts to commonly used directories, like the user's documents directory as NSURLs. Let's get started.

We start by generating our new Pod like this:

➜ pod lib create common-paths

We'll be asked a few questions such as what language we want to use (Objective-C/Swift), whether we'd like to create a demo application, etc. then a complete Xcode workspace and directory structure will be created.

We'll open up the newly created workspace and get started by filling out all the metadata (summary, author, etc.) about our new Pod inside the .podspec file.

CocoaPods generated a blank .swift showing us where to put our code. We do as we're told, and replace it with a new file called NSURL+CommonPaths.swift.

// NSURL+CommonPaths.swift

import Foundation

extension NSURL {
  static var documentsDirectoryURL: NSURL? {
    guard let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true).first else { return nil }
    return NSURL.fileURLWithPath(path)
  }
}

Neat, now let's publish it! We create a new Github repo matching the source we entered in our .podspec. Then we commit our changes, tag them, and push them up:

➜ git add .
➜ git commit -am 'Initial commit.'
➜ git tag 0.1.0
➜ git push origin master --tags

Finally, we'll register via our email and follow the instructions, then release to the world! 🎉

➜ pod trunk register jake@deallocatedobjects.com 'Jake Marsh'
➜ pod trunk push

Here's our new common-paths Pod up on the CocoaPods website: common-paths.

Topics

#56: ResponseDetective 🔍

Topics

ResponseDetective is a new debugging library from Adrian Kashivskyy and the team at Netguru for "intercepting" HTTP activity and logging it. You can configure different sets of Interceptor classes, depending on what kinds of data you'd like to see. Let's set it up:

The first thing we'll need to do is register some request and response Interceptors. ResponseDetective comes with quite a few Interceptors out of the box, and you can of course create your own. Here we'll log all the headers of all requests and responses, as well as the content of any JSON requests.

We start with the default session config, and insert the InterceptingProtocol at the front of it's protocol classes.

Finally, we create a session with our config and kick off a new task to an example API endpoint.

InterceptingProtocol.registerRequestInterceptor(HeadersInterceptor())
InterceptingProtocol.registerResponseInterceptor(HeadersInterceptor())
InterceptingProtocol.registerErrorInterceptor(HeadersInterceptor())

InterceptingProtocol.registerResponseInterceptor(JSONInterceptor())

let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()

configuration.protocolClasses = map(configuration.protocolClasses, {
  (var protocolClasses) in
    protocolClasses.insert(InterceptingProtocol.self, atIndex: 0)
    return protocolClasses
}) ?? [InterceptingProtocol.self]

let session = NSURLSession(configuration: configuration)

session.dataTaskWithRequest(
    NSURLRequest(URL: NSURL(string: "http://httpbin.org/get")!)
).resume()

Now, if we look at our console, we can see that all the HTTP activity has been logged:

GET http://httpbin.org/get

200 no error
Content-Length: 304
Server: nginx
Content-Type: application/json
Access-Control-Allow-Origin: *
Date: Mon, 10 Aug 2015 04:11:45 GMT
Access-Control-Allow-Credentials: true
Connection: keep-alive
{
  "args" : {

  },
  "headers" : {
    "User-Agent" : "056-responsedetective\/1 CFNetwork\/711.4.6 Darwin\/14.4.0",
    "Accept-Encoding" : "gzip, deflate",
    "Host" : "httpbin.org",
    "Accept-Language" : "en-us",
    "Accept" : "*\/*"
  },
  "origin" : "76.102.27.127",
  "url" : "http:\/\/httpbin.org\/get"
}

More info about ResponseDetective can be found at git.io/responsedetective

Topics

#52: R.swift 🎨

Topics

R.swift is a tool by Mathijs Kadijk that takes the resources in your app (images, segues, storyboards, nibs, etc.), examines them and generates a file in the root of your project called R.generated.swift containing a simple struct that maps your resources' actual names to Swift properties. R.swift updates the file each time you build, so you can focus on using your resources instead of managing them.

Why do this? For starters, it gives us Xcode native autocompleteion of resources names (finally). We're also saved from hardcoding strings throughout our app, or needing to go update a set of constants somewhere everytime we add a resource.

Let's look at some examples of where R.swift comes in handy. First up, images:

if spaceship.model == SpaceshipModel.Xwing {
  cell?.imageView!.image = R.image.shipXwing
} else {
  cell?.imageView!.image = R.image.shipGeneric
}

Nibs are also much easier to work with, here we instantiate the top view in a nib:

let view = R.nib.controlPanelView.instantiateWithOwner(nil, options: nil)

Instantiate view controllers from Storyboards with ease:

let spaceshipVC = R.storyboard.main.spaceshipViewController

R.swift also makes it easy to work with our cell reuse identifiers. It even includes extensions for UITableView and UICollectionView that save you from casting your cell's type:

tableView.dequeueReusableCellWithIdentifier(
  R.reuseIdentifier.shipTableViewCell, 
  forIndexPath: indexPath
)

More info about R.swift can be found at git.io/rswift

Topics

#30: UI Testing 🚸

Topics

Testing is incredibly important in ensuring our app arrives to our users in as good a state as possible.

Xcode has had great unit testing support for a few releases now, but testing our UI has always been a challenge.

Instruments added support for it a while back, but it’s never felt like a first class citizen... Until now!

Xcode 7 brings us an awesome, fully baked set of UI testing tools. We can even make Xcode write much of your test code for us by simply interacting with our app.

Here's an example test created using the default Master-Detail template in Xcode. It presses the add button 3 times, then verifies that 3 cells now exist in the table view:

func testExample() {
    let app = XCUIApplication()

    let masterNavigationBar = app.navigationBars["Master"]
    let addButton = masterNavigationBar.buttons["Add"]

    addButton.tap()
    addButton.tap()
    addButton.tap()

    XCTAssert(app.tables.childrenMatchingType(.Cell).count == 3)
}

Recording UI Tests couldn't be easier. We just write an new empty test function, put our cursor in the middle of it, and press record.

UI Testing uses Accessibility under the hood to identify and retrieve UI elements at runtime. Just one more reason to make our apps as accessible as possible!

Topics

#15: FontBlaster 📄

Topics

FontBlaster is probably one of the simplest things we'll cover in these bites.

Don't be fooled though, it is one of the most useful little utilities and has quickly made its way into my "standard set" of third-party I use in all my new projects.

Adding a custom font to an iOS app is normally just a little cumbersome:

😁 First drag the font file(s) into Xcode

😐 Open up your Info.plist

😒 Add a new key for "Fonts provided by application"

😲 Find the exact names of each font that iOS’s font system wants to hear, adding items in the .plist for each

😭 Watch as loading them all slows down your app’s initial launch

With FontBlaster, all you have to do is make sure your custom font files are added to your app's main bundle then call this method anywhere in your app:

FontBlaster.blast()

More info about FontBlaster can be found at git.io/fontblaster

Authors Note

Hey everyone, first off the response to little bites has been absolutely incredible. I'd like to thank every single one of you for reading, responding, retweeting, requesting, etc. as I get things off the ground here.

Second, I'd like to let everyone know that there will be no bites published next week as I'll be busy attending Apple's Worldwide Developer Conference learning what is sure to be tons of exciting new things that I’ll get to cover here in the coming months.

If you're going to be at the conference as well, reach out on Twitter, I'd love to meet and talk to as many of you all as I can! 👍

Bites will resume their regular schedule starting Monday June, 15th.

Cheers! 🍫
- Jake

Topics

#11: Carthage 🗿

Topics

Carthage is a new way to integrate third-party libraries in your apps. One way to think of it is as a simpler alternative to CocoaPods. It’s a dependency manager distilled down to just about it’s simplest possible form.

One of the notable differences between Carthage and CocoaPods is that Carthage doesn’t require an Xcode Workspace to be created or "automagically" managed. It can be installed via a downloadable installer or with a simple brew install carthage.

Create a Cartfile, which is just a plain text file filled with lines like the ones below. Each of these tell Carthage about a library you’d like to use in your app.

github 'Alamofire/Alamofire'
github 'Hearst-DD/ObjectMapper'
github 'Haneke/HanekeSwift'

Then, simply run carthage update. At which point Carthage will go out and download and build all the libraries you asked for.

Note: Similar to CocoaPods’ pod install, you’ll also need to run carthage update anytime you change or update your Cartfile.

Where things begin to feel different from CocoaPods is that the next step is to manually drag and drop the frameworks Carthage built when you ran carthage update into the Linked Frameworks and Libraries of your Xcode target’s General settings panel.

Finally, import the libraries in your code:

import Alamofire
import ObjectMapper
import Haneke

More info about Carthage can be found at git.io/carthage

Page 5 of 6