iPhone отримає SSID без приватної бібліотеки


154

У мене є комерційний додаток, який має цілком законну причину бачити SSID мережі, до якої він підключений: Якщо він підключений до мережі Adhoc для стороннього апаратного пристрою, він повинен функціонувати інакше, ніж якщо це підключено до Інтернету.

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

Я застряг між Apple і важким місцем, або щось тут мені не вистачає?


Відповіді:


186

Як і в 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 автоматично додає права доступу до інформації WiFi у файл ваших прав та ідентифікатор додатка. Документальне посилання

Швидкий 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

}

8
Дякую! Якщо ви використовуєте ARC, ось як він повинен виглядати: - (id) fetchSSIDInfo {NSArray * ifs = ( bridge_transfer id) CNCopySupportedInterfaces (); NSLog (@ "% s: Підтримувані інтерфейси:% @", _func , ifs); id info = нуль; для (NSString * ifnam у ifs) {info = ( bridge_transfer id) CNCopyCurrentNetworkInfo (( _bridge CFStringRef) ifnam); NSLog (@ "% s:% @ =>% @", __func , ifnam, info); if (info && [info count]) {перерва; }} повернення інформації; }
ельсурудо

1
+1 Чудово працює! Не забудьте додати / підключити рамку [+] до свого проекту. Якщо ви бачите дивні помилки компіляції під час використання цього методу, мабуть, ваша проблема. Наприклад, отримати SSID від повернення використання словника // Отримання об’єкта словника, що містить інформацію мережі, iPhone підключається до NSDictionary * networkDict = [self fetchSSIDInfo]; // Виберіть SSID з мережевої інформації NSString * iPhoneNetworkSSID = [networkDict objectForKey: @ "SSID"];
Грут

хтось знає, що таке BSSID? це схоже на MAC-адресу маршрутизатора, але насправді це не так. ні це MAC-адреса пристрою.
peetonn

1
@Filip Оновлений код ARC-зручного вирішення цього питання, використовуючи модульний включає ( @importа не #import). Clang автоматично посилатиметься на необхідний фреймворк, коли імпортується модуль, а не просто заголовок.
Джеремі В. Шерман

1
iOS 13 бета-версія потребує дозволу на розташування на додаток до можливості CNCopyCurrentNetworkInfoповернути корисну інформацію; інакше повертається nil. Див: stackoverflow.com/questions/56583650 / ...
RedMatt

61

Ось очищена версія 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;
}

1
Це дійсно має бути кращою відповіддю, оскільки він використовує ARC. Я пропустив це з самого початку лише тому, що це не відповідь, що пропонується.
Йохан Карлссон

@mindbomb правильно, ось питання про цю проблему: stackoverflow.com/questions/31555640 / ...
newenglander

Так, Apple знову ввімкнула API CaptiveNetwork після депресії на бета-версії iOS9 ..
mindbomb

@mindbomb це все-таки так? У мене виникають проблеми з цим впровадженням.
SamYoungNY

@SamYoungNY Зверніть увагу, що це працює лише на пристрої, на Simulator повертається порожнє
esad

60

ОНОВЛЕННЯ ДЛЯ iOS 10 і новіших версій

CNCopySupportedInterfaces більше не застаріло в iOS 10. ( API Reference )

Вам потрібно імпортувати SystemConfiguration / CaptiveNetwork.h та додати SystemConfiguration.framework до пов'язаних бібліотек вашої цілі (під фази збірки).

Ось фрагмент коду в швидкому режимі (відповідь 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 повертає нуль на тренажері.)

Для Objective-c дивіться відповідь Есада тут і нижче

+ (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 Закрита мережа застаріла *. ( джерело )

* Більше не застаріло в iOS 10, див. Вище.

Рекомендується використовувати NEHotspotHelper ( джерело )

Вам потрібно буде надіслати електронний лист на електронну пошту networkextension@apple.com та надіслати запит на отримання прав. ( джерело )

Приклад коду ( не мій код. Див. Відповідь Пабло А ):

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 Developer.


Я отримав схвалення від Apple для використання мережевого розширення, але я все-таки отримав порожній масив від [NEHotspotHelper supportNetworkInterfaces]. Чи знаєте ви можливу причину?
JZAU

28

Це працює для мене на пристрої (не на тренажері). Переконайтеся, що ви додали рамку конфігурації системи.

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

10

Цей код добре працює для отримання 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>;

}


1
Працювали чудово.
Йомо

9

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


Дякую, виправили мою проблему!
david72


5

Ось коротка та солодка версія Swift.

Не забудьте зв’язати та імпортувати Рамки:

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, метод поверне нуль - отже, необов’язковий.


2

Для iOS 13

З iOS 13 вашому додатку також потрібен доступ до базового розташування, щоб використовувати цю CNCopyCurrentNetworkInfo функцію, якщо він не налаштував поточну мережу або не має конфігурацій VPN:
витяг з https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo?language=objc

Отже, це те, що вам потрібно (див. Документацію щодо яблук ):
- Пов’язати CoreLocation.frameworkбібліотеку
- Додати location-servicesяк UIRequiredDeviceCapabilitiesключ / значення в Info.plist
- додати NSLocationWhenInUseUsageDescriptionключ / значення в Info.plist, що описує, чому для вашого додатка потрібна основна локація,
- додайте "Доступ до Wi-Fi Інформація "права на ваш додаток

Тепер, як приклад 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];
            }
        }
    }        
        ...
    ...
}

Це потрібно для iOS13, інакше ви отримаєте нуль для CNCopyCurrentNetworkInfo ()
Franz Wang

@Sugar Так дійсно iOS13 - дивіться перший рядок моєї відповіді
Франсуа Б
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.