linuxswiftfoundationcore-foundationtoll-free-bridging

Bridging to/from CoreFoundation on Linux


I'm trying to compile some code on Linux that uses both CoreFoundation and Foundation, but Linux doesn't implement bridging in the same way macOS and iOS do.

Bridging between Objective-C and Swift works:

import Foundation
import CoreFoundation
import Glibc

func wantsNSString(_ string: NSString) {
        print(string)
}

let string = "Hello, world!"
wantsNSString(string._bridgeToObjectiveC())

But I can't figure out how to bridge to CoreFoundation. I can't just pass an NSString to a function that wants a CFString:

import Foundation
import CoreFoundation
import Glibc

func wantsCFString(_ string: CFString) {
        print(string)
}

let string = "Hello, world!"
wantsCFString(string._bridgeToObjectiveC()) //error: cannot convert value of type 'String._ObjectType' (aka 'NSString') to expected argument type 'CFString'

I can't just cast it like on macOS:

import Foundation
import CoreFoundation
import Glibc

func wantsCFString(_ string: CFString) {
        print(string)
}

let string = "Hello, world!"
wantsCFString(string._bridgeToObjectiveC() as CFString)
//error: 'String._ObjectType' (aka 'NSString') is not convertible to 'CFString'; did you mean to use 'as!' to force downcast?

Using as! like the error message suggests compiles but results in a crash at runtime (Illegal instruction), and as? produces the error:

error: conditional downcast to CoreFoundation type 'CFString' will always succeed

Bridging.swift has protocols for converting between NS and CF types, and many types have initializers and properties, but those are all internal or private. I could just use CFStringCreateWithCString, but this needs to work with other class pairs like InputStream and CFReadStream.

Am I missing something here, or is there really no way to convert between Foundation and CoreFoundation types on Linux?


Solution

  • It looks like I can unsafeBitCast NSString to/from CFString:

    import Foundation
    import CoreFoundation
    import Glibc
    
    func wantsCFString(_ string: CFString) {
            print(string)
    }
    
    let string = "Hello, world!"
    wantsCFString(unsafeBitCast(string._bridgeToObjectiveC(), to: CFString.self)
    //prints "Hello, world!"
    

    This makes sense, since CoreFoundation and Foundation types were designed to have the same memory layout -- that's why toll-free bridging works. I'm somewhat surprised that it works with the Swift implementation of Foundation though.