0

So here's some code I found from another question, it has a forced downcast from Any to CFString

import SystemConfiguration.CaptiveNetwork

func fetchSSIDInfo() -> String? {
    var ssid: String?
    if let interfaces = CNCopySupportedInterfaces() as NSArray? {
        for interface in interfaces {
            // is there any way to remove the force downcast below 
            if let interfaceInfo = CNCopyCurrentNetworkInfo(interface as! CFString) as NSDictionary? {
                ssid = interfaceInfo[kCNNetworkInfoKeySSID as String] as? String
                break
            }
        }
    }
    return ssid
}

Do you have to use the forced downcast or is there another way? Are there special rules when dealing with old Corefoundation objects? Because working with NSObjects seems a lot simpler.

Yogurt
  • 2,913
  • 2
  • 32
  • 63
  • It is always a good idea to *add a link* to the question (or answer) from where you got the code that you are asking about. – Martin R Feb 05 '20 at 18:56
  • Try always to cast CoreFoundation types directly to Swift types rather than to *typeless* `NSArray/NSDictionary` for example `if let interfaces = CNCopySupportedInterfaces() as? [CFString] { ...` – vadian Feb 05 '20 at 19:03

2 Answers2

1

Import Foundation to give the compiler information about bridging between Core Foundation types and Foundation types. Then cast the array as you want:

import Foundation
import SystemConfiguration.CaptiveNetwork

func fetchSSIDInfo() -> String? {
    var ssid: String?
    if let interfaces = CNCopySupportedInterfaces() as? [CFString] {
        for interface in interfaces {
            if let interfaceInfo = CNCopyCurrentNetworkInfo(interface) as NSDictionary? {
                ssid = interfaceInfo[kCNNetworkInfoKeySSID as String] as? String
                break
            }
        }
    }
    return ssid
}
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • 2
    Also `as NSDictionary?` can be replaced by `as? [CFString: Any]`, I think. – Martin R Feb 05 '20 at 19:08
  • Probably but I can't actually test this easily right now as `CNCopyCurrentNetworkInfo` is iOS-only and I'm testing on macOS. – rob mayoff Feb 05 '20 at 19:14
  • I can confirm that it *compiles* in an iOS project, but I cannot *test* it due to the lack of captive networks at my place. – Martin R Feb 05 '20 at 19:17
1

You can avoid the forced downcast and safely validate the type of elements in the CFArray returned by CNCopySupportedInterfaces by conditionally casting each element to a String, and then to a CFString in your CNCopyCurrentNetworkInfo call.

func fetchSSIDInfo() -> String? {
    var ssid: String?
    if let interfaces = CNCopySupportedInterfaces() as NSArray? {
        for cfInterface in interfaces {
            guard let interface = cfInterface as? String else {
                continue
            }
            if let interfaceInfo = CNCopyCurrentNetworkInfo(interface as CFString) as NSDictionary? {
                ssid = interfaceInfo[kCNNetworkInfoKeySSID as String] as? String
                break
            }
        }
    }
    return ssid
}

Conditionally casting the return value of CNCopySupportedInterfaces to [CFString] allows you to avoid the forced downcast but will not safely validate the type of elements in the array. For example, the following snippet will compile but raises an NSException when executed.

let cfArray = [1 as CFNumber, 2 as CFNumber] as CFArray
if let arrayOfStrings = cfArray as? [CFString] {
    for e in arrayOfStrings {
        print((e as String).appending("foo"))
    }
}
bcal
  • 139
  • 2