Xcode14.3(iOS 16.4 SDK)以上の環境の場合、このキャリア判定の方法は非推奨となりました。
iOS 16 CTCarrier deprecation
https://developer.apple.com/forums/thread/714876
🎄この記事はiOS Advent Calendar 2017 その2の13日目の記事です🎄
概要
CoreTelephony.frameworkを利用し、iOS端末の通信キャリアを判定をするTipsです。Web上であまりまとまった記事がないので、自分なりに整理してみました。
以下の内容をまとめています。
- CoreTelephony.frameworkの概要
- MCCとMNCってなに?
- キャリア情報を取得するサンプルコード
- MVNOだとどうなる?
- SIMカードを抜いているとどうなる?
- キャリア判定を行うUtilクラス
- 別のSIMカードに変えた場合の検知
CoreTelephony.frameworkとは
ユーザー端末の通話機能や通信キャリア(通信プロバイダ)に関する情報を取得するためのフレームワークです。
主な用途としては、通信キャリアが自サービスの加入者のみにサービス提供する場合に利用されます。
CoreTelephony.frameworkを利用すると、以下のような情報が取得できます。
- 通話状態(発信中、着信、接続、切断)
- VoIP通話を行えるかどうか
- 通信キャリア名
- 通信キャリアのISO国名コード
- 通信キャリアのMobile Country Code(MCC)
- 通信キャリアのMobile Network Code(MNC)
MCCとMNC
MCCとMNC、あまり耳慣れないキーワードがでてきました。
先にこのMCCとMNCについて簡単に説明します。
ざっくりいうと、通信キャリアの識別用に割り当てられた番号です。
Mobile Country Code(MCC)
通信キャリアの運用地域を示す3桁の番号のこと。
日本の場合は440もしくは441が割り当てられています。
後述するMNCと合わせることで、通信キャリアの識別が可能です。
日本 : 440, 441
中国 : 460
アメリカ: 544, 310, 311
各国のMCCの割当はこちらのWikiから確認できます
https://en.wikipedia.org/wiki/Mobile_country_code
Mobile Network Code(MNC)
通信キャリアを識別するための2桁の番号のこと。
各国の通信キャリアのMNCと区別するため、MCCと合わせて表記されます。
※MCCとMNCの組み合わせはPLMN(公衆陸上移動体ネットワーク番号|Public Land Mobile Network Number)とも呼ばれます。
Y!Mobile: 44000, 44110
UQ WiMAX: 44001
IIJmio : 44003
NTTドコモ : 44010
Softbank: 44020, 44021, 44101
au : 44050〜44054, 44070〜44076, 44078
MNCは通信キャリアで1つでなく、auやソフトバンクは複数のMNCが割り当てられているようです。
MNCが複数あるキャリアの場合、考慮漏れや今後エリア拡大することを想定すると、MNCからキャリア判定を行うのはあまり現実的でなさそうです。
キャリア情報を取得してみる
キャリア情報を取得するサンプルコードは以下のようになります。
ユーザーの通信キャリア情報を取得するにはCTTelephonyNetworkInfo
クラスのsubscriberCellularProvider
プロパティ(CTCarrier
クラス)を利用します。
CTCarrier
クラスのcarrierName
プロパティもしくはmobileCountryCode
とmobileNetworkCode
の組み合わせでキャリア判定ができます。
import UIKit
import CoreTelephony
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// subscriberCellularProvideから通信キャリア情報(CTCareerクラス)が取得できる
guard let provider = CTTelephonyNetworkInfo().subscriberCellularProvider else { return }
// キャリア名
print("carrierName: ", provider.carrierName ?? "")
// MCC
print("mobileCountryCode: ", provider.mobileCountryCode ?? "")
// MNC
print("mobileNetworkCode: ", provider.mobileNetworkCode ?? "")
// ISO国名コード
print("isoCountryCode: ", provider.isoCountryCode ?? "")
// VoIP通話を行えるかどうか
print("allowsVOIP: ", provider.allowsVOIP)
}
}
実行結果
SoftbankのSIMカードを入れたiPhoneで実行すると以下のようになりました。キャリア名が日本語で取得できるのはちょっと意外です。
carrierName: ソフトバンク
mobileCountryCode: 440
mobileNetworkCode: 20
isoCountryCode: jp
allowsVOIP: true
**MVNO(仮想移動通信業者)**の場合や、音声通話できないiPadだとどうなるのでしょうか?
次に、MVNOのmineo(ドコモプラン)のSIMカードを入れたiPadでも実行してみます。
carrierName: ドコモ
mobileCountryCode: 440
mobileNetworkCode: 10
isoCountryCode: jp
allowsVOIP: true
carrierNameやMCC/MNCはNTTドコモのものが表示されています。またiPadでは音声通話はできませんが、VoIP契約済みSIMカードの場合、allowsVOIPはtrueとなるようです。
carrierNameで取得できる文字列の例
Web上の記事等で確認すると、carrierNameで取得できる文字列は以下のようなものになるようです。これ以外にもあればコメントに書いて頂ければ随時更新します。
// ソフトバンク
"ソフトバンク"
// ソフトバンクモバイル
"ソフトバンクモバイル"
// NTTドコモ
"ドコモ"
// au
"KDDI"
// Y!mobile
"ワイモバイル"
おそらく出力される文字列は設定アプリのキャリア情報(一般 > 情報 > キャリア)に表示されているものと同じになりそうです。
SIMカードを抜いているとどうなる?
SIMカードを抜いて実行した場合、最後に読み取ったSIMカードの情報が一部残るようです。mobileCountryCode、mobileNetworkCode、isoCountryCodeプロパティはnilが返ってきました。これらのプロパティがnilかどうかをチェックすることで、現在SIMカードが挿入されているかもチェックできそうです。
carrierName: ドコモ
mobileCountryCode:
mobileNetworkCode:
isoCountryCode:
allowsVOIP: true
公式ドキュメントにも以下の記載がありました。端末が機内モード、SIMカード未挿入、端末が圏外の場合はnilが取得できるようです。
The value for this property is nil if any of the following apply:
- The device is in Airplane mode.
- There is no SIM card in the device.
- The device is outside of cellular service range.
キャリア判定を行うUtilクラス
carrierNameからキャリア判定を行うUtilクラスを実装してみました。
import CoreTelephony
final class NetworkInfo {
enum Career: String {
case softbank = "ソフトバンク"
case softbankMobile = "ソフトバンクモバイル"
case docomo = "ドコモ"
case au = "KDDI"
case yMobile = "ワイモバイル"
}
private static var provider: CTCarrier? {
return CTTelephonyNetworkInfo().subscriberCellularProvider
}
/// 最後にアクティベートしたキャリア(判定不可な場合はnilを返す)
static var latestActivatedCareer: Career? {
return Career(rawValue: provider?.carrierName ?? "")
}
/// 現在のキャリア名(判定不可、もしくはSIMカードが挿入されていない場合はnilを返す)
static var currentCareer: Career? {
guard hasSIMCard else { return nil }
return latestActivatedCareer
}
/// SIMカードが挿入されているか(機内モード、圏外の場合はfalse)
static var hasSIMCard: Bool {
return provider?.isoCountryCode != nil
}
}
以下のようにSwitchやifで分岐が可能です。
// キャリアによって処理を分岐する
guard let currentCareer = NetworkInfo.currentCareer else { return }
switch currentCareer {
case .softbank: break
case .softbankMobile: break
case .docomo: break
case .au: break
case .yMobile: break
}
// auかY!mobileの場合のみ
if currentCareer == .au || currentCareer == .yMobile {
// 何か
}
別のSIMカードに変えた場合の検知方法
CTTelephonyNetworkInfo
クラスの以下のクロージャを設定することで、ユーザーが別のSIMカードに変更したことを検知できます。
var subscriberCellularProviderDidUpdateNotifier: ((CTCarrier) -> Void)? { get set }
import UIKit
import CoreTelephony
class ViewController: UIViewController {
private let networkInfo = CTTelephonyNetworkInfo()
override func viewDidLoad() {
super.viewDidLoad()
networkInfo.subscriberCellularProviderDidUpdateNotifier = {
(career: CTCarrier) in
print("キャリア情報が変更されました: \(career)")
}
}
}
以下のように、SIMカードを別のSIMカードに入れ替えるとクロージャが実行されました。
なお、同じ通信キャリアのSIMカードを抜き差ししてもクロージャは実行されないようです。
キャリア情報が変更されました: CTCarrier (0x1c4258810) {
Carrier name: [ソフトバンク]
Mobile Country Code: [440]
Mobile Network Code:[20]
ISO Country Code:[jp]
Allows VOIP? [YES]
}
まとめ
- iOS端末の通信キャリア情報はCoreTelephony.frameworkで簡単に取得できる
- MMC+MNCよりも、シンプルにキャリア名(carrierName)を取得したほうが扱いやすい
- SIMカードを抜いた状態だと、最後に読み取ったキャリア名が取得される
- SIMカードが挿入されていないことも判定できる(isoCountryCodeがnil)
- 別キャリアのSIMカードに変更されたことも検知できる(subscriberCellularProviderDidUpdateNotifier)