21

I'm trying to set integer endianness using htonl() in Swift but the compiler isn't finding the htonl() method.

I've been using this as my reference: Append NSInteger to NSMutableData

Here's my code:

import Foundation
import CFNetwork
import CoreFoundation

var data = NSMutableData()
var number : UInt32 = 12
var convertedNumber : UInt32 = htonl(number)
data.appendBytes(&convertedNumber, length: 4)

I've just been running this in a playground. The error is:

Use of unresolved identifier 'htonl'
Community
  • 1
  • 1
Michael
  • 703
  • 1
  • 5
  • 11

3 Answers3

33

As of Xcode 6, Beta 3, all integer types have a bigEndian method which returns the big-endian representation of the integer and changes the byte order if necessary. Example:

var data = NSMutableData()
var number : UInt32 = 0x12345678
var convertedNumber = number.bigEndian
data.appendBytes(&convertedNumber, length: 4)
print(data)
// Output: <12345678>

Update for Swift 3:

let data = NSMutableData()
let number: UInt32 = 0x12345678
var convertedNumber = number.bigEndian
data.append(&convertedNumber, length: 4)

Or, using the new Data value type:

var data = Data()
let number: UInt32 = 0x12345678
var convertedNumber = number.bigEndian
withUnsafePointer(to: &convertedNumber) {
    data.append(UnsafeRawPointer($0).assumingMemoryBound(to: UInt8.self), count: 4)
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 3
    If you're looking for the opposite direction (network to host), you have `UInt32(bigEndian: number)`, and similar `inits` for other integer types. – Rob N Aug 28 '16 at 09:50
11

Update: Martin R's answer is the correct answer for current versions of Swift: https://stackoverflow.com/a/24653879/195691

--

You should use the CFSwapInt32HostToBig() CoreFoundation method, as htonl is likely implemented as a C macro that will not work in swift.

However, it looks like this doesn't currently work in the Swift playground. I'm getting the error Playground execution failed: error: error: Couldn't lookup symbols: __OSSwapInt32 despite the fact that the CoreFoundation documentation now includes method signatures in Swift for its byte swapping methods.

Interestingly, it looks like at least part of <arpa/inet.h> has been ported to swift, as methods like inet_ntoa() and inet_addr() are available to Swift, but the htonl family is not.

Community
  • 1
  • 1
Alex Pretzlav
  • 15,505
  • 9
  • 57
  • 55
  • Awesome, thanks! Looks like this will work. I ended up implementing htonl() in a bridging header / native-c but I'll switch to this. – Michael Jun 24 '14 at 19:49
  • Right. htonl/htons etc is a macro and hence not imported into Swift (varargs C funcs like ioctl/fcntl are another common thing missing). If you know the endianess it's easy to reimplement:func ntohs(value: CUnsignedShort) -> CUnsignedShort { // hm, htons is not a func in OSX and the macro is not mapped return (value << 8) + (value >> 8); } let htons = ntohs // same thing, swap bytes :-) – hnh Jun 27 '14 at 14:16
  • Actually `CFSwapInt32HostToBig` and friends are available now in the Swift that comes with Xcode 6.1.1 :) – Martin R Jan 19 '15 at 20:42
  • 1
    @MartinR That may be true, but `UInt32.bigEndian`, `UInt32.littleEndian`, and `UInt32.byteSwapped` seem much nicer than using `CFSwapInt32HostToBig` – Alex Pretzlav Jan 19 '15 at 20:45
  • 1
    PSA `5.bigEndian.littleEndian` does not equal 5! However, `CFSwapInt16BigToHost(CFSwapInt16HostToBig(5))` does in fact equal 5! – Lou Zell Jul 21 '16 at 23:15
4

Instead of using CFSwapInt32HostToBig() as Alex Pretzlav advised you should use

CFSwapInt32()

instead of

ntonl() and ntohl()

and

CFSwapInt16()

instead of

ntons() and ntohs()

Your code should look like

import Foundation
import CFNetwork
import CoreFoundation

var data = NSMutableData()
var number : UInt32 = 12
var convertedNumber : UInt32 = CFSwapInt32(number)
data.appendBytes(&convertedNumber, length: 4)

Also there is

CFSwapInt64()

for 8-byted integer swapping.

Edit 1

Of course you should use these functions only on processors with little endian integers cause functions I provided perform bytes swap anyway.

fnc12
  • 2,241
  • 1
  • 21
  • 27
  • I don't understand your answer. You say not to use `CFSwapInt32HostToBig`, suggesting you use `CFSwapInt32` instead, followed by " you should use these functions only on processors with little endian integers"... the entire point of `CFSwapInt32HostToBig` is precisely that it swaps, or doesn't, depending on the platform. – Alexander May 09 '20 at 21:08