[iOS 13] iOS 13からSSIDの取得方法が変わりました

iOS 13からSSIDの取得方法が変わり、iOS 12まで取得出来ていた方法では取得できなくなりました。

iOS 12までの取得方法

環境

Xcode 10.1
Swift 4.2

コード

import UIKit
import SystemConfiguration.CaptiveNetwork

class ViewController: UIViewController {

    var ssid: String {
        if let interfaces = CNCopySupportedInterfaces() as NSArray? {
            for interface in interfaces {
                if let interfaceInfo = CNCopyCurrentNetworkInfo(interface as! CFString) as NSDictionary?,
                    let ssid = interfaceInfo[kCNNetworkInfoKeySSID as String] as? String {
                    return ssid
                }
            }
        }
        return "取得できませんでした。"
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        print("ssid:\(self.ssid)")
    }
}

iOS 12からはAccess Wifi Informationを追加する必要があります。
参考:iOS12以降でCNCopyCurrentNetworkInfo() の注意点

iOS 12で実行すると、現在接続中のSSIDがログに出力されるかと思います。  

iOS 13で同じように実行すると、以下のようなログがでます。

ssid:Wi-Fi

SSIDが取れずWi-Fiとなってしまっています。

原因

WWDC2019のAdvances in Networking, Part 2で発表された内容が影響しているようです。

一部抜粋

And one of the things we realized is that accessing Wi-Fi information can be used to infer locations. So starting now, to access that Wi-Fi information, you will need the same kind of privileges that you need to get other location information.

Google翻訳

そして、私たちが気づいたことの1つは、Wi-Fi情報へのアクセスを使用して場所を推測できることです。 したがって、今から、そのWi-Fi情報にアクセスするには、他の位置情報を取得するために必要なのと同じ種類の特権が必要になります。

Wi-Fi情報にアクセスするには、位置情報を取得する権限が必要になったようです。

対応方法

iOS13でSSIDを取得するには、位置情報の許可を求めます。
先ほどのソースを修正します。

import UIKit
import SystemConfiguration.CaptiveNetwork
import CoreLocation

class ViewController: UIViewController {

    private var locationManager = CLLocationManager()

    var ssid: String {
        if let interfaces = CNCopySupportedInterfaces() as NSArray? {
            for interface in interfaces {
                if let interfaceInfo = CNCopyCurrentNetworkInfo(interface as! CFString) as NSDictionary?,
                    let ssid = interfaceInfo[kCNNetworkInfoKeySSID as String] as? String {
                    return ssid
                }
            }
        }
        return "取得できませんでした。"
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        if #available(iOS 13.0, *) {
            self.locationManager.delegate = self
            self.locationManager.requestWhenInUseAuthorization()
        } else {
            print("ssid:\(self.ssid)")
        }
    }
}

extension ViewController: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        print("ssid:\(self.ssid)")
    }
}

iOS 13で実行して位置情報を許可すると、SSIDがログに出力されます。

検証

CNCopyCurrentNetworkInfoのページを見ると、位置情報が許可されていない場合の挙動として、以下の記載があります。

・An app linked against iOS 12 or earlier receives a dictionary with pseudo-values. In this case, the SSID is Wi-Fi (or WLAN in the China region), and the BSSID is 00:00:00:00:00:00.
・An app linked against iOS 13 or later receives NULL.

Google翻訳

・iOS 12以前に対してリンクされたアプリは、擬似値を持つ辞書を受け取ります。
この場合、SSIDはWi-Fi(または中国地域のWLAN)であり、BSSIDは00:00:00:00:00:00です。
・iOS 13以降に対してリンクされたアプリはNULLを受け取ります。

ビルドするSDKによって挙動が変わるようなので、Xcode 10Xcode 11でビルドして動作を検証してみます。

XCode 10でビルドして、iOS 13で動作

ssid:Wi-Fi

CNCopyCurrentNetworkInfo は取れてますが、Wi-Fiが固定で返ってきます。

XCode 11でビルドして、iOS 13で動作

ssid:取得できませんでした。

CNCopyCurrentNetworkInfonilになり、SSIDを取得できませんでした。

最後に

あまりSSIDを取得する機会はないと思いますが、何かの参考になれば幸いです。

最後までご覧いただき、ありがとうございました。