iPhone obtém SSID sem biblioteca privada

154

Eu tenho um aplicativo comercial que tem uma razão completamente legítima para ver o SSID da rede à qual está conectado: Se estiver conectado a uma rede Adhoc para um dispositivo de hardware de terceiros, ele precisará estar funcionando de maneira diferente da que está. Conectado a internet.

Tudo o que vi sobre a obtenção do SSID me diz que tenho que usar o Apple80211, o que entendo ser uma biblioteca privada. Também li que, se eu usar uma biblioteca privada, a Apple não aprovará o aplicativo.

Estou preso entre uma Apple e um lugar difícil ou há algo que estou perdendo aqui?

Steve Reed Sr
fonte

Respostas:

186

A partir do iOS 7 ou 8, você pode fazer isso (é necessário o Titular for iOS 12+, conforme mostrado abaixo):

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

Exemplo de saída:

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

Observe que nenhum ifs é suportado no simulador. Teste no seu dispositivo.

iOS 12

Você deve habilitar o acesso a informações wifi a partir dos recursos.

Importante Para usar esta função no iOS 12 e posterior, ative o recurso Acessar informações de WiFi para seu aplicativo no Xcode. Quando você ativa esse recurso, o Xcode adiciona automaticamente o direito de acesso às informações de WiFi ao seu arquivo de direitos e ID do aplicativo. Link da documentação

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
fonte
8
Obrigado! Se você estiver usando o ARC, veja como deve ser: - (id) fetchSSIDInfo {NSArray * ifs = ( id do bridge_transfer) CNCopySupportedInterfaces (); NSLog (@ "% s: interfaces suportadas:% @", _func , ifs); id info = nulo; for (NSString * ifnam em ifs) {info = ( bridge_transfer id) CNCopyCurrentNetworkInfo (( _bridge CFStringRef) ifnam); NSLog (@ "% s:% @ =>% @", __func , ifnam, informações); if (info && [info count]) {break; }} retornar informações; }
elsurudo
1
+1 Funciona muito bem! Não esqueça de adicionar / vincular a estrutura [+] ao seu projeto. Se você encontrar erros de compilação estranhos ao usar esse método, provavelmente é seu problema. Para, por exemplo, obter o SSID do dicionário retornado, use // Obtendo um objeto de dicionário contendo as informações da rede em que o iPhone está conectado ao NSDictionary * networkDict = [self fetchSSIDInfo]; // Selecione o SSID nas informações da rede NSString * iPhoneNetworkSSID = [networkDict objectForKey: @ "SSID"];
Groot
alguém sabe o que é BSSID? parece o endereço MAC de um roteador, mas na verdade não é. nem é o endereço MAC do dispositivo.
peetonn
1
@Filip O código atualizado compatível com ARC corrige isso usando inclusões modulares (em @importvez de #import). O Clang vinculará automaticamente a estrutura necessária quando um módulo, em vez de apenas um cabeçalho, for importado.
Jeremy W. Sherman
1
O iOS 13 beta agora requer permissão de localização, além da capacidade de CNCopyCurrentNetworkInforetornar informações úteis; caso contrário, ele retorna nil. Veja: stackoverflow.com/questions/56583650/…
RedMatt
61

Aqui está a versão limpa do ARC, com base no código do @ 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
fonte
1
Essa deve ser realmente a resposta preferida, pois usa o ARC. Eu perdi isso desde o início apenas porque não era a resposta oferecida.
Johan Karlsson
@mindbomb é certo, aqui está uma pergunta sobre essa questão: stackoverflow.com/questions/31555640/...
newenglander
sim, a Apple reativou a API do CaptiveNetwork após descontinuar o uso no iOS9 betas ..
mindbomb 5/15
@mindbomb este ainda é o caso? Estou tendo problemas para implementar isso.
SamYoungNY
@SamYoungNY Note que isto só funciona no dispositivo, em Simulator um vazio é devolvido
ESAD
60

ATUALIZAÇÃO PARA iOS 10 e superior

CNCopySupportedInterfaces não é mais reprovado no iOS 10. ( Referência da API )

Você precisa importar SystemConfiguration / CaptiveNetwork.he adicionar SystemConfiguration.framework às Bibliotecas vinculadas do seu destino (nas fases de construção).

Aqui está um trecho de código no swift (resposta 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 retorna nulo no simulador.)

Para o Objective-c, veja a resposta de Esad aqui e abaixo

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

ATUALIZAÇÃO PARA iOS 9

A partir do iOS 9, o Captive Network está obsoleto *. ( fonte )

* Não é mais descontinuado no iOS 10, veja acima.

É recomendável que você use NEHotspotHelper ( origem )

Você precisará enviar um e-mail para a apple em [email protected] e solicitar direitos. ( fonte )

Código de exemplo ( não é meu código. Veja a resposta 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 lateral: Sim, eles preteriram o CNCopySupportedInterfaces no iOS 9 e reverteram sua posição no iOS 10. Conversei com um engenheiro de rede da Apple e a reversão ocorreu depois que tantas pessoas arquivaram Radares e falaram sobre o problema nos fóruns de desenvolvedores da Apple.

Emin Israfil iOS
fonte
Fui aprovado pela apple para usar a extensão de rede, mas ainda tenho uma matriz vazia em [NEHotspotHelper supportedNetworkInterfaces]. Você conhece o possível motivo?
JZAU
28

Isso funciona para mim no dispositivo (não no simulador). Certifique-se de adicionar a estrutura de configuração do 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
fonte
10

Esse código funciona bem para obter o 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;
}

E este é o resultado:

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

}

Jameel
fonte
1
Funcionou perfeitamente.
Yomo
9

Se você estiver executando o iOS 12, precisará executar uma etapa extra. Eu tenho me esforçado para fazer esse código funcionar e finalmente o encontrei no site da Apple: "Importante Para usar esta função no iOS 12 e posterior, ative o recurso Acessar informações de WiFi para seu aplicativo no Xcode. Quando você habilita esse recurso, o Xcode automaticamente adiciona o direito de acesso às informações de WiFi ao seu arquivo de direitos e ID do aplicativo ". https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo

Muvimotv
fonte
Obrigado, corrigiu o meu problema!
David72 28/08
5

Aqui está a versão curta e doce do Swift.

Lembre-se de vincular e importar o Framework:

import UIKit
import SystemConfiguration.CaptiveNetwork

Defina o 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
}

Chame o método quando precisar:

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

Como mencionado em outro lugar, isso funciona apenas no seu iDevice. Quando não estiver no WiFi, o método retornará nulo - daí o opcional.

n.Drake
fonte
2

Para iOS 13

A partir do iOS 13, seu aplicativo também precisa de acesso ao Local Principal para usar a CNCopyCurrentNetworkInfo função, a menos que tenha configurado a rede atual ou possua configurações de VPN:
trecho de https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo?language=objc

Portanto, é isso que você precisa (consulte a documentação da Apple ):
- Vincule a CoreLocation.frameworkbiblioteca
- Adicione location-servicescomo uma UIRequiredDeviceCapabilitieschave / valor no Info.plist
- Adicione uma NSLocationWhenInUseUsageDescriptionchave / valor no Info.plist descrevendo por que seu aplicativo exige a localização principal
- adicione o "Access WiFi Informações "para o seu aplicativo

Agora, como um exemplo de Objective-C, verifique primeiro se o acesso ao local foi aceito antes de ler as informações da rede 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];
            }
        }
    }        
        ...
    ...
}
François B
fonte
Isso é necessário em iOS13, caso contrário, você tem nil para CNCopyCurrentNetworkInfo ()
Franz Wang
@Sugar sim, de fato iOS13 - veja a primeira linha da minha resposta #
Francois B