iPhone obtener SSID sin biblioteca privada

154

Tengo una aplicación comercial que tiene una razón completamente legítima para ver el SSID de la red a la que está conectada: si está conectada a una red Adhoc para un dispositivo de hardware de terceros, debe estar funcionando de una manera diferente de lo que es. conectado a Internet.

Todo lo que he visto sobre obtener el SSID me dice que tengo que usar Apple80211, que entiendo es una biblioteca privada. También leí que si uso una biblioteca privada, Apple no aprobará la aplicación.

¿Estoy atrapado entre una Apple y un lugar difícil, o hay algo que me falta aquí?

Steve Reed Sr
fuente

Respuestas:

186

A partir de iOS 7 u 8, puede hacer esto (necesita el derecho para iOS 12+ como se muestra a continuación):

@import SystemConfiguration.CaptiveNetwork;

/** Returns first non-empty SSID network info dictionary.
 *  @see CNCopyCurrentNetworkInfo */
- (NSDictionary *)fetchSSIDInfo {
    NSArray *interfaceNames = CFBridgingRelease(CNCopySupportedInterfaces());
    NSLog(@"%s: Supported interfaces: %@", __func__, interfaceNames);

    NSDictionary *SSIDInfo;
    for (NSString *interfaceName in interfaceNames) {
        SSIDInfo = CFBridgingRelease(
            CNCopyCurrentNetworkInfo((__bridge CFStringRef)interfaceName));
        NSLog(@"%s: %@ => %@", __func__, interfaceName, SSIDInfo);

        BOOL isNotEmpty = (SSIDInfo.count > 0);
        if (isNotEmpty) {
            break;
        }
    }
    return SSIDInfo;
}

Salida de ejemplo:

2011-03-04 15:32:00.669 ShowSSID[4857:307] -[ShowSSIDAppDelegate fetchSSIDInfo]: Supported interfaces: (
    en0
)
2011-03-04 15:32:00.693 ShowSSID[4857:307] -[ShowSSIDAppDelegate fetchSSIDInfo]: en0 => {
    BSSID = "ca:fe:ca:fe:ca:fe";
    SSID = XXXX;
    SSIDDATA = <01234567 01234567 01234567>;
}

Tenga en cuenta que no se admiten ifs en el simulador. Prueba en tu dispositivo.

iOS 12

Debe habilitar el acceso a la información wifi desde las capacidades.

Importante Para usar esta función en iOS 12 y versiones posteriores, habilite la capacidad de Acceso a información WiFi para su aplicación en Xcode. Cuando habilita esta capacidad, Xcode agrega automáticamente el derecho de Acceso a la información WiFi a su archivo de derechos e ID de la aplicación. Enlace de documentación

Swift 4.2

func getConnectedWifiInfo() -> [AnyHashable: Any]? {

    if let ifs = CFBridgingRetain( CNCopySupportedInterfaces()) as? [String],
        let ifName = ifs.first as CFString?,
        let info = CFBridgingRetain( CNCopyCurrentNetworkInfo((ifName))) as? [AnyHashable: Any] {

        return info
    }
    return nil

}
Jeremy W. Sherman
fuente
8
¡Gracias! Si está utilizando ARC, así es como debería verse: - (id) fetchSSIDInfo {NSArray * ifs = ( bridge_transfer id) CNCopySupportedInterfaces (); NSLog (@ "% s: interfaces compatibles:% @", _func , ifs); id información = nil; para (NSString * ifnam en ifs) {info = ( bridge_transfer id) CNCopyCurrentNetworkInfo (( _bridge CFStringRef) ifnam); NSLog (@ "% s:% @ =>% @", __func , ifnam, info); if (info && [info count]) {break; }} devolver información; }
elsurudo
1
¡+1 funciona muy bien! No olvide agregar / vincular el marco [+] a su proyecto. Si ve errores de compilación extraños al usar este método, ese es probablemente su problema. Para, por ejemplo, obtener el SSID del diccionario devuelto, use // Obtener un objeto de diccionario que contenga la información de la red que el iPhone está conectado a NSDictionary * networkDict = [self fetchSSIDInfo]; // Seleccione el SSID de la información de red NSString * iPhoneNetworkSSID = [networkDict objectForKey: @ "SSID"];
Groot
¿Alguien sabe qué es BSSID? parece la dirección MAC de un enrutador, pero en realidad no lo es. tampoco es la dirección MAC del dispositivo.
peetonn
1
@Filip Actualizó la dirección del código compatible con ARC mediante el uso de incluye modular (en @importlugar de #import) Clang se vinculará automáticamente en el marco necesario cuando se importe un módulo, en lugar de solo un encabezado.
Jeremy W. Sherman
1
iOS 13 beta ahora requiere permiso de ubicación además de la capacidad para CNCopyCurrentNetworkInfodevolver información útil; de lo contrario vuelve nil. Ver: stackoverflow.com/questions/56583650/…
RedMatt
61

Aquí está la versión ARC limpiada, basada en el código de @ elsurudo:

- (id)fetchSSIDInfo {
     NSArray *ifs = (__bridge_transfer NSArray *)CNCopySupportedInterfaces();
     NSLog(@"Supported interfaces: %@", ifs);
     NSDictionary *info;
     for (NSString *ifnam in ifs) {
         info = (__bridge_transfer NSDictionary *)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
         NSLog(@"%@ => %@", ifnam, info);
         if (info && [info count]) { break; }
     }
     return info;
}
esad
fuente
1
Esta debería ser la respuesta preferida, ya que utiliza ARC. Me perdí esto desde el principio solo porque no era la respuesta ofrecida.
Johan Karlsson
@mindbomb tiene razón, aquí hay una pregunta sobre ese tema: stackoverflow.com/questions/31555640/…
newenglander
sí, Apple volvió a habilitar la API de CaptiveNetwork después de dejar de usar las versiones beta de iOS9 ..
mindbomb
@mindbomb, ¿sigue siendo así? Tengo problemas para implementar esto.
SamYoungNY
@SamYoungNY Tenga en cuenta que esto solo funciona en el dispositivo, en el simulador se devuelve un vacío
esad
60

ACTUALIZACIÓN PARA iOS 10 y superior

CNCopySupportedInterfaces ya no está en desuso en iOS 10. ( Referencia de la API )

Debe importar SystemConfiguration / CaptiveNetwork.h y agregar SystemConfiguration.framework a las bibliotecas vinculadas de su destino (en las fases de compilación).

Aquí hay un fragmento de código en swift (Respuesta de RikiRiocma) :

import Foundation
import SystemConfiguration.CaptiveNetwork

public class SSID {
    class func fetchSSIDInfo() -> String {
        var currentSSID = ""
        if let interfaces = 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
                }
            }
        }
        return currentSSID
    }
}

( Importante: CNCopySupportedInterfaces devuelve nulo en el simulador).

Para Objective-c, vea la respuesta de Esad aquí y abajo

+ (NSString *)GetCurrentWifiHotSpotName {    
    NSString *wifiName = nil;
    NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
    for (NSString *ifnam in ifs) {
        NSDictionary *info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
        if (info[@"SSID"]) {
            wifiName = info[@"SSID"];
        }
    }
    return wifiName;
}

ACTUALIZACIÓN PARA iOS 9

A partir de iOS 9, Captive Network está en desuso *. ( fuente )

* Ya no está en desuso en iOS 10, ver arriba.

Se recomienda usar NEHotspotHelper ( fuente )

Deberá enviar un correo electrónico a apple a [email protected] y solicitar derechos. ( fuente )

Código de muestra ( no es mi código. Vea la respuesta de Pablo A ):

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;
}

Nota al margen: Sí, desaprobaron CNCopySupportedInterfaces en iOS 9 e invirtieron su posición en iOS 10. Hablé con un ingeniero de redes de Apple y la reversión se produjo después de que muchas personas archivaron Radares y hablaron sobre el problema en los foros de desarrolladores de Apple.

Emin Israfil iOS
fuente
Apple me aprobó para usar la extensión de red, pero aún obtuve una matriz vacía de [NEHotspotHelper supportNetworkInterfaces]. ¿Sabes la posible razón?
JZAU
28

Esto funciona para mí en el dispositivo (no simulador). Asegúrese de agregar el marco de configuración del sistema.

#import <SystemConfiguration/CaptiveNetwork.h>

+ (NSString *)currentWifiSSID {
    // Does not work on the simulator.
    NSString *ssid = nil;
    NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
    for (NSString *ifnam in ifs) {
        NSDictionary *info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
        if (info[@"SSID"]) {
            ssid = info[@"SSID"];
        }
    }
    return ssid;
}
Chris
fuente
10

Este código funciona bien para obtener SSID.

#import <SystemConfiguration/CaptiveNetwork.h>

@implementation IODAppDelegate

@synthesize window = _window;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{


CFArrayRef myArray = CNCopySupportedInterfaces();
CFDictionaryRef myDict = CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(myArray, 0));
NSLog(@"Connected at:%@",myDict);
NSDictionary *myDictionary = (__bridge_transfer NSDictionary*)myDict;
NSString * BSSID = [myDictionary objectForKey:@"BSSID"];
NSLog(@"bssid is %@",BSSID);
// Override point for customization after application launch.
return YES;
}

Y estos son los resultados:

Connected at:{
BSSID = 0;
SSID = "Eqra'aOrange";
SSIDDATA = <45717261 27614f72 616e6765>;

}

Jameel
fuente
1
Funcionó perfectamente.
Yomo
9

Si está ejecutando iOS 12, deberá realizar un paso adicional. He estado luchando para que este código funcione y finalmente encontré esto en el sitio de Apple: "Importante Para usar esta función en iOS 12 y versiones posteriores, habilite la capacidad de Acceso a información WiFi para su aplicación en Xcode. Cuando habilita esta capacidad, Xcode automáticamente agrega el derecho de Acceso a la información WiFi a su archivo de derechos y al ID de la aplicación ". https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo

Muvimotv
fuente
Gracias, solucionó mi problema!
david72
5

Aquí está la versión corta y dulce de Swift.

Recuerde vincular e importar el Marco:

import UIKit
import SystemConfiguration.CaptiveNetwork

Define el método:

func fetchSSIDInfo() -> CFDictionary? {
    if let
        ifs = CNCopySupportedInterfaces().takeUnretainedValue() as? [String],
        ifName = ifs.first,
        info = CNCopyCurrentNetworkInfo((ifName as CFStringRef))
    {
        return info.takeUnretainedValue()
    }
    return nil
}

Llame al método cuando lo necesite:

if let
    ssidInfo = fetchSSIDInfo() as? [String:AnyObject],
    ssID = ssidInfo["SSID"] as? String
{
    println("SSID: \(ssID)")
} else {
    println("SSID not found")
}

Como se mencionó en otra parte, esto solo funciona en su iDevice. Cuando no está en WiFi, el método devolverá nulo, de ahí el opcional.

n.Drake
fuente
2

Para iOS 13

A partir de iOS 13, su aplicación también necesita acceso a la ubicación principal para usar la CNCopyCurrentNetworkInfo función a menos que haya configurado la red actual o tenga configuraciones de VPN:
Extracto de https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo?language=objc

Entonces, esto es lo que necesita (consulte la documentación de Apple ):
- Enlace la CoreLocation.frameworkbiblioteca
- Agregue location-servicescomo una UIRequiredDeviceCapabilitiesclave / valor en Info.plist
- Agregue una NSLocationWhenInUseUsageDescriptionclave / valor en Info.plist que describa por qué su aplicación requiere Ubicación principal
- Agregue el "Acceso WiFi Derecho de información "para su aplicación

Ahora, como un ejemplo de Objective-C, primero verifique si se ha aceptado el acceso a la ubicación antes de leer la información de la red usando CNCopyCurrentNetworkInfo:

- (void)fetchSSIDInfo {
    NSString *ssid = NSLocalizedString(@"not_found", nil);

    if (@available(iOS 13.0, *)) {
        if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
            NSLog(@"User has explicitly denied authorization for this application, or location services are disabled in Settings.");
        } else {
            CLLocationManager* cllocation = [[CLLocationManager alloc] init];
            if(![CLLocationManager locationServicesEnabled] || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined){
                [cllocation requestWhenInUseAuthorization];
                usleep(500);
                return [self fetchSSIDInfo];
            }
        }
    }

    NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
    id info = nil;
    for (NSString *ifnam in ifs) {
        info = (__bridge_transfer id)CNCopyCurrentNetworkInfo(
            (__bridge CFStringRef)ifnam);

        NSDictionary *infoDict = (NSDictionary *)info;
        for (NSString *key in infoDict.allKeys) {
            if ([key isEqualToString:@"SSID"]) {
                ssid = [infoDict objectForKey:key];
            }
        }
    }        
        ...
    ...
}
Francois B
fuente
Eso es necesario en iOS13, de lo contrario, obtuvo cero para CNCopyCurrentNetworkInfo ()
Franz Wang
@Sugar sí, de hecho iOS13 - mira la primera línea de mi respuesta
Francois B