2

I've a problem with accessing a specific (or any) key in a CFDictionary. Honestly I don't really get the way you need to do this in Swift and I think it's overly complicated...

My Code:

if let session = DASessionCreate(kCFAllocatorDefault) {
let mountedVolumes = FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys: [], options: [])!
for volume in mountedVolumes {
    if let disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, volume as CFURL) {
        let diskinfo = DADiskCopyDescription(disk);   

        var volumeValue = CFDictionaryGetValue(diskinfo, <#T##key: UnsafeRawPointer!##UnsafeRawPointer!#>)


    }
}

What I want to achieve: Get the Value for the Key or field DAVolumeNetwork into the variable volumeValue. I guess I need to pass the kDADiskDescriptionVolumeNetworkKey to the CFDictionaryGetValue Method? How do I achieve this?

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
nja
  • 571
  • 1
  • 7
  • 15

2 Answers2

4

Don't use CFDictionary in Swift. (It is possible, but not worth the effort, see below.)

  • CFDictionary is toll-free bridged with NSDictionary, which in turn can be cast to a Swift Dictionary.
  • The value of the kDADiskDescriptionVolumeNetworkKey key is a CFBoolean which can be cast to a Swift Bool.

Example:

if let session = DASessionCreate(kCFAllocatorDefault),
    let mountedVolumes = FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys: []) {
    for volume in mountedVolumes {
        if let disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, volume as CFURL),
            let diskinfo = DADiskCopyDescription(disk) as? [NSString: Any] {

            if let networkValue = diskinfo[kDADiskDescriptionVolumeNetworkKey] as? Bool {
                print(networkValue)
            }
        }
    }
}

Just for the sake of completeness: This is the necessary pointer juggling to call CFDictionaryGetValue in Swift 3:

if let session = DASessionCreate(kCFAllocatorDefault),
    let mountedVolumes = FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys: []) {
    for volume in mountedVolumes {
        if let disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, volume as CFURL),
            let diskinfo = DADiskCopyDescription(disk) {

            if let ptr = CFDictionaryGetValue(diskinfo, Unmanaged.passUnretained(kDADiskDescriptionVolumeNetworkKey).toOpaque()) {
                let networkValue = Unmanaged<NSNumber>.fromOpaque(ptr).takeUnretainedValue()
                print(networkValue.boolValue)
            }
        }
    }
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thank you for the explanation and code! I still don't really get the CFDictionary but it's good if it can be cast in something useful :) – nja Mar 14 '17 at 13:44
  • 1
    @nja: CFDictionary uses a C API, passing void pointers around. In (Objective-)C you can simply pass an object pointer for a void pointer and vice versa. You cannot do that in Swift and have to use the Unmanaged type instead, compare http://stackoverflow.com/questions/33294620/how-to-cast-self-to-unsafemutablepointervoid-type-in-swift. – Martin R Mar 14 '17 at 13:51
  • CFDictionary is toll-free bridged with NSDictionary, and this is the easiest/most performant way to get values out in Swift: https://stackoverflow.com/a/45625787/225810 – Gabriel Aug 11 '17 at 01:59
1

You can cast CFDictionaryRef to a Swift dictionary:

if let disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, volume as CFURL), let diskinfo = DADiskCopyDescription(disk) as? [String: AnyObject] {
}

and then cast kDADiskDescriptionVolumeNetworkKey to a Swift string:

var volumeValue = diskinfo[kDADiskDescriptionVolumeNetworkKey as String]
sbooth
  • 16,646
  • 2
  • 55
  • 81
  • Casting a CFDictionary to Swift dictionary is expensive (not toll-free bridged). Best to cast to NSDictionary and use that: https://stackoverflow.com/a/45625787/225810 – Gabriel Aug 11 '17 at 02:00