How to convert an Int into NSData in Swift?

33,255

Solution 1

With Swift 3.x to 5.0:

var myInt = 77
var myIntData = Data(bytes: &myInt, 
                     count: MemoryLayout.size(ofValue: myInt))

Solution 2

In contemporary versions of Swift, I would do:

let score = 1000
let data = withUnsafeBytes(of: score) { Data($0) }
e8 03 00 00 00 00 00 00 

And convert that Data back to an Int:

let value = data.withUnsafeBytes {
    $0.load(as: Int.self)
}

Note, when dealing with binary representations of numbers, especially when exchanging with some remote service/device, you might want to make the endianness explicit, e.g.

let data = withUnsafeBytes(of: score.littleEndian) { Data($0) }
 e8 03 00 00 00 00 00 00 

And convert that Data back to an Int:

let value = data.withUnsafeBytes {
    $0.load(as: Int.self).littleEndian
}

Versus big endian format, also known as “network byte order”:

let data = withUnsafeBytes(of: score.bigEndian) { Data($0) }
 00 00 00 00 00 00 03 e8

And convert that Data back to an Int:

let value = data.withUnsafeBytes {
    $0.load(as: Int.self).bigEndian
}

Needless to say, if you don’t want to worry about endianness, you could use some established standard like JSON (or even XML).


For Swift 2 rendition, see previous revision of this answer.

Solution 3

For any integer type:

extension FixedWidthInteger {
    var data: Data {
        let data = withUnsafeBytes(of: self) { Data($0) }
        return data
    }
}

Example:

let data = 1.data

Solution 4

You can convert in this way:

var myScore: NSInteger = 0
let data = NSData(bytes: &myScore, length: sizeof(NSInteger))

Solution 5

Swift 5, add an other option.

NSData is old, still effective

Write Data:

let buffer = NSMutableData()
let size = MemoryLayout<UInt>.size
let big = 1000
let small = 10
withUnsafeBytes(of: big, { (p) in
      let bufferPointer = p.bindMemory(to: UInt.self)
      if let address = bufferPointer.baseAddress{
             buffer.append(address, length: size)
      }
})

withUnsafeBytes(of: small, { (p) in
      let bufferPointer = p.bindMemory(to: UInt.self)
      if let address = bufferPointer.baseAddress{
           buffer.append(address, length: size)
      }
 })

Read data:

if let d = buffer.copy() as? Data{
     var big: UInt = 0
     var small: UInt = 0
     let size = MemoryLayout<UInt>.size
     let meta = NSData(data: data)
     meta.getBytes(&big, range: NSRange(location: 0, length: size))
     meta.getBytes(&small, range: NSRange(location: size, length: size))

     print("big:", big, "\nsmall:", small)
    //  big: 1000 
    //  small: 10 
}

You know the memory layout, the data put in the memory,

Then put them out exactly.

unsafe method is funny

Share:
33,255
Cesare
Author by

Cesare

Updated on July 17, 2022

Comments

  • Cesare
    Cesare almost 2 years

    In Objective-C I use the following code to

    1. Convert an Int variable into NSData, a packet of bytes.

      int myScore = 0;
      NSData *packet = [NSData dataWithBytes:&myScore length:sizeof(myScore)];
      
    2. Use the converted NSData variable into a method.

      [match sendDataToAllPlayers: 
      packet withDataMode: GKMatchSendDataUnreliable 
      error: &error];
      

    I tried converting the Objective-C code into Swift:

    var myScore : Int = 0
    
    func sendDataToAllPlayers(packet: Int!,
                withDataMode mode: GKMatchSendDataMode,
                error: NSErrorPointer) -> Bool {
    
                return true
    }
    

    However, I am not able to convert an Int variable into an NSData and use it an a method. How can I do that?

  • Cesare
    Cesare about 9 years
    Thank you very much! May you please tell me how to include data into my method? I get data is not a type when I code it this way: func sendDataToAllPlayers(data,
  • David V
    David V about 9 years
    You are welcome :) Sorry, I don't catch your question, what you need to do?
  • Cesare
    Cesare about 9 years
    If you look at the second bullet point of my question there is a code of a function sendDataToAllPlayers Now, I translated that function already but I would like to include the constant data you have declared in your answer into my Swift method. Sorry for the misunderstanding!
  • David V
    David V about 9 years
    It seems that Rob's answer is what you are looking, is it true? of I'm missing something?
  • Drux
    Drux about 9 years
    IMO such data won't be portable (should not be transferred without additional metadata) between devices with different byte orders (see here).
  • Rob
    Rob about 9 years
    I actually agree. I just modeled my answer after the sending data to other players discussion in the Game Center Programming Guide, but it does seem more prudent to use archive or plist or some other more robust format.
  • Benjohn
    Benjohn about 7 years
    You can use score.bigEndian or score.littleEndian to force the byte order to be of a specific endianness. The data will then be compatible between architectures able to be written on one and read on another.
  • Benjohn
    Benjohn about 7 years
    I'd suggest bigEndian, as this is generally considered to be "network byte order". And is also, obviously, the correct way around ;-)
  • bojan
    bojan almost 4 years
    Data([UInt8(value)]) will only let you use numbers > 0 and < 256...
  • Brody Robertson
    Brody Robertson almost 4 years
    No doubt, that's why I qualified the answer.