I’m trying to get the IP address of the wifi interface on iOS devices using getifaddrs() and searching for the en0 name. This is working fine, but it assumes that en0 is always Wi-Fi. Is there some way to ensure that the interface I’m querying is specifically Wi-Fi for future proofing?
I use the following to find the wifi information by looking for a device which has a BSSID for the wifi station. It will also have a SSID.
The following class method returns the wifi info dictionary only. It is a simple extension to also return the device label (ifname variable). It is usually en0, as you have noted, but this would work as a way to make sure you pick up the wifi if it was not en0 in the future.
+ (NSDictionary *)getWifiInfo {
NSDictionary *ret=nil;
// Get the supported interfaces.
NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
// Find one with a BSSID and return that. This should be the wifi.
for (NSString *ifnam in ifs) {
NSDictionary *info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
NSLog(#"Network device found = %#", info.description);
if (info[(__bridge NSString *)kCNNetworkInfoKeyBSSID]) {
// Return this as it should be the right one given it has a MAC address for the station
ret=info;
}
}
NSLog(#"Exit: returning wifi info = %#", ret.description);
return ret;
}
Once you have the device label based on BSSID, you can then use getifaddrs to get its IP, gateway etc knowing it is the wifi device.
NB This returns nil in the iOS simulator as there is no wifi detected, so for testing you need some workaround code to meet your needs.
A hacky option is to find the IP address with an associated MAC of 02:00:00:00:00:00. iOS hides the MAC address from coders and it has this value.
Related
I need to get the device IP of the WiFi interface.
According to several StackOverflow threads, we could assume that "en0" corresponds to the Wi-Fi interface name :
https://stackoverflow.com/a/30754194/12866797
However, this feels like some kind of convention, not a standard.
Is there any consistent/standard way to retrieve the WiFi interface or the device WiFi IP address, using the iOS SDK ?
It would be nice if the API is available starting from iOS 11 but I won't be picky.
My best attempt was to use NWPathMonitor (iOS 12+) and monitor network changes corresponding to WiFi interfaces (NWInterface.InterfaceType.wifi) :
- (void) MonitorWifiInterface
{
m_pathMonitor = nw_path_monitor_create_with_type(nw_interface_type_wifi);
nw_path_monitor_set_update_handler(m_pathMonitor, ^(nw_path_t _Nonnull path) {
NSLog(#"[NetInterfaceUtilies] Network path changed");
nw_path_enumerate_interfaces(path, ^ bool (nw_interface_t _Nonnull itf)
{
NSLog(#"[NetInterfaceUtilies] Name : %s , Index : %u", nw_interface_get_name(itf), nw_interface_get_index(itf));
return true; // In order to continue the enumeration
});
});
nw_path_monitor_start(m_pathMonitor);
}
But I am not happy with it for the following reasons :
NWPathMonitor is supposed to be used for monitoring network changes : I haven't managed to get network information whenever I wanted, but only when WiFi has been set on/off.
I only managed to get the network interface name. But I can combine this data with the network interfaces retrieved with getifaddrs() in order to deduce the correct interface and IP : it's a step forward ?
It's "only" available starting from iOS 12.
I want to check which Bluetooth Devices my iPhone is connected to. In order to do that, I use CBCentralManager.retrieveConnectedPeripherals() like this:
let connectedPerphs = centralManager.retrieveConnectedPeripherals(withServices: []);
My problem is that even if my iPhone is connected to a BluetoothDongle (it explicitly says "connected" in the settings), the list that is returned by retriveConnectedPeripherals() is always empty. Am I using the method in a wrong way or can it not be used to detect a bluetooth connection such as the connection to to my dongle? If the latter is the case, how can I detect that connection?
Let me clear, centralManager.retrieveConnectedPeripherals always return empty or nil value, If you are not passing any value into serviceUUIDs
retrieveConnectedPeripherals(withServices:)
Returns a list of the peripherals (containing any of the specified
services) currently connected to the system.
serviceUUIDs:
A list of service UUIDs (represented by CBUUID objects).
Update:
Unfortunately this the long way to do it. You can create Array of CBUUID statically then you can pass it to the method. Please refer below code.
let aryUUID = ["1800","18811"]
var aryCBUUIDS = [CBUUID]()
for uuid in aryUUID{
let uuid = CBUUID(string: "1800")
aryCBUUIDS.append(uuid)
}
let connectedPerphs = centralManager.retrieveConnectedPeripherals(withServices: aryCBUUIDS)
List of available services
First, this works only with BLE devices, thus if your dongle is using a common BT you will not get it from here, but probably using EAAccessoryManager var connectedAccessories: [EAAccessory] method, but as far as I know your app must comply to MFI.
That is why is asking which service your devices are exposing as a filter.
I want to check WIFI signal strength to display some kind of message for weak WIFI signal.
I fount that it is not possible in iOS 8 and older.
Is it possible to get wifi signal strength in iOS 9? and if answer is yes then How?
Yes , it is possible in iOS 9.Look into NEHotspotNetwork
Register your app as Hotspot helper. (forums.developer.apple.com/message/30657#30657)
#import <NetworkExtension/NetworkExtension.h>
for(NEHotspotNetwork *hotspotNetwork in [NEHotspotHelper supportedNetworkInterfaces]) {
NSString *ssid = hotspotNetwork.SSID;
NSString *bssid = hotspotNetwork.BSSID;
BOOL secure = hotspotNetwork.secure;
BOOL autoJoined = hotspotNetwork.autoJoined;
double signalStrength = hotspotNetwork.signalStrength;
}
It is, but you need permission from Apple. Once Apple allows you to use the HotspotHelper framework, you'll be able to create profiles with the hotspot helper entitlements. Check this post: How to get Wifi SSID in iOS9 after CaptiveNetwork is deprecated and calls for Wifi name are already blocked
I am writing an iOS app to communicate with a BLE device. The device can change names between connections (not during the BLE connection), but iOS refuses to change the device name.
For example: I can connect to the device when its name is SadName. I disconnect it, shut down the app, etc. and change the device's name to HappyName. But, when I scan for devices iOS still shows the peripheral name as SadName.
If I debug the app and look at:
(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
the value of peripheral.name is SadName so I don't think that it is something that I am interpreting incorrectly in code. I should mention that when I scan for devices, my code is:
[self.CM scanForPeripheralsWithServices:nil options:0]; // Start scanning
I am guessing that it is simply because the devices UUID is the same so iOS is pulling it from its cached devices list, but I want to override that.
Thoughts? Sorry, I am new to iOS.
Cheers -
MSchmidtbauer
The CoreBluetooth API of iOS SDK does not provide a way to force refresh the peripheral name.
Currently it is not feasible to use peripheral.name in iOS when the device name in the BLEdevice changes.
Apple suggests to scan for a specific device by specifying a list of CBUUID objects (containing one or more service UUIDs) that you pass to scanForPeripheralsWithServices:
NSArray *services = #[[CBUUID UUIDWithString: #"2456e1b9-26e2-8f83-e744-f34f01e9d701"] ]; // change to your service UUID!
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:1] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
[self.manager scanForPeripheralsWithServices:services options:dictionary];
This reduces the number of calls of didDiscoverPeripheral. Do not just pass nil to scanForPeripheralsWithServices. It also allows your app to scan for a peripheral when in background state.
If you are looking for a way to broadcast dynamic information that's available before a connection is established, you can use the Advertise or Scan Response Data. The peripheral can be configured to broadcast the entries called Local Name and Manufacturer Specific Data. This data is availabe in the didDiscoverPeripheral:
- (void)centralManager: (CBCentralManager *)central
didDiscoverPeripheral: (CBPeripheral *)peripheral
advertisementData: (NSDictionary *)advertisementData
RSSI: (NSNumber *)RSSI {
NSString *localName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey];
NSData *manufacturerData = [advertisementData objectForKey:CBAdvertisementDataManufacturerDataKey];
NSLog(#"Local: name: %#", localName);
NSLog(#"Manufact. Data: %#", [manufacturerData description]);
}
Local Name is an NSString, so write only printable characters on the BLE device in this filed. Manufacturer Data is an NSData, this can contain any byte value, so you can even have binary data here.
Depending on the BLE device you use, the length of Local Name and Manufacturer Specific Data is limited.
On my BLE device,I can send the 128 Bit service UUID and a 8 char Local Name with the Advertise Data. The Manufacturer Specific Data goes into the Scan Response Data and can be 29 bytes long.
Good thing about using the Adv./Scan Response Data is, it can change on this BLE device without a power cycle.
Suggestion:
Use the service UUID to filter when scanning (UUID must be part of advertising data! I omitted it in the above description)
Use the Advertise/Scan Response Data for further filtering
Forget about peripheral.name as long as there is no deterministic refresh available
Your guessing is correct.
It is because of the core-blutetooth cache.
Generally changing name / services / characteristics on BLE devices are "not supported". All these parameters are getting cached.
There are two ways of solving this:
restart bluetooth adapter, so bluetooth cache gets cleared (I'm afraid there is no way to do this programatically, but i might be wrong)
your device BLE implements the GATT Service Changed characteristic: read about this here: core_v4.1.zip
Vol 3, Part G, 2.5.2, and Vol 3, Part G, 7.1.
Alternatively check the advertisement data of your BLE device. It might have a name property which should get refreshed every time the BLE device is advertising data (advertising data doesn't get cachced).
The CBPeripheralDelegate protocol contains a method...
- (void)peripheralDidUpdateName:(CBPeripheral *)peripheral NS_AVAILABLE(NA, 6_0);
... which is made for this purpose.
Edit - just realized that the second part of the accepted answer above has the same solution :-( I should have read more closely. I will leave this answer here anyway, since it includes RoboVM code.
I have found a solution to this problem. Adding the GATT Service Changed characteristic didn't work, nor did reading the device name directly from the Device Name characteristic 2A00 since iOS hides the Generic Access service. However, if the peripheral includes its local name in an advertising packet, it is available from the advertisement data dictionary provided on a scan result using the retrieval key CBAdvertisementDataLocalNameKey. I copy this into my BLE device wrapper and use it instead of the name available from the CBPeripheral. Example code in Java for RoboVM is below. The OBJC or Swift equivalent is straightforward.
#Override
public void didDiscoverPeripheral(CBCentralManager cbCentralManager, CBPeripheral cbPeripheral, CBAdvertisementData cbAdvertisementData, NSNumber rssi) {
NSData manufacturerData = cbAdvertisementData.getManufacturerData();
byte[] data = null;
if(manufacturerData != null)
data = manufacturerData.getBytes();
IosBleDevice bleDevice = new IosBleDevice(cbPeripheral);
String name = cbAdvertisementData.getLocalName();
if(name != null && !name.equals(cbPeripheral.getName())) {
CJLog.logMsg("Set local name to %s (was %s)", name, cbPeripheral.getName());
bleDevice.setName(name);
}
deviceList.put(bleDevice.getAddress(), bleDevice);
if(!iosBlueMaxService.getSubscriber().isDisposed()) {
BleScanResult bleScanResult = new IosBleScanResult(bleDevice,
cbAdvertisementData.isConnectable(),
data);
bleScanResult.setRssi(rssi.intValue());
iosBlueMaxService.getSubscriber().onNext(bleScanResult);
}
}
I'm looking for a way to scan available Wi-Fi access points (hotspots) from my IOS-app.
All that I need is a list of active at the moment hotspot names, where a device is able to connect to.
Like Safari shows a list of Wi-Fi hotspots when we start or activate it.
I'd like to publish the app on App Store, so I can't use any kind of Private API's (right?).
And unfortunatelly CoreWLAN.framework is unavailable for IOS (right?).
So, is it possible to achieve my target? Can I collect available access points names (SSID) some way?
There is no Public APIs at the moment that you can use to get a list of available Wi-Fi access points. As you're planning to publish on the App Store, Private APIs are not an option.
The closest thing you can achieve is getting your currently connected Wi-Fi name and details which can be achievied with CaptiveNetwork with the SystemConfiguration.framework.
+ (NSString *)currentSSID
{
NSString *ssid;
NSArray *interfaces = (__bridge_transfer id)CNCopySupportedInterfaces();
for (NSString *interfaceName in interfaces) {
NSDictionary *informations = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)interfaceName);
if (informations[#"SSID"]) {
ssid = informations[#"SSID"];
}
}
return ssid;
}