Hex String to Bytes (NSData)
First note that your code does not detect invalid input data.
For example, the string "XX" is just converted to a zero byte.
Detecting invalid input with strtoul() is a bit tricky, there is an
alternative suggestion below.



let data = NSMutableData(capacity: length/2)


creates an optional NSData. If that fails then the optional chaining

data?.appendBytes(&amp;wholeByte, length: 1)


simply does nothing, i.e. the error is ignored. Better check the 
success immediately:

guard let data = NSMutableData(capacity: length/2) else { return nil }





  How can I optimize the ugly i+=1 lines?


By using stride:

func dataFromHexString() -&gt; NSData? {
    guard let chars = cStringUsingEncoding(NSUTF8StringEncoding) else { return nil}
    let length = characters.count

    guard let data = NSMutableData(capacity: length/2) else { return nil }
    var byteChars: [CChar] = [0, 0, 0]
    var wholeByte: CUnsignedLong = 0

    for i in 0.stride(to: length, by: 2) {
        byteChars[0] = chars[i]
        byteChars[1] = chars[i + 1]
        wholeByte = strtoul(byteChars, nil, 16)
        data.appendBytes(&amp;wholeByte, length: 1)
    }

    return data
}


This does not change the performance. The time to convert a 512,000
character string is 0.0140 sec on my computer (test code at the end).




  Is cStringUsingEncoding(NSUTF8StringEncoding) the correct way to get the [CChar] from the string?


That is fine as far as I can see. There is also 

self.withCString {
    // $0 is a pointer to the NUL-terminated UTF-8 string
}


which I personally prefer, but I could not detect a difference in
the performance.




  Since this is almost a direct translation from Objective-C, is there any way to make this more "Swifty," say by using map or stride, without sacrificing speed?


stride() is already used for the iteration. I do not see an use-case
for map() here.

But the performance can be improved considerably.
As observed here
and here, accessing the UTF-16 view
of a Swift string is very fast. This leads to the following implementation:

func dataFromHexString() -&gt; NSData? {

    let utf16 = self.utf16
    guard let data = NSMutableData(capacity: utf16.count/2) else { return nil }

    var byteChars: [CChar] = [0, 0, 0]
    var wholeByte: CUnsignedLong = 0
    var i = utf16.startIndex
    while i != utf16.endIndex {
        byteChars[0] = CChar(truncatingBitPattern: utf16[i])
        byteChars[1] = CChar(truncatingBitPattern: utf16[i.advancedBy(1, limit: utf16.endIndex)])
        wholeByte = strtoul(byteChars, nil, 16)
        data.appendBytes(&amp;wholeByte, length: 1)
        i = i.advancedBy(2, limit: utf16.endIndex)
    }
    return data
}


which converts the 512,000 character string in 0.00185 sec.
Note that invalid input is still not detected.

We can still make it faster by converting the UTF-16 code points
"manually" instead of using strtoul(). This is more code,
but again faster, and also detects all kinds of invalid input:

func dataFromHexString() -&gt; NSData? {

    // Convert 0 ... 9, a ... f, A ...F to their decimal value,
    // return nil for all other input characters
    func decodeNibble(u: UInt16) -&gt; UInt8? {
        switch(u) {
        case 0x30 ... 0x39:
            return UInt8(u - 0x30)
        case 0x41 ... 0x46:
            return UInt8(u - 0x41 + 10)
        case 0x61 ... 0x66:
            return UInt8(u - 0x61 + 10)
        default:
            return nil
        }
    }

    let utf16 = self.utf16
    guard let data = NSMutableData(capacity: utf16.count/2) else {
        return nil
    }

    var i = utf16.startIndex
    while i != utf16.endIndex {
        guard let
            hi = decodeNibble(utf16[i]),
            lo = decodeNibble(utf16[i.advancedBy(1, limit: utf16.endIndex)])
        else {
                return nil
        }
        var value = hi &lt;&lt; 4 + lo
        data.appendBytes(&amp;value, length: 1)
        i = i.advancedBy(2, limit: utf16.endIndex)
    }
    return data
}


The time to convert the 512,000 character string is now
0.0008 seconds. This is more than 17 times faster than the original code.



Test code:

let s1 = (0 ... 255).map { String(format:"%02x", $0) }.joinWithSeparator("")
let str = Repeat(count: 100, repeatedValue: s1).joinWithSeparator("")
print(str.characters.count) // 51200

let start = NSDate()
if let data = str.dataFromHexString() {
    let duration = NSDate().timeIntervalSinceDate(start)
    print(duration)
} else {
    print("failed")
}


The tests were done on a MacBook, with the program compiled in
Release mode.