前提
Xcode Version 12.4 (12D4e)で動作確認しています。
概要
iOSでCoreTelephonyを使って、今使っているキャリアが5Gかどうかを判別します。
また、その際の罠について紹介します。
結論
iOS 14.1以上で、5Gかどうかの判断をするようにしましょう。
今使っているキャリアを取得
まず、CTTelephonyNetworkInfo
を生成します。
CTTelephonyNetworkInfoからserviceSubscriberCellularProviders
を使って端末のキャリアを取得します。
keyとともに CTCarrier
を取得できます。
let info = CTTelephonyNetworkInfo()
info.serviceSubscriberCellularProviders?.forEach({ (key, value) in
print(value.carrierName ?? "")
})
無線テクノロジーを取得
先ほど得たキーで、serviceCurrentRadioAccessTechnology
より無線のテクノロジーが何であるかを取得可能です。
CoreTelephonyのドキュメントに Radio Access Technology Constants
があるので、switchを使って目的の無線テクノロジーを検出できます。
CTRadioAccessTechnologyNR
,CTRadioAccessTechnologyNRNSA
は5Gになります。
二つの違いはスタンドアローン型か非スタンドアローン型かの違いです。詳しくはWikipediaを貼っておきます。
https://ja.wikipedia.org/wiki/5G_NR#展開方式
let info = CTTelephonyNetworkInfo()
info.serviceSubscriberCellularProviders?.forEach({ (key, value) in
if let radioAccessTechnology = info.serviceCurrentRadioAccessTechnology?[key] {
switch radioAccessTechnology {
case CTRadioAccessTechnologyLTE:
print("これはLTEです")
case CTRadioAccessTechnologyNRNSA:
print("これは非スタンドアローンモード 5Gです")
case CTRadioAccessTechnologyNR:
print("スタンドアローンモード 5Gです")
default:
print(radioAccessTechnology)
}
}
})
iOS 13以下への対応
しかしながら、開発中のアプリがiOS13以降にも対応するとなると、このコードだとエラーが起きます。
'CTRadioAccessTechnologyNRNSA' is only available in iOS 14.0 or newer
ということなので、version checkのコードを追加します。
let info = CTTelephonyNetworkInfo()
info.serviceSubscriberCellularProviders?.forEach({ (key, value) in
if let radioAccessTechnology = info.serviceCurrentRadioAccessTechnology?[key] {
switch radioAccessTechnology {
case CTRadioAccessTechnologyLTE:
print("これはLTEです")
default:
if #available(iOS 14.0, *) {
switch radioAccessTechnology {
case CTRadioAccessTechnologyNRNSA:
print("これは非スタンドアローンモード 5Gです")
case CTRadioAccessTechnologyNR:
print("スタンドアローンモード 5Gです")
default:
print(radioAccessTechnology)
}
} else {
print(radioAccessTechnology)
}
}
}
})
ここから罠
実はこのコード、iOS 14.0系のOSで実行するとクラッシュします。
理由はCTRadioAccessTechnologyNRNSAとCTRadioAccessTechnologyNRの部分で、
実はこの二つの定数はiOS 14.1からのAPIであるとの記載がWebのドキュメントにあります。
しかしながら、Xcodeで定義にジャンプするとこれらのAPIは @available(iOS 14.0, *)
でマークされています。
このため、対応OSと定義で不整合が起きて、iOS 14.0に本来無いAPIに触ろうとしてクラッシュするわけです。
対策
#available
の部分を iOS 14.1に変更しましょう。
これで本来APIが使えるOSだけがAPIに触ることになります。
let info = CTTelephonyNetworkInfo()
info.serviceSubscriberCellularProviders?.forEach({ (key, value) in
if let radioAccessTechnology = info.serviceCurrentRadioAccessTechnology?[key] {
switch radioAccessTechnology {
case CTRadioAccessTechnologyLTE:
print("これはLTEです")
default:
if #available(iOS 14.1, *) {
switch radioAccessTechnology {
case CTRadioAccessTechnologyNRNSA:
print("これは非スタンドアローンモード 5Gです")
case CTRadioAccessTechnologyNR:
print("スタンドアローンモード 5Gです")
default:
print(radioAccessTechnology)
}
} else {
print(radioAccessTechnology)
}
}
}
})
ちなみに
Xcode Version 12.5 beta 3 (12E5244e)だと、この問題は解決されていて、CTRadioAccessTechnologyNRNSAとCTRadioAccessTechnologyNRは @available(iOS 14.1, *)
でマークされています。