There's some good answers here, which is really nice to see ^^
However if you'd like to avoid interacting with the C-interopability API of Swift, then I recommend to take a look at my example. It's also just as generic for all the datatype sizes. Note that MemoryLayout is only being used a sanity check.
Code:
public extension UnsignedInteger {
init(_ bytes: [UInt8]) {
precondition(bytes.count <= MemoryLayout<Self>.size)
var value: UInt64 = 0
for byte in bytes {
value <<= 8
value |= UInt64(byte)
}
self.init(value)
}
}
Example usage:
let someBytes = [UInt8](repeating: 0x42, count: 2)
let someValue = UInt16(someBytes)
For little endian support, you need for byte in bytes.reversed() instead.
Explanation:
<<= is the bitwise left shift assignment operator:
It shifts the left hand operand (usually a numerical value type) by N bits defined by the right hand operand, for example:
0b00000001 << 7 == 0b10000000
|= is the bitwise or assignment operator:
It applies a bitwise or on the left and right hand operands, for example:
0b00000001 | 0b10000000 == 0b10000001
So when you have an array of 2 unsinged bytes and want to convert it a unsinged short you can simply;
let bytes = [UInt8](repeating: UInt8(255), count: 2)
var short: UInt16 = 0
// "add" our first unsinged byte
short |= UInt16(bytes[0])
// our short now looks like this in memory: 0b0000000011111111
// make room for the unsinged byte ;)
short <<= 8
// our short now looks like this in memory: 0b1111111100000000
// "add" our last unsinged byte
short |= UInt16(bytes[1])
// our short now looks like this in memory: 0b1111111111111111
print(short == UInt16.max)