Audrey
Audrey

Reputation: 205

How to convert Int to byte array of 4 bytes in Swift?

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
    }

Upvotes: 12

Views: 14462

Answers (3)

Ihar Katkavets
Ihar Katkavets

Reputation: 1570

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)
    }
}

Upvotes: 1

Cindy Chen
Cindy Chen

Reputation: 1

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.

Upvotes: 0

Martin R
Martin R

Reputation: 539685

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]

Upvotes: 31

Related Questions