8

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?

NobodyNada
  • 7,002
  • 6
  • 39
  • 48
  • Does this work? http://stackoverflow.com/a/24900185/603977 Ugly double cast. Don't know about Linux, though. The big "gotcha" with Swift on Linux is that the ObjC runtime isn't there. – jscs Oct 03 '16 at 19:10
  • @JoshCaswell No; bridging between Swift and Foundation [isn't done automatically by the compiler yet](https://github.com/apple/swift-corelibs-foundation/blob/master/Docs/Issues.md), which is why I have to use the temporary `_bridgeToObjectiveC` method, and I tried casting from `NSString` to `CFString` in my third example. – NobodyNada Oct 03 '16 at 19:16

1 Answers1

1

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.

NobodyNada
  • 7,002
  • 6
  • 39
  • 48