Convert CFString to NSString - Swift

18,010

Solution 1

Swift.String and NSString are toll free bridged.

NSString and CFString can be cast to one another, but you can't directly cast from a Swift String to a CFString or vice versa.

Follow these steps to cast from a Core Foundation String to a Swift String:    

var cfStr:CFString = "Soon, I'll be a Swift String"
var nsTypeString = cfStr as NSString
var swiftString:String = nsTypeString

Example for CFTypeRef:

var cfStr:CFTypeRef = "Soon, I'll be a Swift String"
var nsTypeString = cfStr as NSString
var swiftString:String = nsTypeString

Solution 2

At least as of Swift 2.0 (verify from a terminal with swift --version), you can convert a CFString to a native Swift String with a simple as String.

This is sufficient, since Swift's String type can be used anywhere NSString is expected.

An example with a kUTType* constant (kUTType* constants are defined by CoreServices and are CFStrings):

// Get UTF8 plain text from the pasteboard.
import AppKit
let str = NSPasteboard.generalPasteboard().stringForType(kUTTypeUTF8PlainText as String)

A more detailed example:

// Import at least the Foundation framework.
// Since Cocoa includes Foundation, `import Cocoa` works too.
// (Note that `import CoreServices`, even though it defines type `CFString`,
// is NOT enough - the conversion will fail.)
import Foundation

// Create a CFString.
// The fact that initializing from a `String` literal here works implies that
// the *reverse* cast - String -> CFString - also works.
var cfStr:CFString = "Cast me."

// Convert it to String.
var swiftStr = cfStr as String

To test what type you're dealing with:

cfStr is CFString // true
swiftStr is String // true

To get the string's type, use .dynamicType; in a string context, this reports the type name but note that you may get the name of a private internal class back:

"cfStr is a \(cfStr.dynamicType) instance."
// -> "cfStr is a _NSContiguousString instance." !!

Still, you can treat this as a CFString, as the is test above shows.

Use _stdlib_getDemangledTypeName() to get the true, underlying class name:

_stdlib_getDemangledTypeName(cfStr) // -> "ObjectC.CFString"
_stdlib_getDemangledTypeName(kUTTypeUTF8PlainText) // ditto

Solution 3

As for me, I like to make extentions and use them afterwards. It seems convenient and clear to read:

Extention:

extension CFString {
    var string: String {
        return self as String
    }
}

Usage:

let specificQueryPart: [String: NSObject] = [
            kSecReturnData.string: true as NSObject,
            kSecAttrService.string: key as NSObject,
            kSecMatchLimit.string: kSecMatchLimitOne
        ]
Share:
18,010

Related videos on Youtube

user3185748
Author by

user3185748

Updated on June 14, 2022

Comments

  • user3185748
    user3185748 about 2 years

    I'm trying to write a program that will scan for available serial ports and present them in a popup menu. Why can I not take the CFString straight from the IORegistryEntryCreateCFProperty() function and add it to the menu via string interpolation in the next line? For some reason my variable declaration is met with the error:

    "NSString is not a subtype of CFString".

    import Foundation
    
    import Cocoa
    
    import IOKit
    import IOKit.serial
    
    
    @objc class Serial {
    
        init() {
        }
    
        @IBOutlet var serialListPullDown : NSPopUpButton!
    
        func refreshSerialList(defaultprompt: String) {
    
    
            let masterPort: mach_port_t = kIOMasterPortDefault
            let classesToMatch: CFDictionary =     IOServiceMatching(kIOSerialBSDServiceValue).takeUnretainedValue()
            var matchingServices: io_iterator_t = 0
    
            // remove everything from the pull down list
            serialListPullDown?.removeAllItems()
    
            // ask for all the serial ports
            let kernResult = IOServiceGetMatchingServices(masterPort, classesToMatch, &matchingServices)
            if kernResult == KERN_SUCCESS {
                // success
                while (io_object_t() == IOIteratorNext(matchingServices)) {
                    var serialport = IORegistryEntryCreateCFProperty(io_object_t(), kIOCalloutDeviceKey, kCFAllocatorDefault, 0)
    
                    serialListPullDown?.addItemWithTitle("\(serialport)")
                }
            }
            else {
                // error
            }
    
        }
    }
    
  • user3185748
    user3185748 almost 10 years
    So that would become this? imgur.com/ecm3JHM Because it's returning the same error for me.
  • Woodstock
    Woodstock almost 10 years
    IORegistryEntryCreateCFProperty returns a CFTypeRef, so you need to declare your var type CFTypeRef not CFString
  • Woodstock
    Woodstock almost 10 years
    @user3185748 CFTypeRef is like Any for Core Foundation
  • user3185748
    user3185748 almost 10 years
    Firstly, thank you for your patience as I try to wrap my head around this. The suggested CFTypeRef however doesn't appear to fix the issue: imgur.com/DxKrGiX
  • Woodstock
    Woodstock almost 10 years
    @user3185748 no problem, OK next lets look at what you are calling IORegistryEntryCreateCFProperty with. What type is kIOCalloutDeviceKey? I think it needs to be a CFString, cast it like this: CFSTR(kIOCalloutDeviceKey)
  • user3185748
    user3185748 almost 10 years
    That seems a little closer, now it's just giving me: "Use of unresolved identifier 'CFSTR'".
  • Woodstock
    Woodstock almost 10 years
  • mklement0
    mklement0 over 8 years
    Perhaps recent changes to Swift changed the behavior: It seems that at least as of Swift version 2.0 you can now convert directly from CFString: var swiftString:String = cfStr as String. Your var swiftString:String = nsTypeString example now needs suffix as String in order to compile.