IPhone получает SSID без частной библиотеки

У меня есть коммерческое приложение, которое имеет совершенно законную причину, чтобы увидеть SSID сети, к которой он подключен: если он подключен к сети Adhoc для стороннего аппаратного устройства, он должен работать по-другому, чем если он подключен к Интернету.

Все, что я видел о получении SSID, говорит мне, что я должен использовать Apple80211, который, как я понимаю, является частной библиотекой. Я также читал, что если я использую частную библиотеку, Apple не одобрит приложение.

Я застрял между Apple и жестким местом, или там что-то мне не хватает?

Ответ 1

Начиная с iOS 7 или 8, вы можете сделать это (вам нужны Права для iOS 12+, как показано ниже):

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

Пример вывода:

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

Обратите внимание, что в симуляторе не поддерживаются ifs. Проверьте на своем устройстве.

iOS 12

Вы должны включить доступ к информации Wi-Fi из возможностей.

Важный Чтобы использовать эту функцию в iOS 12 и позже, включите возможность Доступа к Информации WiFi для своего приложения в XCode. Когда вы включаете эту возможность, XCode автоматически добавляет право доступа к информации Wi-Fi в ваш файл разрешений и идентификатор приложения. Ссылка на документацию

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

}

Ответ 2

Здесь очищенная версия ARC, основанная на коде @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;
}

Ответ 3

ОБНОВЛЕНИЕ ДЛЯ iOS 10 и up

CNCopySupportedInterfaces больше не устаревает в iOS 10. (Справочник API)

Вам нужно импортировать SystemConfiguration/CaptiveNetwork.h и добавить SystemConfiguration.framework в целевые связанные библиотеки (в фазах сборки).

Вот фрагмент кода в swift (ответ 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
    }
}

( Важно: CNCopySupportedInterfaces возвращает nil на симуляторе.)

Для Objective-c см. Esad ответ здесь и ниже

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

ОБНОВЛЕНИЕ ДЛЯ iOS 9

В iOS 9 Captive Network устарела *. (источник)

* Больше не рекомендуется использовать в iOS 10, см. выше.

Рекомендуется использовать NEHotspotHelper (источник)

Вам нужно будет отправить электронное письмо по адресу [email protected] и запросить разрешения. (источник)

Пример кода (Не мой код. См. ответ Пабло):

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

Боковое примечание: Да, они отказались от CNCopySupportedInterfaces в iOS 9 и отменили свою позицию в iOS 10. Я разговаривал с инженером сети Apple, и разворот пришел после того, как так много людей подали Radars и высказались по поводу этой проблемы на форумах разработчиков Apple,

Ответ 4

Это работает для меня на устройстве (а не на симуляторе). Убедитесь, что вы добавили фреймворк systemconfiguration.

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

Ответ 5

Этот код работает хорошо, чтобы получить 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;
}

И это результаты:

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

}

Ответ 6

Если вы используете iOS 12, вам нужно сделать дополнительный шаг. Я пытаюсь сделать этот код работой и, наконец, нашел это на сайте Apple: "Важно. Чтобы использовать эту функцию в iOS 12 и более поздних версиях, включите возможность доступа к информации о WiFi для вашего приложения в Xcode. Когда вы включите эту возможность, Xcode автоматически добавляет право доступа к беспроводной сети доступа к вашему файлу прав и идентификатору приложения. " https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo

Ответ 8

Вот короткая и сладкая версия Swift.

Не забудьте связать и импортировать Framework:

import UIKit
import SystemConfiguration.CaptiveNetwork

Определите метод:

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

Вызвать метод, когда вам это нужно:

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

Как уже упоминалось, это работает только на вашем iDevice. Когда не в WiFi, метод вернет nil - следовательно, необязательный.

Ответ 9

Для iOS 13

Начиная с iOS 13, вашему приложению также необходим доступ к Core Location, чтобы использовать функцию CNCopyCurrentNetworkInfo function, если оно не настроило текущую сеть или не настроило VPN:
excerpt from https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo?language=objc

Так что это то, что вам нужно (см. документацию Apple):
 - Ссылка на библиотеку CoreLocation.framework
 - Добавьте location-services в качестве ключа/значения UIRequiredDeviceCapabilities в Info.plist
 - Добавьте ключ/значение NSLocationWhenInUseUsageDescription в Info.plist, описывающий, почему вашему приложению требуется базовое расположение
 - Добавьте разрешение "Доступ к WiFi-информации" для своего приложения

Теперь, в качестве примера Objective C, сначала проверьте, был ли принят доступ к местоположению, прежде чем читать информацию о сети, используя 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];
            }
        }
    }        
        ...
    ...
}