iPhone如何在不使用私有库的情况下获取SSID

156

我有一个商业应用程序,它有完全合法的理由查看连接的网络的SSID:如果连接到第三方硬件设备的Adhoc网络,则需要以不同的方式运行。

我看到的所有关于获取SSID的信息都告诉我必须使用Apple80211,这是一个私有库。 我也读到过,如果使用私有库,苹果将不会批准此应用程序。

我是否处于进退两难的境地,或者我这里还有什么遗漏吗?


相关链接:https://dev59.com/5nRC5IYBdhLWcg3wRO3k - Senseful
9个回答

187

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

请注意,在模拟器上不支持任何if条件语句。请在您的设备上测试。

iOS 12

您必须从能力中启用WiFi访问信息。

重要提示 要在iOS 12及更高版本中使用此功能,请在Xcode中为您的应用程序启用Access WiFi Information功能。启用此功能时,Xcode会自动将Access WiFi Information权限添加到您的权限文件和App ID中。文档链接

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

}

8
谢谢!如果您正在使用ARC,这是它应该看起来的样子:
  • (id)fetchSSIDInfo { NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces(); NSLog(@"%s: 支持的接口:%@", func, ifs); id info = nil; for (NSString *ifnam in ifs) { info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam); NSLog(@"%s: %@ => %@", func, ifnam, info); if (info && [info count]) { break; } } return info; }
- elsurudo
1
+1 运行得非常好!不要忘记将[+]框架添加/链接到您的项目中。如果使用此方法时出现奇怪的编译错误,那可能是您的问题。例如,要从返回的字典中获取SSID,请使用以下代码:// 获取包含iPhone连接的网络信息的字典对象 NSDictionary *networkDict = [self fetchSSIDInfo];// 从网络信息中选择SSID NSString *iPhoneNetworkSSID = [networkDict objectForKey:@"SSID"]; - Groot
有人知道BSSID是什么吗?它看起来像路由器的MAC地址,但实际上并不是。它也不是设备的MAC地址。 - peetonn
1
@Filip 更新的 ARC 友好代码通过使用模块化包含(@import 而不是 #import)来解决这个问题。当导入一个模块而不仅仅是头文件时,Clang 会自动链接所需的框架。 - Jeremy W. Sherman
1
iOS 13 beta现在需要位置权限以及能够返回有用信息的CNCopyCurrentNetworkInfo功能;否则它将返回nil。 参见:https://dev59.com/X1MI5IYBdhLWcg3wS5cv - RedMatt
显示剩余5条评论

62

iOS 10及以上的更新

CNCopySupportedInterfaces在iOS 10中不再被弃用。(API参考)

您需要导入SystemConfiguration/CaptiveNetwork.h并将SystemConfiguration.framework添加到目标的链接库中(在构建阶段下)。

以下是一个Swift代码片段(RikiRiocma's Answer)

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。(来源

您需要向apple发送电子邮件(networkextension@apple.com)并请求授权。(来源

示例代码(非本人代码,请查看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;
}

旁注:是的,在iOS 9中他们弃用了CNCopySupportedInterfaces,并在iOS 10中扭转了他们的立场。我与一位苹果网络工程师交谈,这种扭转是在很多人在Apple开发者论坛上提交Radar并发表意见之后发生的。


我已经获得了苹果公司的批准来使用网络扩展,但是我仍然从[NEHotspotHelper supportedNetworkInterfaces]中得到了空数组。你知道可能的原因吗? - JZAU

61

这是基于 @elsurudo 的代码进行清理后的 ARC 版本:

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

@mindbomb 是正确的,这里有一个关于该问题的问题:https://dev59.com/XFwZ5IYBdhLWcg3wWfOl - newenglander
是的,苹果在 iOS9 beta 版本中弃用 CaptiveNetwork API 后重新启用了它。 - mindbomb
@mindbomb 这还是现状吗?我在实施这个过程中遇到了问题。 - SamYoungNY
@SamYoungNY 注意,这仅适用于设备,在模拟器上返回空值。 - esad

28
这对于我的设备有效(不适用于模拟器)。请确保添加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;
}

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

9

如果您正在运行iOS 12,您需要额外执行一步操作。我一直在努力使这个代码工作,最终在苹果的网站上找到了答案:

重要提示: 为了在iOS 12及以上版本中使用此函数,请在Xcode中为您的应用程序启用Access WiFi Information功能。当您启用此功能时,Xcode会自动将Access WiFi Information权限添加到您的权限文件和App ID。

https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo


5
这里是简短易懂的 Swift 版本。
记得链接和导入框架:
```swift import FrameworkName ```
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")
}

正如其他地方所提到的那样,这仅适用于您的i设备。当不在WiFi环境下时,该方法将返回空值 - 因此是可选的。

5

iOS 13

从iOS 13开始,如果你的应用没有配置当前网络或没有VPN配置,那么需要访问Core Location才能使用CNCopyCurrentNetworkInfo函数
摘自 https://developer.apple.com/documentation/systemconfiguration/1614126-cncopycurrentnetworkinfo?language=objc

下面是需要做的事情(参见苹果文档):
- 链接CoreLocation.framework
- 将location-services作为Info.plist中的UIRequiredDeviceCapabilities关键字和值添加
- 在Info.plist中添加一个NSLocationWhenInUseUsageDescription关键字和值以说明您的应用程序需要 Core Location 的原因
- 为您的应用程序添加“访问 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];
            }
        }
    }        
        ...
    ...
}

这在iOS13上是必需的,否则您将得到CNCopyCurrentNetworkInfo()nil - Franz Wang
@Sugar 是的,确实是iOS13 - 请看我回答的第一行。 - Francois B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接