iPhone otrzymuje identyfikator SSID bez prywatnej biblioteki

154

Mam komercyjną aplikację, która ma całkowicie uzasadniony powód, aby zobaczyć identyfikator SSID sieci, z którą jest połączona: Jeśli jest podłączona do sieci Adhoc urządzenia innej firmy, musi działać w inny sposób niż gdyby była Podłączony do internetu.

Wszystko, co widziałem na temat uzyskiwania identyfikatora SSID, mówi mi, że muszę używać Apple80211, co, jak rozumiem, jest prywatną biblioteką. Przeczytałem też, że jeśli korzystam z prywatnej biblioteki, Apple nie zatwierdzi aplikacji.

Czy utknąłem między jabłkiem a trudnym miejscem, czy jest coś, czego tu brakuje?

Steve Reed Sr
źródło

Odpowiedzi:

186

Od iOS 7 lub 8 możesz to zrobić (potrzebujesz uprawnienia dla iOS 12+, jak pokazano poniżej):

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

Przykładowe dane wyjściowe:

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

Należy pamiętać, że symulator nie obsługuje żadnych funkcji if. Przetestuj na swoim urządzeniu.

iOS 12

Musisz włączyć dostęp do informacji o Wi-Fi z funkcji.

Ważne Aby korzystać z tej funkcji w systemie iOS 12 i nowszych, włącz funkcję Access WiFi Information dla swojej aplikacji w Xcode. Po włączeniu tej funkcji Xcode automatycznie dodaje uprawnienie Access WiFi Information do pliku uprawnień i identyfikatora aplikacji. Link do dokumentacji

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
źródło
8
Dzięki! Jeśli używasz ARC, oto jak powinno to wyglądać: - (id) fetchSSIDInfo {NSArray * ifs = ( bridge_transfer id) CNCopySupportedInterfaces (); NSLog (@ "% s: Obsługiwane interfejsy:% @", _func , ifs); id info = nil; for (NSString * ifnam in ifs) {info = ( identyfikator_przenoszenia_ mostu) CNCopyCurrentNetworkInfo (( _bridge CFStringRef) ifnam); NSLog (@ "% s:% @ =>% @", __func , ifnam, info); if (info && [liczba informacji]) {przerwa; }} informacje zwrotne; }
elsurudo
1
+1 Działa świetnie! Nie zapomnij dodać / połączyć frameworka [+] do swojego projektu. Jeśli widzisz dziwne błędy kompilacji podczas korzystania z tej metody, prawdopodobnie jest to twój problem. Aby np. Pobrać SSID ze zwróconego słownika użyj // Pobranie obiektu słownika zawierającego informacje o sieci, do której iPhone jest podłączony do NSDictionary * networkDict = [self fetchSSIDInfo]; // Wybierz SSID z informacji o sieci NSString * iPhoneNetworkSSID = [networkDict objectForKey: @ "SSID"];
Groot
czy ktoś wie, co to jest BSSID? wygląda jak adres MAC routera, ale w rzeczywistości nim nie jest. nie jest też adresem MAC urządzenia.
peetonn
1
@Filip Zaktualizowany kod przyjazny dla ARC za pomocą modułowych dołączeń ( @importzamiast #import). Clang automatycznie połączy się w wymaganej strukturze, gdy importowany jest moduł, a nie tylko nagłówek.
Jeremy W. Sherman
1
iOS 13 beta wymaga teraz uprawnienia do lokalizacji oprócz możliwości CNCopyCurrentNetworkInfozwracania przydatnych informacji; w przeciwnym razie wraca nil. Zobacz: stackoverflow.com/questions/56583650/…
RedMatt
61

Oto oczyszczona wersja ARC oparta na kodzie @ 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
źródło
1
To naprawdę powinna być preferowana odpowiedź, ponieważ używa ARC. Brakowało mi tego od początku tylko dlatego, że nie była to podana odpowiedź.
Johan Karlsson,
@mindbomb ma rację, oto pytanie dotyczące tego problemu: stackoverflow.com/questions/31555640/ ...
newenglander
tak, Apple ponownie włączyło CaptiveNetwork API po wycofaniu go z wersji beta iOS9 ..
mindbomb
@mindbomb czy nadal tak jest? Mam problemy z wdrożeniem tego.
SamYoungNY
@SamYoungNY Zauważ, że działa to tylko na urządzeniu, w symulatorze zwracane jest puste
esad
60

AKTUALIZACJA DLA iOS 10 i nowszych

CNCopySupportedInterfaces nie jest już przestarzałe w iOS 10. (dokumentacja API )

Musisz zaimportować SystemConfiguration / CaptiveNetwork.h i dodać SystemConfiguration.framework do połączonych bibliotek celu (w fazie kompilacji).

Oto fragment kodu w języku swift (odpowiedź RikiRiocmy) :

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

( Ważne: CNCopySupportedInterfaces zwraca zero na symulatorze).

W przypadku Objective-c zobacz odpowiedź Esada tutaj i poniżej

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

AKTUALIZACJA DLA iOS 9

Od wersji iOS 9 Captive Network jest przestarzała *. ( źródło )

* Nie jest już przestarzała w iOS 10, patrz wyżej.

Zaleca się używanie NEHotspotHelper ( źródło )

Będziesz musiał wysłać e-mail do Apple na adres [email protected] i poprosić o uprawnienia. ( źródło )

Przykładowy kod (to nie mój kod. Zobacz odpowiedź 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;
}

Uwaga boczna: tak, wycofali oni CNCopySupportedInterfaces w iOS 9 i odwrócili swoje stanowisko w iOS 10. Rozmawiałem z inżynierem sieciowym Apple i odwrócenie nastąpiło po tym, jak wiele osób złożyło radary i wypowiedziało się o problemie na forach Apple Developer.

Emin Israfil iOS
źródło
Firma Apple wyraziła zgodę na używanie rozszerzenia sieciowego, ale nadal mam pustą tablicę z [NEHotspotHelper supportedNetworkInterfaces]. Czy znasz możliwy powód?
JZAU
28

To działa dla mnie na urządzeniu (nie na symulatorze). Upewnij się, że dodajesz strukturę konfiguracji systemu.

#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
źródło
10

Ten kod działa dobrze, aby uzyskać identyfikator 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;
}

A oto wyniki:

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

}

Jameel
źródło
1
Działał doskonale.
Yomo
9

Jeśli korzystasz z iOS 12, musisz wykonać dodatkowy krok. Zmagałem się, aby ten kod działał i w końcu znalazłem to w witrynie Apple: „Ważne Aby korzystać z tej funkcji w systemie iOS 12 i nowszych, włącz funkcję Dostęp do informacji o sieci Wi-Fi dla swojej aplikacji w Xcode. Po włączeniu tej funkcji Xcode automatycznie dodaje uprawnienie Access WiFi Information do pliku uprawnień i identyfikatora aplikacji ”. https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo

Muvimotv
źródło
Dziękuję, rozwiązało mój problem!
david72
5

Oto krótka i słodka wersja Swift.

Pamiętaj, aby połączyć i zaimportować Framework:

import UIKit
import SystemConfiguration.CaptiveNetwork

Określ metodę:

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

Wywołaj metodę, kiedy jej potrzebujesz:

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

Jak wspomniano w innym miejscu, działa to tylko na twoim iDevice. Gdy nie ma Wi-Fi, metoda zwróci zero - stąd opcja.

n.Drake
źródło
2

W przypadku iOS 13

Począwszy od iOS 13, Twoja aplikacja potrzebuje również dostępu do lokalizacji podstawowej, aby móc korzystać z tej CNCopyCurrentNetworkInfo funkcji, chyba że skonfigurowała bieżącą sieć lub ma konfiguracje VPN:
fragment z https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo?language=objc

Oto, czego potrzebujesz (zobacz dokumentację Apple ):
- Połącz CoreLocation.frameworkbibliotekę
- Dodaj location-servicesjako UIRequiredDeviceCapabilitiesklucz / wartość w Info.plist
- Dodaj NSLocationWhenInUseUsageDescriptionklucz / wartość w Info.plist opisując, dlaczego Twoja aplikacja wymaga lokalizacji podstawowej
- Dodaj „Dostęp do WiFi Uprawnienie do informowania o Twojej aplikacji

Teraz, jako przykład Objective-C, najpierw sprawdź, czy dostęp do lokalizacji został zaakceptowany przed odczytaniem informacji o sieci za pomocą 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.
źródło
Jest to wymagane na iOS13, w przeciwnym razie masz zero dla CNCopyCurrentNetworkInfo ()
Franz Wang
@Sugar, rzeczywiście, iOS13 - zobacz pierwszą linię mojej odpowiedzi
Francois B