12

I'm using Swift and trying to convert an Int (for example: -1333) to a byte array of 4 bytes. I was able to convert an Int to an array of 8 bytes (-1333 becomes [255, 255, 255, 255, 255, 255, 250, 203]), but I need it to be 4 bytes. I know that there are ways to do this in other languages like Java, but is there a way for Swift? Here's my code: (I used THIS answer)

func createByteArray(originalValue: Int)->[UInt8]{
        var result:[UInt8]=Array()

            var _number:Int = originalValue

            let mask_8Bit=0xFF

            var c=0
            var size: Int = MemoryLayout.size(ofValue: originalValue)
            for i in (0..<size).reversed(){
                //at: 0 -> insert at the beginning of the array
                result.insert(UInt8( _number&mask_8Bit),at:0)
                _number >>= 8 //shift 8 times from left to right
            }

        return result
    }
0

3 Answers 3

31

In Java an integer is always 32-bit, but in Swift it can be 32-bit or 64-bit, depending on the platform. Your code creates a byte array with the same size as that of the Int type, on a 64-bit platform that are 8 bytes.

If you want to restrict the conversion to 32-bit integers then use Int32 instead of Int, the result will then be an array of 4 bytes, independent of the platform.

An alternative conversion method is

let value: Int32 = -1333
let array = withUnsafeBytes(of: value.bigEndian, Array.init)
print(array) // [255, 255, 250, 203]

Or as a generic function for integer type of all sizes:

func byteArray<T>(from value: T) -> [UInt8] where T: FixedWidthInteger {
    withUnsafeBytes(of: value.bigEndian, Array.init)
}

Example:

print(byteArray(from: -1333))        // [255, 255, 255, 255, 255, 255, 250, 203]
print(byteArray(from: Int32(-1333))) // [255, 255, 250, 203]
print(byteArray(from: Int16(-1333))) // [250, 203]
Sign up to request clarification or add additional context in comments.

Comments

1

It's hard for beginners to understand what is going on in the answers above

public extension FixedWidthInteger {
    var bytes: [UInt8] {
        withUnsafeBytes(of: bigEndian, Array.init)
    }
}

First of all lets rewrite computed property bytes more detailed

var bytes: [UInt8] {
    var copyOfSelf = self.bigEndian // this defines the order of bytes in result array
    // for int '1' it can be [0, 0, 0, 0, 0, 0, 0, 1] 
    // or [1, 0, 0, 0, 0, 0, 0, 0] 
    return withUnsafeBytes(of: copyOfSelf) { urbp: UnsafeRawBufferPointer in
        // Array has a constructor 
        // init<S>(_ s: S) where Element == S.Element, S : Sequence
        // so 'UnsafeRawBufferPointer' confirms 'Sequence' protocol
        // and 'urbp' can be passed as a parameter to Array constructor
        return Array(urbp) 
    }
}

And here some tests to explain the result

final class FixedWidthIntegerTests: XCTestCase {
    func testBytes() {
        XCTAssertEqual([0,0,0,0,0,0,0,1], Int(1).bytes)
        XCTAssertEqual([255,255,255,255,255,255,255,255], UInt.max.bytes)
        XCTAssertEqual([127,255,255,255,255,255,255,255], Int.max.bytes)
        XCTAssertEqual([1], Int8(1).bytes)
        XCTAssertEqual([0,1], Int16(1).bytes)
        XCTAssertEqual([0,0,0,1], Int32(1).bytes)
    }
}

Comments

0

However, not like Java using big endian, iOS platform uses 'little endian' instead. So if it would be

let value: Int32 = -1333
let array2 = withUnsafeBytes(of: value.littleEndian, Array.init)// [203, 250, 255, 255]

You can verify it by wrapping the value into data with below extension and check the bytes

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

Just a reminder for those iOS developers who are digging into memory layout.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.