Getting Data Out of Nsdata with Swift

Getting data out of NSData with Swift

You may want to check out RawData which is really new and this guy just experimented a bit with this idea, so don't think that it's tested well or anything, some function aren't even implemented yet. It's basically a Swift-y wrapper around (you guessed it) raw data, a series of bytes.

Using this extension, you can initialise it with an NSData instance:

extension RawData {
convenience init(data: NSData) {
self.init(UnsafeMutableBufferPointer(start: UnsafeMutablePointer(data.bytes), count: data.length))
}
}

You'de be calling it like this:

let data = "Hello, data!".dataUsingEncoding(NSASCIIStringEncoding)!

let rawData = RawData(data: data)

EDIT: To answer your questions:

The thing is that data can be large, very large. You generally don't want to copy large stuff, as space is valuable. The difference between an array of [UInt8] values and an NSData instance is that the array gets copied every time, you give it to a function -> new copy, you do an assignment -> new copy. That's not very desirable with large data.

1) If you want the most native, safe way, without any 3rd party libraries as the one mentioned, you can do this:

let data = UnsafeMutableBufferPointer(start: UnsafeMutablePointer(data.bytes), count: data.length)

(I know it doesn't sound very safe, but believe me it is). You can use this almost like an ordinary array:

let data = "Hello, data!".dataUsingEncoding(NSASCIIStringEncoding)!

let bytes = UnsafeMutableBufferPointer(start: UnsafeMutablePointer<UInt8>(data.bytes), count: data.length)

for byte in bytes {}
bytes.indexOf(0)
bytes.maxElement()

and it doesn't copy the data as you pass it along.

2) UnsafeMutablePointer<Void> is indeed very C-like, in this context it represents the starting value (also called base) in a sequence of pointers. The Void type comes from C as well, it means that the pointer doesn't know what kind of value it's storing. You can cast all kinds of pointers to the type you're expecting like this: UnsafeMutablePointer<Int>(yourVoidPointer) (This shouldn't crash). As mentioned before, you can use UnsafeMutableBufferPointer to use it as a collection of your type. UnsafeMutableBufferPointer is just a wrapper around your base pointer and the length (this explains the initialiser I used).

Your method of decoding the data directly into your struct does indeed work, the properties of a struct are in the right order, even after compile time, and the size of a struct is exactly the sum of it's stored properties. For simple data like yours that's totally fine. There is an alternative: To use the NSCoding protocol. Advantage: safer. Disadvantage: You have to subclass NSObject. I think you should be sticking to the way you do it now. One thing I would change though is to put the decoding of your struct inside the struct itself and use sizeof. Have it like this:

struct TreeDescription {
var id:UInt32 = 0x00000000
var channel:UInt8 = 0x00
var rssi:UInt8 = 0x00

init(data: NSData) {
data.getBytes(&self, length: sizeof(TreeDescription))
}
}

Another EDIT: You can always get the underlying data from an Unsafe(Mutable)Pointer<T> with the method memory whose return type is T. If you need to you can always shift pointers (to get the next value e.g.) by just adding/subtracting Ints to it.

EDIT answering your comment: You use the & to pass an inout variable, which can then be modified within the function. Because an inout variable is basically the same as passing the pointer, the Swift devs decided to make it possible to pass &value for an argument that is expecting an UnsafeMutablePointer. Demonstration:

func inoutArray(inout array: [Int]) {}

func pointerArray(array: UnsafeMutablePointer<Int>) {}

var array = [1, 2, 3]

inoutArray(&array)
pointerArray(&array)

This also works for structs (and maybe some other things)

Working with NSData in swift

If you want to modify the bytes retrieved from the NSData
object, then you should copy the bytes into a separate array

var bytes = [UInt8](count: msgData.length, repeatedValue: 0)
msgData.getBytes(&bytes, length: bytes.count)

bytes[11] = UInt8(UInt32(bytes[11]) | 0x0FF)

NSData is an immutable object, and

let ptr = UnsafePointer<UInt8>(msgData.bytes)

is a constant pointer, so you must not modify the pointed-to data.

Alternatively, use a mutable data object from the beginning:

var msgData = NSMutableData(bytes: testBytes, length: testBytes.count)

let ptr = UnsafeMutablePointer<UInt8>(msgData.mutableBytes)
var bytes = UnsafeMutableBufferPointer<UInt8>(start: ptr, count: msgData.length)

bytes[11] = UInt8(UInt32(bytes[11]) | 0x0FF)

Note the usage of msgData.mutableBytes instead of msgData.bytes.
This will modify the data in msgData directly.

Converting NSData to Integer in Swift

Like this:

var src: NSInteger = 2525
var out: NSInteger = 0

let data = NSData(bytes: &src, length: sizeof(NSInteger))
data.getBytes(&out, length: sizeof(NSInteger))
println(out) // ==> 2525

Converted NSData to Data in Swift, and now when I try to cast bytes to [UInt] I get a crash

The reason you are crashing is this expression:

bytes : UnsafePointer<[UInt]>

You are assuming that the data represents a series of UInt. So a pointer to the start of the data is not as unsafe pointer to a [UInt], an array of UInt; it is an unsafe pointer to a UInt, i.e. the first in the series. You should be saying:

bytes : UnsafePointer<UInt>

So much for the crash. Now let's talk about the thing you are mostly trying to do here.

I'm uncertain what the string format is supposed to do, but I do grasp that the idea of ntohl is to guarantee the endianity of some C long ints (32 bits). So I'll omit the string format part and just talk about how you would take a stream of C long int received into a Data and reverse the endianity of the long ints.

Suppose d is a mutable Data (i.e. declared with var). Then, assuming it represents a sequence of UInt32 little-endian values and you want to convert those to big-endian, you would say:

let ct = d.count/4
d.withUnsafeMutableBytes{
(ptr:UnsafeMutablePointer<UInt32>) in
for ix in 0..<ct {
ptr[ix] = ptr[ix].bigEndian
}
}

NSData to String in Swift Issues

let testBytes : [UInt8] = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64]

func bytes2String(array:[UInt8]) -> String {
return String(data: NSData(bytes: array, length: array.count), encoding: NSUTF8StringEncoding) ?? ""
}

Xcode 8.2 • Swift 3.0.2

func bytes2String(_ array: [UInt8]) -> String {
return String(data: Data(bytes: array, count: array.count), encoding: .utf8) ?? ""
}

Testing:

bytes2String(testBytes)  // "Hello World"

Swift. How to write bytes from NSData into another NSData?

You can extract the data using subdataWithRange():

let firstData1 = mutableData.subdataWithRange(NSMakeRange(sizeof(Int), length))
if let firstString1 = NSString(data: firstData1, encoding: NSUTF8StringEncoding) as? String {
println(firstString1)
} else {
// bad encoding
}

Your solution

var data = NSData()
mutableData.getBytes(&data, range: NSMakeRange(sizeof(Int), length))

does not work and crashes because NSData is a reference type and
data a pointer to the object. You are overwriting this pointer
and the following bytes in memory.



Related Topics



Leave a reply



Submit