Edited at
iOS2Day 13

Swiftで端末の通信キャリアを判定する

More than 1 year has passed since last update.

🎄この記事は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と合わせることで、通信キャリアの識別が可能です。


MCCの例

日本  : 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)とも呼ばれます。


MCCとの併記例(後半の2桁がMNC)

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プロパティもしくはmobileCountryCodemobileNetworkCodeの組み合わせでキャリア判定ができます。


キャリア情報を取得するサンプルコード

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で実行すると以下のようになりました。キャリア名が日本語で取得できるのはちょっと意外です。


コンソール出力|iPhone(Softbank)の場合

carrierName:  ソフトバンク

mobileCountryCode: 440
mobileNetworkCode: 20
isoCountryCode: jp
allowsVOIP: true

MVNO(仮想移動通信業者)の場合や、音声通話できないiPadだとどうなるのでしょうか?

次に、MVNOのmineo(ドコモプラン)のSIMカードを入れたiPadでも実行してみます。


コンソール出力|iPad(mineo/ドコモプラン)の場合

carrierName:  ドコモ

mobileCountryCode: 440
mobileNetworkCode: 10
isoCountryCode: jp
allowsVOIP: true

carrierNameやMCC/MNCはNTTドコモのものが表示されています。またiPadでは音声通話はできませんが、VoIP契約済みSIMカードの場合、allowsVOIPはtrueとなるようです。


carrierNameで取得できる文字列の例

Web上の記事等で確認すると、carrierNameで取得できる文字列は以下のようなものになるようです。これ以外にもあればコメントに書いて頂ければ随時更新します。


carrierNameで取得できる文字列の例

// ソフトバンク

"ソフトバンク"

// ソフトバンクモバイル
"ソフトバンクモバイル"

// NTTドコモ
"ドコモ"

// au
"KDDI"

// Y!mobile
"ワイモバイル"


おそらく出力される文字列は設定アプリのキャリア情報(一般 > 情報 > キャリア)に表示されているものと同じになりそうです。


SIMカードを抜いているとどうなる?

SIMカードを抜いて実行した場合、最後に読み取ったSIMカードの情報が一部残るようです。mobileCountryCode、mobileNetworkCode、isoCountryCodeプロパティはnilが返ってきました。これらのプロパティがnilかどうかをチェックすることで、現在SIMカードが挿入されているかもチェックできそうです。


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クラスを実装してみました。


キャリア判定を行う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カードに変更したことを検知できます。


subscriberCellularProviderDidUpdateNotifierの定義

var subscriberCellularProviderDidUpdateNotifier: ((CTCarrier) -> Void)? { get set }



subscriberCellularProviderDidUpdateNotifierの実装例

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)


参考