swiftsocketsunsafe-pointerscfsocket

How do I handle CFSocketCallBackType.dataCallback in Swift?


All documentation and examples say that if a socket's CFSocketCallBack callout is given a .dataCallback as its second parameter (callbackType), that implies that the fourth one (data) can be cast to a CFData object containing all the data pre-read from the socket.

However, when I try to do this, it fails:

private func myCallout(socket: CFSocket?,
                       callBackType: CFSocketCallBackType,
                       address: CFData?,
                       callBackTypeMetaData: UnsafeRawPointer?,
                       info: UnsafeMutableRawPointer?) -> Void {

    // Behavior inferred from Developer
    // https://developer.apple.com/documentation/corefoundation/cfsocketcallback

    guard
        let info = info,
        let socket = socket
        else {
        return assertionFailure("Socket may have gone out of scope before response")
    }

    // <REDACTED>


    if callBackType == .dataCallBack {
        guard let data = callBackTypeMetaData?.load(as: CFData.self) else {
            return assertionFailure("Data Callback's metadata was not a CFData object")
        }

        foo(data as Data) // Crashes here
    }

    // <REDACTED>
}

The crash I get is this:

Thread 1: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0).

In the debugger, when I type this:

(lldb) po callBackTypeMetaData!.load(as: CFData.self)

it just prints a pointer address (rather high, also; only two significant 0s). However, when I type this:

(lldb) po callBackTypeMetaData!.load(as: CFData.self) as Data

It prints this:

error: Execution was interrupted, reason: EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0).
The process has been returned to the state before expression evaluation.

So it seems what I got is not really a CFData, but it's close enough to seem similar to it on the surface, until something actually tries to read it as such.

So what's going on here, where is my data, and how do I fix this?


Solution

  • data = callBackTypeMetaData!.load(as: CFData.self)
    

    reads a CFData pointer from the memory location that callBackTypeMetaData! points to. What you want is to "reinterpret" the raw pointer as a CFData (or NSData) pointer:

    if callBackType == .dataCallBack {
        if let rawPointer = callBackTypeMetaData  {
            let data = Unmanaged<NSData>.fromOpaque(rawPointer).takeUnretainedValue() as Data
            // ...
        }
    }