swiftnsdataendianness

How to byte reverse NSData output in Swift the littleEndian way?


I have this output from NSData: <00000100 84000c00 071490fe 4dfbd7e9>

So how could I byte reverse it in Swift and have this output: <00000001 0084000c 1407fe90 fb4de9d7>?


Solution

  • This should work to swap each pair of adjacent bytes in the data. The idea is to interpret the bytes as an array of UInt16 integers and use the built-in byteSwapped property.

    func swapUInt16Data(data : NSData) -> NSData {
        
        // Copy data into UInt16 array:
        let count = data.length / sizeof(UInt16)
        var array = [UInt16](count: count, repeatedValue: 0)
        data.getBytes(&array, length: count * sizeof(UInt16))
        
        // Swap each integer:
        for i in 0 ..< count {
            array[i] = array[i].byteSwapped // *** (see below)
        }
    
        // Create NSData from array:
        return NSData(bytes: &array, length: count * sizeof(UInt16))
    }
    

    If your actual intention is to convert data from an (external) big-endian representation to the host (native) byte order (which happens to be little-endian on all current iOS and OS X devices) then you should replace *** by

    array[i] = UInt16(bigEndian: array[i])
    

    Example:

    var bytes : [UInt8] = [1, 2, 3, 4, 5, 6, 7, 8]
    let data = NSData(bytes: &bytes, length: bytes.count)
    print(data)
    // <01020304 05060708>
    print(swapUInt16Data(data))
    // <02010403 06050807>
    

    Update for Swift 3: The generic withUnsafeMutableBytes() methods allows to obtain a UnsafeMutablePointer<UInt16> to the bytes and modify them directly:

    func swapUInt16Data(data : Data) -> Data {
        var mdata = data // make a mutable copy
        let count = data.count / MemoryLayout<UInt16>.size
        mdata.withUnsafeMutableBytes { (i16ptr: UnsafeMutablePointer<UInt16>) in
            for i in 0..<count {
                i16ptr[i] =  i16ptr[i].byteSwapped
            }
        }
        return mdata
    }
    

    Example:

    let data = Data(bytes: [1, 2, 3, 4, 5, 6, 7, 8])
    print(data as NSData) // <01020304 05060708>
    
    let swapped = swapUInt16Data(data: data)
    print(swapped as NSData) // <02010403 06050807>
    

    Update for Swift 5, (fixing a deprecated warning):

    func swapUInt16Data(data : Data) -> Data {
        var mdata = data // make a mutable copy
        let count = data.count / MemoryLayout<UInt16>.size
        mdata.withUnsafeMutableBytes { ptr in
            let i16ptr = ptr.assumingMemoryBound(to: UInt16.self)
            for i in 0..<count {
                i16ptr[i] =  i16ptr[i].byteSwapped
            }
        }
        return mdata
    }