Swift CNCopySupportedInterfaces not valid - ios

Trying to get the SSID of current device. I have found plenty of examples on how to do it however I am struggling with getting the CNCopySupportedInterfaces to autocomplete. I have 'import SystemConfiguration' at the top of my swift file but no success. Can't seem to figure out what I am doing wrong.

iOS 12
You must enable Access WiFi Information from capabilities.
Important
To use this function in iOS 12 and later, enable the Access WiFi Information capability for your app in Xcode. When you enable this capability, Xcode automatically adds the Access WiFi Information entitlement to your entitlements file and App ID. Documentation link
You need: import SystemConfiguration.CaptiveNetwork
Underneath the covers, CaptiveNetwork is a C header file (.h) that is within the SystemConfiguration framework:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/SystemConfiguration.framework/Headers/CaptiveNetwork.h
If you know Objective-C, this goes into more depth:
iPhone get SSID without private library
You have to use the awkward syntax to bridge from any pure C API, so the following is required:
for interface in CNCopySupportedInterfaces().takeRetainedValue() as! [String] {
println("Looking up SSID info for \(interface)") // en0
let SSIDDict = CNCopyCurrentNetworkInfo(interface).takeRetainedValue() as! [String : AnyObject]
for d in SSIDDict.keys {
println("\(d): \(SSIDDict[d]!)")
}
}
ADDENDUM FOR SWIFT 2.2 and 3.0
The CFxxx datatypes are now bridged to native Objective-C runtime, eliminating the head-scratching retain calls. However, nullable pointers give rise to Optionals, so things don't get any shorter. At least, it's fairly clear what's going on, plus the nil helps us identify the simulator. The other answer uses an awful lot of bit-casting and unsafe operations which seems non-Swiftian, so I offer this.
func getInterfaces() -> Bool {
guard let unwrappedCFArrayInterfaces = CNCopySupportedInterfaces() else {
print("this must be a simulator, no interfaces found")
return false
}
guard let swiftInterfaces = (unwrappedCFArrayInterfaces as NSArray) as? [String] else {
print("System error: did not come back as array of Strings")
return false
}
for interface in swiftInterfaces {
print("Looking up SSID info for \(interface)") // en0
guard let unwrappedCFDictionaryForInterface = CNCopyCurrentNetworkInfo(interface) else {
print("System error: \(interface) has no information")
return false
}
guard let SSIDDict = (unwrappedCFDictionaryForInterface as NSDictionary) as? [String: AnyObject] else {
print("System error: interface information is not a string-keyed dictionary")
return false
}
for d in SSIDDict.keys {
print("\(d): \(SSIDDict[d]!)")
}
}
return true
}
Output on success:
SSIDDATA: <57696c6d 79>
BSSID: 12:34:56:78:9a:bc
SSID: YourSSIDHere

In Swift 2.0 / iOS 9 the API CaptiveNetwork is (nearly) gone or depreciated. I contacted Apple regarding this problem and I thought we could (or should) use the NEHotspotHelper instead. I got a respond from Apple today: One should continue to use CaptiveNetwork and the two relevant APIs (even tough there marked depreciated):
CNCopySupportedInterfaces
CNCopyCurrentNetworkInfo
The user braime posted an updated code-snippet for this problem on Ray Wenderlich forums:
let interfaces:CFArray! = CNCopySupportedInterfaces()
for i in 0..<CFArrayGetCount(interfaces){
let interfaceName: UnsafePointer<Void>
= CFArrayGetValueAtIndex(interfaces, i)
let rec = unsafeBitCast(interfaceName, AnyObject.self)
let unsafeInterfaceData = CNCopyCurrentNetworkInfo("\(rec)")
if unsafeInterfaceData != nil {
let interfaceData = unsafeInterfaceData! as Dictionary!
currentSSID = interfaceData["SSID"] as! String
} else {
currentSSID = ""
}
}
Works perfect for me.

Swift:
import SystemConfiguration.CaptiveNetwork
func currentSSIDs() -> [String] {
guard let interfaceNames = CNCopySupportedInterfaces() as? [String] else {
return []
}
return interfaceNames.flatMap { name in
guard let info = CNCopyCurrentNetworkInfo(name as CFString) as? [String:AnyObject] else {
return nil
}
guard let ssid = info[kCNNetworkInfoKeySSID as String] as? String else {
return nil
}
return ssid
}
}
Then print(currentSSIDs()), not working on simulator, only real devices.
Taken from https://forums.developer.apple.com/thread/50302

func getInterfaces() -> String? {
var ssid: String?
if let interfaces = CNCopySupportedInterfaces() as NSArray? {
for interface in interfaces {
if let interfaceInfo = CNCopyCurrentNetworkInfo(interface as! CFString) as NSDictionary? {
ssid = interfaceInfo[kCNNetworkInfoKeySSID as String] as? String
break
}
}
}
return ssid
}
In iOS 12 and up you will need to enable the Access WiFi Information capability for your app in order to get the ssid

Related

SystemConfiguration.CaptiveNetwork doesn't work on iOS 12

I have a function that detects the current SSID from the user. Unfortunately this doesn't work anymore with iOS 12. This means it just jumps over the if let interfaceInfo = CNCopyCurrentNetworkInfo(interface as! CFString) as NSDictionary? { part. Maybe it's just a bug or it's deprecated. I've found nothing on Apple Docs.
On older iOS 11, 10, and 9 devices, it works well.
Here's my Code:
func getWiFiSsid() -> String? {
if let interfaces = CNCopySupportedInterfaces() as NSArray? {
for interface in interfaces {
if let interfaceInfo = CNCopyCurrentNetworkInfo(interface as! CFString) as NSDictionary? {
ssid = interfaceInfo[kCNNetworkInfoKeySSID as String] as? String
}
}
}
return ssid
}
To use this function in iOS 12 and later, enable the Access WiFi Information capability for your app in Xcode. When you enable this capability, Xcode automatically adds the Access WiFi Information entitlement to your entitlements file and App ID.
https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo?language=objc

How to check if WiFi is on or off in iOS Swift 2?

I want to check if the wifi is off then show alert to the user to check his/her connectivity.
I find code like this but it checks if there is an internet connection, not checking if the wifi is on or off:
func isConnectionAvailble()->Bool{
var rechability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, "www.apple.com").takeRetainedValue()
var flags : SCNetworkReachabilityFlags = 0
if SCNetworkReachabilityGetFlags(rechability, &flags) == 0
{
return false
}
let isReachable = (flags & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection)
}
You can't.
With Apple's reachability class, you can distinguish three things according to the NetworkStatus struct:
typedef enum : NSInteger {
NotReachable = 0, // 1
ReachableViaWiFi, // 2
ReachableViaWWAN // 3
} NetworkStatus;
You have neither WiFi nor mobile data connection.
You have a WiFi connection, but you may or may not have a mobile data connection.
You have a mobile data connection, but no WiFi connection.
You can't check whether WiFi is turned off, or whether WiFi is turned on but there is no WiFi network nearby, or whether Airplane mode has been turned on.
For mobile data, you can use the telephony class to find whether your device is capable of mobile data connections (iPhone and not iPad, and SIM card plugged in), and you can detect whether mobile data is disabled in the preferences of your application.
Found the following, which was really helpful for me (found on the Apple Developer Forums). The below code works with Swift 4.
func fetchSSIDInfo() -> String {
var currentSSID = ""
if let interfaces:CFArray = CNCopySupportedInterfaces() {
for i in 0..<CFArrayGetCount(interfaces){
let interfaceName: UnsafeRawPointer = CFArrayGetValueAtIndex(interfaces, i)
let rec = unsafeBitCast(interfaceName, to: AnyObject.self)
let unsafeInterfaceData = CNCopyCurrentNetworkInfo("\(rec)" as CFString)
if unsafeInterfaceData != nil {
let interfaceData = unsafeInterfaceData! as Dictionary!
for dictData in interfaceData! {
if dictData.key as! String == "SSID" {
currentSSID = dictData.value as! String
}
}
}
}
}
return currentSSID
}
You can then check if a device is connected to Wi-Fi by the following:
if fetchSSIDInfo() != nil {
/* Wi-Fi is Connected */
}
Not perfect, but if the device is not connected to a Wi-Fi Network, you could then ask the user to connect to a Wi-Fi Network:
let wifiNotifcation = UIAlertController(title: "Please Connect to Wi-Fi", message: "Please connect to your standard Wi-Fi Network", preferredStyle: .alert)
wifiNotifcation.addAction(UIAlertAction(title: "Open Wi-Fi", style: .default, handler: { (nil) in
let url = URL(string: "App-Prefs:root=WIFI")
if UIApplication.shared.canOpenURL(url!){
UIApplication.shared.openURL(url!)
self.navigationController?.popViewController(animated: false)
}
}))
self.present(wifiNotifcation, animated: true, completion: nil)
Tested with swift 4 and swift 5
let nwPathMonitor = NWPathMonitor()
nwPathMonitor.pathUpdateHandler = { path in
if path.usesInterfaceType(.wifi) {
print("Path is Wi-Fi")
} else if path.usesInterfaceType(.cellular) {
print("Path is Cellular")
} else if path.usesInterfaceType(.wiredEthernet) {
print("Path is Wired Ethernet")
} else if path.usesInterfaceType(.loopback) {
print("Path is Loopback")
} else if path.usesInterfaceType(.other) {
print("Path is other")
}
}
nwPathMonitor.start(queue: .main)
As already #abba_de_bo mentioned: you could fetch the current SSID and check if it's set or nil.
This is the answer Apple's Eskimo gave to this question:
The trick with using CF-based APIs from Swift is to get the data into ‘Swift space’ as quickly as possible.
func currentSSIDs() -> [String] {
guard let interfaceNames = CNCopySupportedInterfaces() as? [String] else {
return []
}
return interfaceNames.flatMap { name in
guard let info = CNCopyCurrentNetworkInfo(name as CFString) as? [String:AnyObject] else {
return nil
}
guard let ssid = info[kCNNetworkInfoKeySSID as String] as? String else {
return nil
}
return ssid
}
}
Note that this returns an array of names; how you handle the non-standard cases (no elements, more than one element) is up to you.
Make sure you import SystemConfiguration.CaptiveNetwork. Otherwise the build will fail with on of those error messages:
Use of unresolved identifier 'CNCopySupportedInterfaces'
Use of unresolved identifier 'CNCopyCurrentNetworkInfo'
Use of unresolved identifier 'kCNNetworkInfoKeySSID'
You can take a look at the official Apple sample for Reachability:
https://developer.apple.com/library/content/samplecode/Reachability/Introduction/Intro.html
var netStatus = reachability.currentReachabilityStatus()
var connectionRequired = reachability.connectionRequired()
var statusString = ""
switch netStatus {
case NotReachable:
break
case ReachableViaWWAN:
//DATA
break
case ReachableViaWiFi:
//WIFI
break
}
You can use this method to check:
First you import this framework:
import SystemConfiguration.CaptiveNetwork
func isWifiEnabled() -> Bool {
var hasWiFiNetwork: Bool = false
let interfaces: NSArray = CFBridgingRetain(CNCopySupportedInterfaces()) as! NSArray
for interface in interfaces {
// let networkInfo = (CFBridgingRetain(CNCopyCurrentNetworkInfo(((interface) as! CFString))) as! NSDictionary)
let networkInfo: [AnyHashable: Any]? = CFBridgingRetain(CNCopyCurrentNetworkInfo(((interface) as! CFString))) as? [AnyHashable : Any]
if (networkInfo != nil) {
hasWiFiNetwork = true
break
}
}
return hasWiFiNetwork;
}

Displaying SSID in iOS App using Swift

I'm currently trying to display the SSID of a user's connected WiFi and compare it to a particular SSID, for example, the set SSID is 'WirelessHotspot'.
When the user's connected WiFi is 'WirelessHotspot', the app will display that it is connected to the correct WiFi and also display the WiFi name.
Currently, I have tried this code, referenced from Get SSID in Swift 2:
import UIKit
import Foundation
import SystemConfiguration.CaptiveNetwork
public class SSID {
class func fetchSSIDInfo() -> String {
var currentSSID = ""
if let interfaces:CFArray! = CNCopySupportedInterfaces() {
for i in 0..<CFArrayGetCount(interfaces){
let interfaceName: UnsafePointer<Void> = CFArrayGetValueAtIndex(interfaces, i)
let rec = unsafeBitCast(interfaceName, AnyObject.self)
let unsafeInterfaceData = CNCopyCurrentNetworkInfo("\(rec)")
if unsafeInterfaceData != nil {
let interfaceData = unsafeInterfaceData! as Dictionary!
currentSSID = interfaceData["SSID"] as! String
}
}
self.networkname.text = String(currentSSID)
}
return currentSSID
}
}
class AttendanceScreen: UIViewController {
#IBOutlet weak var networkname: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
However, this code:
self.networkname.text = String(currentSSID)
Will return error:
Type 'SSID' has no member 'networkname'
So, how can I implement this in Swift for iOS 9? Thanks in advance!
I figured out that it would be much easier to create a bridge from Swift to Objective-C.
Importing framework:
#import <SystemConfiguration/CaptiveNetwork.h>
Code to get the SSID of user's connected WiFi:
func getMAC()->(success:Bool,ssid:String,mac:String){
if let cfa: NSArray = CNCopySupportedInterfaces() {
for x in cfa {
if let dict = CFBridgingRetain(CNCopyCurrentNetworkInfo(x as! CFString)) {
let ssid = dict ["SSID"]!
let mac = dict["BSSID"]!
return (true, ssid as! String, mac as! String)
}
}
}
return (false,"","")
}
Print and display in a label when needed:
let x = getMAC()
if x.success {
MAClabel = x.mac
SSIDlabel = x.ssid
print(x.mac)
print (x.ssid)
}
I hope that those with this question would find this useful!

How to get ssid in Swift 2.0 without CaptiveNetwork deprecated framework?

No one of the solutions found in this other question Get SSID in Swift 2 works because CaptiveNetwork framework was deprecated in Swift 2.0
In Swift 1.2 a use this function:
func getSSID() -> String {
let interfaces = CNCopySupportedInterfaces()
if interfaces == nil {
return ""
}
//let interfacesArray = interfaces.takeRetainedValue() as! [String]
let interfacesArray = Array(arrayLiteral: interfaces)
if interfacesArray.count <= 0 {
return ""
}
let interfaceName = String(interfacesArray[0])
let unsafeInterfaceData = CNCopyCurrentNetworkInfo(interfaceName)
if unsafeInterfaceData == nil {
return ""
}
let interfaceData = unsafeInterfaceData.takeRetainedValue() as Dictionary!
print(interfaceData["SSID"], terminator: "")
return interfaceData["SSID"] as! String
}
But the following code does not work anymore..
As far as I'm aware, the CaptiveNetwork APIs are deprecated in iOS 9, but still available — so you should still be able to use them (at your own peril, as future updates may cause them to no longer work as expected). If they aren't visible from Swift, you can make them so from an ObjC bridging header.
This isn't an area I work with much, but it looks like the new Network Extensions API is intended to replace CaptiveNetwork anyway. See Network Extension Framework Reference for docs and the WWDC15 session What's New in Network Extension and VPN.

How to get available wifi network name in iOS using swift

I want to get all the WiFi networks available in a region and their SSID value. But the problem is how to get the SSID of all the WiFi network available even if I am not connected to one.
iOS 12
You must enable Access WiFi Information from capabilities.
Important
To use this function in iOS 12 and later, enable the Access WiFi Information capability for your app in Xcode. When you enable this capability, Xcode automatically adds the Access WiFi Information entitlement to your entitlements file and App ID. Documentation link
First;
import SystemConfiguration.CaptiveNetwork
Then;
func getInterfaces() -> Bool {
guard let unwrappedCFArrayInterfaces = CNCopySupportedInterfaces() else {
print("this must be a simulator, no interfaces found")
return false
}
guard let swiftInterfaces = (unwrappedCFArrayInterfaces as NSArray) as? [String] else {
print("System error: did not come back as array of Strings")
return false
}
for interface in swiftInterfaces {
print("Looking up SSID info for \(interface)") // en0
guard let unwrappedCFDictionaryForInterface = CNCopyCurrentNetworkInfo(interface) else {
print("System error: \(interface) has no information")
return false
}
guard let SSIDDict = (unwrappedCFDictionaryForInterface as NSDictionary) as? [String: AnyObject] else {
print("System error: interface information is not a string-keyed dictionary")
return false
}
for d in SSIDDict.keys {
print("\(d): \(SSIDDict[d]!)")
}
}
return true
}
Here my class that prints the WIFI network name
import UIKit
import Foundation
import SystemConfiguration.CaptiveNetwork
class FirstView: UIViewController
{
#IBOutlet weak var label: UILabel!
override func viewDidLoad()
{
super.viewDidLoad()
let ssid = self.getWiFiName()
print("SSID: \(ssid)")
}
func getWiFiName() -> String? {
var ssid: String?
if let interfaces = CNCopySupportedInterfaces() as NSArray? {
for interface in interfaces {
if let interfaceInfo = CNCopyCurrentNetworkInfo(interface as! CFString) as NSDictionary? {
ssid = interfaceInfo[kCNNetworkInfoKeySSID as String] as? String
break
}
}
}
return ssid
}
}
Yes it is possible to list all nearby WiFi networks.You need to complete a questionnaire at https://developer.apple.com/contact/network-extension, and then you can use NEHotspotHelper to return a list of hotspots. Technical Q&A https://developer.apple.com/library/archive/qa/qa1942/_index.html

Resources