I have an Objective-C iPhone application and Currently I am using below code to get the connected Wifi name. But it is not working in iOS 13. How can I get the connected Wifi SSID in iOS 13?
Currently I am using the below code in Swift:
public class SSID {
class func fetch() -> String {
var currentSSID = ""
if let interfaces = CNCopySupportedInterfaces() {
for i in 0..<CFArrayGetCount(interfaces) {
let interfaceName = CFArrayGetValueAtIndex(interfaces, i)
let rec = unsafeBitCast(interfaceName, to: AnyObject.self)
let unsafeInterfaceData = CNCopyCurrentNetworkInfo("\(rec)" as CFString)
if let interfaceData = unsafeInterfaceData as? [String: AnyObject] {
currentSSID = interfaceData["SSID"] as! String
let BSSID = interfaceData["BSSID"] as! String
let SSIDDATA = interfaceData["SSIDDATA"] as! String
debugPrint("ssid=\(currentSSID), BSSID=\(BSSID), SSIDDATA=\(SSIDDATA)")
}
}
}
return currentSSID
}
}
But this code is returning nil in iOS 13, Thanks in advance!
Using the code provided on iOS 14 I got the following error:
nehelper sent invalid result code [1] for Wi-Fi information request
Searching for that error took me to this question
Solution:
The requesting app must meet one of the following requirements:
The app uses Core Location, and has the user’s authorization to use
location information.
The app uses the NEHotspotConfiguration API to configure the current
Wi-Fi network.
The app has active VPN configurations installed.
An app that fails to meet any of the above requirements receives the
following return value:
An app linked against iOS 12 or earlier receives a dictionary with
pseudo-values. In this case, the SSID is Wi-Fi (or WLAN in the China
region), and the BSSID is 00:00:00:00:00:00.
An app linked against iOS 13 or later receives NULL.
Important
To use this function, an app linked against iOS 12 or later must
enable the Access WiFi Information capability in Xcode.
I also confirmed that this in fact works on iOS 14 once you request location permission.
import CoreLocation
import UIKit
import SystemConfiguration.CaptiveNetwork
final class ViewController: UIViewController {
var locationManager: CLLocationManager?
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager?.delegate = self
locationManager?.requestAlwaysAuthorization()
}
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
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedAlways || status == .authorizedAlways {
let ssid = self.getWiFiName()
print("SSID: \(String(describing: ssid))")
}
}
}
Output: SSID: YaMomsWiFi
Don't forget to include the wifi entitlement, and the necessary keys in your plist for location permission.
Related
I'm currently trying to build a proof-of-concept iOS app to check if we are able to implement some sort of indoor positioning capability without deploying beacons or any other hardware.
What we have
There is a database containing all registered access points in our building including their X- and Y-coordinates. The coordinates are mapped to a custom-built grid that spans the whole building.
The app will be released using our Enterprise distribution, so there are no constraints concerning any Apple Store requirements. The app will be running exclusively on devices that automatically connect to the proper WiFi using a certificate.
What we'd like to build
In order to improve the usability of the app, we'd like to show the user his current position. Using Apples native CLLocation services is not accurate enough because we are operating inside a building. The basic idea is to fetch all nearby access points including their BSSID and signal strength and calculate a more or less accurate position using both signal strength and the location database for our access points (see above).
What i've tried so far
Using SystemConfiguration.CaptiveNetwork to get the BSSID
import SystemConfiguration.CaptiveNetwork
func getCurrentBSSID() -> String {
guard let currentInterfaces = CNCopySupportedInterfaces() as? [String] else { return "" }
for interface in currentInterfaces {
print("Looking up BSSID info for \(interface)") // en0
let SSIDDict = CNCopyCurrentNetworkInfo(interface as CFString) as! [String : AnyObject]
return SSIDDict[kCNNetworkInfoKeyBSSID as String] as! String
}
return ""
}
This solution works (after setting the proper entitlements), but i'm only able to read the BSSID of the CURRENTLY CONNECTED access point.
Using UIStatusBarDataNetworkItemView to read signal strength
private func wifiStrength() -> Int? {
let app = UIApplication.shared
var rssi: Int?
guard let statusBar = app.value(forKey: "statusBar") as? UIView, let foregroundView = statusBar.value(forKey: "foregroundView") as? UIView else {
return rssi
}
for view in foregroundView.subviews {
if let statusBarDataNetworkItemView = NSClassFromString("UIStatusBarDataNetworkItemView"), view .isKind(of: statusBarDataNetworkItemView) {
if let val = view.value(forKey: "wifiStrengthRaw") as? Int {
rssi = val
break
}
}
}
return rssi
}
This one is kind of obvious, it only reads the signal strength for the connected WiFi network, not the access point specific one.
QUESTION
Is there any way to read a list of available access points (not WiFi networks) including their BSSID and signal strength? We cannot jailbreak the devices since they are under device management.
Maybe there is some way to do it using MobileWiFi.framework (see this link), but i couldn't wrap my head around doing it in Swift (kind of a beginner when it comes to iOS development).
I am afraid it is not possible to implement this on not jailbroken device.
I found some code for this, but it was outdated. I don't think that you will use it on iOS 3/4 devices.
NEHotspotHelper works only when Settings->Wifi page is active. You can get signal strength there, but I unsure how it will work.
MobileWiFi.framework requires entitlement, which can't be set without jailbreak.
Useful links:
Technical Q&A QA1942
Probably iBeacons or QR (AR) is the only options.
Although many resources say that while using Apple "official" frameworks, you can only get network's SSID that your iPhone is at the moment connected to. Here are workaround:
You can use NEHotspotConfigurationManager class but at first you must to enable the Hotspot Configuration Entitlement (property list key) in Xcode.
You can also use NEHotspotHelper class (although it requires Apple's permission). For this you need to apply for the Network Extension entitlement and then modify your Provisioning Profile plus some additional actions. Look at this SO post for further details.
Here's a code snippet how to use NEHotspotConfigurationManager:
import NetworkExtension
class ViewController: UIViewController {
let SSID = ""
#IBAction func connectAction(_ sender: Any) {
let hotspotConfig = NEHotspotConfiguration(ssid: SSID, passphrase: "", isWEP: false)
NEHotspotConfigurationManager.shared.apply(hotspotConfig) {[unowned self] (error) in
if let error = error {
self.showError(error: error)
} else {
self.showSuccess()
}
}
}
#IBAction func disconnectAction(_ sender: Any) {
NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: SSID)
}
private func showError(error: Error) {
let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
let action = UIAlertAction(title: "Darn", style: .default, handler: nil)
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
private func showSuccess() {
let alert = UIAlertController(title: "", message: "Connected", preferredStyle: .alert)
let action = UIAlertAction(title: "Cool", style: .default, handler: nil)
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
}
Here's a code snippet how to use NEHotspotHelper:
import NetworkExtension
import SystemConfiguration.CaptiveNetwork
func getSSID() -> String {
if #available(iOS 11.0, *) {
let networkInterfaces = NEHotspotHelper.supportedNetworkInterfaces()
let wiFi = NEHotspotNetwork()
let st = "SSID:\(wiFi.SSID), BSSID:\(wiFi.BSSID)"
return st
for hotspotNetwork in NEHotspotHelper.supportedNetworkInterfaces() {
let signalStrength = hotspotNetwork.signalStrength
print(signalStrength)
}
} else {
let interfaces = CNCopySupportedInterfaces()
guard interfaces != nil else {
return ""
}
let if0: UnsafePointer<Void>? = CFArrayGetValueAtIndex(interfaces, 0)
guard if0 != nil else {
return ""
}
let interfaceName: CFStringRef = unsafeBitCast(if0!, CFStringRef.self)
let dictionary = CNCopyCurrentNetworkInfo(interfaceName) as NSDictionary?
guard dictionary != nil else {
return ""
}
return String(dictionary![String(kCNNetworkInfoKeySSID)])
}
}
You can use transportable Differential GPS reference station inside your building and improve accuracy to about 1-3 cm and then rely on mobile phone built-in GPS.
I have a question concerning the WCSession transferUserInfo method. When I try to send a CLLocation object from the Apple Watch to the owning iPhone, the corresponding receive method is never called. The code on the watch side looks like follows (shortened of course):
class InterfaceController: WKInterfaceController, WCSessionDelegate, CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if WCSession.default.activationState == .activated {
let userInformation = ["MyLocation" : locations.last] as [String : Any]
WCSession.default.transferUserInfo(userInformation)
}
else {
os_log("Can not send session data", type: .error)
}
}
}
The iPhone counterpart code:
class TableViewController: UITableViewController, WCSessionDelegate {
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
let location = userInfo["MyLocation"] as? CLLocation
if location == nil {
os_log("Location not found", type: .error)
}
os_log("RX DATA : %#", location.description)
}
}
When I replace the location object by a string, everything works as expected. The string will be delivered to the iPhone.
Why is the CLLocation object not delivered but the string is? How can I configure XCode to show me the error/reason? Currently nothing happens, not even an error is shown.
Thank you
The user info dictionary only accepts property list types.
CLLocation isn't one, but Data is and CLLocation implements NSCoding so you can use a keyed archiver/unarchiver to convert it to Data and back.
Convert it to data on the watch:
if let location = locations.last {
let data = NSKeyedArchiver.archivedData(withRootObject: location)
// put the data in your user info and send it along
}
Then convert it back to a location on the phone:
if let data = userInfo["MyLocation"] as? Data,
let location = NSKeyedUnarchiver.unarchiveObject(with: data) as? CLLocation {
// do whatever with the location
}
I'm pretty new in Swift 3.
I want to get the ip of my NetServices which I explored with bonjour to show them to the user, not to connect with the device. So far I can search for devices with bonjour and get them listed in a listView with this great access code:
https://github.com/ecnepsnai/BonjourSwift
This is my function to scan the bonjour services and put them into a local array of NetServices:
// scanning for services, delete old bonjourServices Array and fill it with new discovered services
var bonjourServices = [NetService]()
private func putServicesToArray(){
let browser: Bonjour = Bonjour()
_ = browser.findService(Bonjour.Services.Line_Printer_Daemon, domain: Bonjour.LocalDomain) { (services) in
self.bonjourServices.removeAll()
for service in browser.services {
if !(self.bonjourServices.contains(service)) {
self.bonjourServices.append(service)
}
}
}
}
I'm using this method to get the ip address, from Swift 3 how to resolve NetService IP?
func netServiceDidResolveAddress(_ sender: NetService) {
print("netServiceDidResolveAddress get called with \(sender).")
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
guard let data = sender.addresses?.first else {
print("guard let data failed")
return
}
do {
try data.withUnsafeBytes { (pointer:UnsafePointer<sockaddr>) -> Void in
guard getnameinfo(pointer, socklen_t(data.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
throw NSError(domain: "domain", code: 0, userInfo: ["error":"unable to get ip address"])
}
}
} catch {
print(error)
return
}
let address = String(cString:hostname)
print("Adress:", address)
}
And with this IBAction I just want to print the ip address, but my ip is empty and my bonjourServices[0].addresses is empty also the addresses.adress NSData Objects
#IBAction func detectNetwork(_ sender: UIButton) {
print("putServiceToArray: ")
putServicesToArray()
for service in bonjourServices {
print(service)
}
bonjourTableView.reloadData()
if !(bonjourServices.isEmpty){
print(netServiceDidResolveAddress(bonjourServices[0]))
print(bonjourServices[0].addresses)
}
}
Here is my console output:
netServiceDidResolveAddress get called with <NSNetService 0x61800003d6a0> local. _printer._tcp. Brother HL-3152CDW series.
guard let data failed
()
Optional([])
Can you please help me resolve this problem?
I managed to solve my problem with this post: Bonjour Service Browser with Swift does not fetch serviceinfo
I did use Swift Development with Cocoa: Developing for the Mac and IOS App Stores published by O'Reilly for my code from page 299:
https://books.google.de/books?id=pNzSBQAAQBAJ&pg=PA299&lpg=PA299&dq=swift+implement+bonjour+discovery&source=bl&ots=6nhrmuDfEp&sig=eY9kPtzOl7nshavmwdwVYQlpNfM&hl=de&sa=X&ved=0ahUKEwjclvHGncLUAhXBKFAKHZeRByQ4ChDoAQgyMAI#v=onepage&q=swift%20implement%20bonjour%20discovery&f=false
And then I had to make a call like this:
self.bonjourServices.removeAll()
self.browser = NetServiceBrowser()
self.browser.delegate = self
self.browser.searchForServices(ofType:"_raop._tcp", inDomain: "")
Now it's working fine and I got all data of my services.
I am working with swift 2.0 and I am stuck with this and probably there is something silly that I must not be implementing properly .. So , when I turn off the location Services in the settings of the phone , and open my app , the prompt that tells the user to turn on the location services keeps popping up and doesn't allow user to select any option.. Anyone has any idea why this might be happening ? If this has been already answered , kindly send me the link as I tried searching and didn't get any help. Let me know if any part of the code needs to be posted as well . Thank you in advance. this is how I have implemented ..
class LocationProcessHandler: NSObject, CLLocationManagerDelegate {
static let sharedInstance = LocationProcessHandler()
let locationManager = CLLocationManager()
func startLocationUpdates() {
NSLog("It entered the start location updates method of the location process handler")
if ((CLLocationManager.locationServicesEnabled() == false) || (CLLocationManager.authorizationStatus() != CLAuthorizationStatus.Denied )) {
NSLog("It Entered to create the managed app feedback ")
let persist = Persistence()
let json: [NSObject : AnyObject] = [
"IsLocationSettingsEnabled" : "\(0)",mdmiosagent_Constants.MESSAGETYPEKEY : mdmiosagent_Constants.LOCATIONMSGTYPEKEY,mdmiosagent_Constants.UDIDKEY : persist.getObject(mdmiosagent_Constants.UDIDKEY),"TimeStamp" : "\(self.toLocalTime())"
]
let userDefaults : NSUserDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.setObject(json, forKey: mdmiosagent_Constants.MANAGED_APP_FEEDBACK)
userDefaults.synchronize()
NSLog("The dict to be sent as managed app feedback is \(json)")
do {
let jsonData : NSData = try NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions.PrettyPrinted)
let wrapper = HttpWrapper()
wrapper.silentPostData(serverurl: mdmiosagent_Constants.NATIVE_APP_SERVLET, urldata: jsonData)
} catch {
NSLog("json error")
}
}
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
if #available(iOS 8.0, *) {
if locationManager.respondsToSelector(#selector(CLLocationManager.requestWhenInUseAuthorization)) {
locationManager.requestAlwaysAuthorization()
}
else {
locationManager.startUpdatingLocation()
}
}
NSLog("Started to monitor the significant location Changes")
locationManager.stopMonitoringSignificantLocationChanges()
locationManager.startMonitoringSignificantLocationChanges()
// NSLog("the distance filter of the location manager is \(locationManager.distanceFilter)")
}
I am developing a location based app which is supposed to fetch user location always.Im using standard location service. But the problem is that the app after keeping idle for some time in background will not fetch the coordinates even after we move to some other locations. As per apple documentation, when a new location arrives, app should wake up automatically, but that is not happening here. I'm sharing the code and using to fetch location and screenshot of my plist.
class SALocation: NSObject,CLLocationManagerDelegate
{
static let sharedInstance : SALocation = SALocation()
var locationManager : CLLocationManager!
var location : CLLocation!
var address : String!
var latitude : NSString?
var longitude : NSString?
var isAdderssLoaded : Bool = false
var locdictionary : NSMutableDictionary = NSMutableDictionary()
func startLocationManager()
{
if self.locationManager == nil
{
self.locationManager = CLLocationManager()
if CLLocationManager.locationServicesEnabled(){
print("location service enabled")
}
self.locationManager.delegate = self
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.distanceFilter = kCLDistanceFilterNone
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
if ( Float (UIDevice.currentDevice().systemVersion) >= 9) {
if #available(iOS 9.0, *) {
self.locationManager.allowsBackgroundLocationUpdates = true
} else {
// Fallback on earlier versions
};
}
self.locationManager.startUpdatingLocation()
//self.locationManager.stopMonitoringSignificantLocationChanges()
}
else
{
self.locationManager.startUpdatingLocation()
}
}
// MARK: CLLocationManagerDelegate
func locationManager(manager: CLLocationManager, didFailWithError error: NSError)
{
UIAlertView(title:"Alert", message:error.description, delegate: nil, cancelButtonTitle:nil, otherButtonTitles:"Ok").show()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
if locations.count > 0
{
self.location = locations[0]
/* storing date and location to plist
*/
let datenow = NSDate()
let dateformatternow = NSDateFormatter ()
dateformatternow.dateFormat = "yyyyMMdd HH:mm:ss"
let timenow:NSString = dateformatternow.stringFromDate(datenow)
let documetsdirectorypath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true).last
latitude = NSString(format: "%f",self.location.coordinate.latitude)
longitude = NSString (format: "%f",self.location.coordinate.longitude)
let latlong : NSString = NSString(format:"%#~%#",latitude!,longitude!)
NSUserDefaults.standardUserDefaults().setObject(latlong, forKey: "latlong")
let aFilePath = NSString(format: "%#/location.plist",documetsdirectorypath!)
locdictionary.setObject(latlong, forKey: timenow as String)
locdictionary.writeToFile(aFilePath as String, atomically: true)
///////////// ||storing date and location to plist code ends here||\\\\\\
// self.getAddressFromLocation(locations[0] )
// if (NSUserDefaults.standardUserDefaults().objectForKey(SettingAppRefresh) != nil)
// {
// if (NSUserDefaults.standardUserDefaults().objectForKey(SettingAppRefresh) as! NSString).isEqualToString(FalseString)
// {
// // self.locationManager.stopUpdatingLocation()
// }
// }
}
}
}
What i'm doing here is just get location and write it to a plist file. This works in foreground, background etc fine. But when i keep the app idle for 20 minutes, location is not fetched even if i move to some other locations as the app is suspended
Capabilities tab looks like this
To start location in background you must start background service from the following path
Click on your name -> Click on your app name (target) -> goto capabilities -> find the background mode -> enable the location update mode
I am not sure you started that or not because you not put any screenshot about this.
And also check that your user started background refresh in settings.refer below link for this.
Background App Refresh checking, enabling and disabling programatically for whole device and for each particular application in iOS 7
Update::
For location update in background used below link(objective c)
http://www.creativeworkline.com/2014/12/core-location-manager-ios-8-fetching-location-background/
Well, I don't know how you're getting location updates - significant-location change as example and how you exit from background.
I suggest checking if your app is truly in background mode - UIApplication.sharedApplication().applicationState as it can be terminated.
And I also suggest checking out Apple's Execution States for Apps. - especially for your possible use case Implementing Long-Running Tasks part. There is also a good tutorial at rayywenderlich.com called Background modes.
Please use
self.locationManager.requestAlwaysAuthorization()
and don't forget to update your Info.plist to define the NSLocationAlwaysUsageDescription key.