自宅Wi-Fiお知らせアプリ開発【第四回】 Wi-Fiお知らせ機能編

この記事は「Houwa System Design Advent Calendar 2018」の2日目の記事です

開発部のなーさんです。
みなさんスマホアプリ作ってますかー?
12月は飲み会が増える時期ですが、なーさんは「俺はアプリを作る!」とばかりに誘いを振り切って帰宅する…
と思いきや、飲み会の誘惑には勝てませんでした(笑)
でもこのブログを書き始めてアプリを作る時間が増えました。
さて、普段はWEB系の開発をしている私が「家庭で使える」をテーマに突如はじめた、自社製品を使ったスマホアプリ作成チャレンジブログ、ラストとなる第4回は、自宅Wi-Fiお知らせアプリの完成に向けて、キモであるWi-Fiお知らせ機能の実装を行っていきます。

下記は2018年10月現在の情報で執筆しています。
文中、画像中で使用されている商標等はそれぞれ企業、団体に帰属します。
コンセプト実証用のソースコードのため簡略化等された箇所があります。

前回の要点

CoreLocation.frameworkを使ったiBeacon探索の実装を行いました。
自宅Wi-Fiお知らせアプリ開発【第三回】 Beacon 探索機能編 その2
自宅Wi-Fiお知らせアプリ開発【第二回】 Beacon 探索機能編
自宅Wi-Fiお知らせアプリ開発【第一回】準備編
今回開発に使用した環境を記載します。
●ハードウェア
iMac2017(macOS 10.14)
iPad Air2 (iOS12.0.1)SIMなし
BLEAD-B 1台
Wi-Fiルータ 1台
●ソフトウェア
Xcode 10.0 + Swift4.2
Apple developer Programアカウント ※HotSpotの許可をONにするために必須です。
プロジェクト形態はSingle View Appで、アプリ名は「Wi-FiNotify」としました。パッケージ名は任意のものを記入してください。
自宅Wi-Fiお知らせアプリの完成イメージです。

NENetworkExtension.framework

前回までに実装したiBeaconの探索処理と連動させて動くように、Wi-Fi接続をお知らせする機能を実装していきます。まず機能について整理したいと思います。
この機能を実現するために使うのが、NENetworkExtension.frameworkです。このフレームワークもたくさん機能が詰まっています。その中で今回実現したい機能には、「NEHotspot〜」で始まるクラスを使います。
近年国内でも整備が進んできたHotSpot2.0に接続するために使うことが主眼のようですが、個別に設定したSSIDに接続することも可能です。
処理の流れですが、
CoreLocationで指定したiBeaconが検知されたら、画面で設定されたSSIDやパスワードを読み取って、Wi-Fi接続を促すダイアログを出現させる処理を呼び出します。基本はこれだけです。Wi-Fiがオフになっていても、自動でオンにしてくれます。ダイアログの生成や「接続」ボタン・「キャンセル」ボタンの処理もおまかせなので、とても便利です。

ダイアログの各ボタンを押した時の挙動を試したところ、下記のようになりました。

ボタン挙動
「接続」ボタン指定されたSSIDに接続を試みます。
無事に接続されたときは特に何も表示されません。
失敗したときは「接続できません」と表示されます。
周辺に実在しないWi-FiスポットのSSIDやパスワードでも、ダイアログは出ますが、当然接続できません。
「キャンセル」ボタン接続しないままダイアログは消えます。

「キャンセル」ボタンについては、今回のアプリでは、iBeaconが検知されている間は何度もダイアログが出現してしまうので、最大3回キャンセルされたらそれ以上は表示しない処理を入れました。バックグラウンド状態では、3回出ないこともあります。
今回は省略しましたが、実際にWi-Fiに接続されたかを確認するコードを入れた方が安全かもしれません。

インポート

import NetworkExtension

クラス

名前機能
NEHotspotConfigurationManager登録したWi-Fiスポットとの接続・除去の管理
NEHotspotConfiguration接続したいWi-Fiスポットの設定を格納

メソッド ー NEHotspotConfigurationManager

名前機能
apply()接続を促すダイアログを出現
removeConfiguration()※今回は使いません
直近で接続していたWi-Fiスポットの情報を除去

プロパティ ー NEHotspotConfigurationManager

名前機能
NEHotspotConfigurationManager.sharedシングルトンのインスタンスを取得

プロパティ ー NEHotspotConfiguration

名前機能
joinOnce接続に関する制限をするか
Trueにすると、フォアグラウンド状態のときしか接続しない等の制限ができる
lifeTimeInDays設定を何日保持するか(1~365日で設定)
NetworkExtensionなどの設定

お知らせ機能に関係する部分のプロジェクトの設定を再掲します。

使用する Capabilities

オンにする項目用途
Hotspot ConfigurationWi-Fi接続を促す機能を使うために必要な許可

Capabilitiesの画面を下までスクロールして「Advanced App Capabilities」と表示されているときは、Hotspot Configurationの許可ができないので、Apple developer Programに参加してください。

画面オブジェクトの実装

SSIDとパスワードは画面から設定できるようにしたので、いくつかオブジェクトを配置します。接続ダイアログが出現する直前に設定値が読み取られるようになっています。
Beaconの探索は、アプリ起動時に自動でスタートするので画面からは制御しません。
配置イメージはこのようになります。

各オブジェクトの種類や設定は下記のようになります。

Label

ラベルを3つ配置します。それぞれの表示を下記のようにしてください。

  • 自宅Wi-Fiお知らせアプリ
  • 接続したいWi-fiスポットのSSID
  • パスワード

Text

SSIDとパスワードを毎回設定するのは面倒なので、初期値として記入してください。初期値の代わりにUserDefaults等を使って入力値の保存と読み出し機能をつけるのもいいと思います。

  • SSID入力欄

Textボックスを画面によしなに配置します。Outletを設定するため、コネクションインスペクター画面でReferencing OutletsのNew Referencing OutletからView Controllerに接続して、NameをtxtSSIDに設定します。

  • パスワード入力欄

同様にReferencing Outlets-New Referencing OutletからView Controllerに接続して、NameをtxtPwdに設定します。またパスワードを直接表示しないようにアトリビュートインスペクター画面でText Input TraitsのSecure Text Entryにチェックを入れます。

ソースコードの実装

各種定義の追加

ViewController.swiftの先頭に、NetworkExtension.frameworkをインポートします。
前回までのソースコードに継ぎ足して、先頭部分を以下のようにしてください。

Privateメソッドの定義

ViewController.swiftに直接実装します。機能をメソッドとしてまとめて、該当のBeaconが検知されたときに呼び出します。ソースコードの一番下に追加します。

名前機能
notifyMyWifiSpot()Wi-Fi接続の通知

NEHotspotConfigurationクラスをインスタンス化するときに、接続したいSSIDやパスワードを画面から読み込んで引数にして初期化します。今回はWEPを使わないので、isWEPにはfalseを指定します。
先頭では、SSIDやパスワードが空欄かどうかをチェックしています。

デリゲートへのコード追加

notifyMyWifiSpot()を呼び出すためにdidRangeBeaconsデリゲート内に処理を記述していきます。Beaconが見つかったら呼び出しますので、コメント文「// ここでWi-Fi接続ダイアログ表示を呼び出す」の下に追記してください。
もし、複数のBeaconを探索しているのであれば、ここでMajor/Minorなどの値を使ってnotifyMyWifiSpot()を実行させたいBeaconをフィルタリングしてください。

ここまで出来たら、試しに動かしてみましょう!!
位置情報は「常に許可」を選択してください。フォアグラウンドで動作したら、バックグラウンドやアプリをキルした状態での動作も試してみてください。

エラーが出て動きません!というときは
なーさんがアプリ作成をしていたとき、最初はエラーが出て期待通りの動作がなかなかできませんでした。Beaconの検知に成功し、ダイアログを出そうとしているところまでは動いているようなので、SSIDの書き間違いやHotspotの利用許可を確認したりしました。
それでも一向に解消せず、結局下記の対応を試すうちに動作するようになりましたので、ご参考までに。
・スマホを再起動
・アプリを一旦削除
・プロジェクトをクリーン&ビルド
接続ダイアログの出現回数制限機構の実装

うまくBeaconと接続ダイアログが連携して動作できたでしょうか?
既にお気づきかもしれませんが、実はBeaconを検知している間はWi-Fiに接続するまで何度も接続ダイアログが出現します。キャンセルボタンを押しても何度も出てくるのでさすがうっとうしいですね。
そこでもう一仕事します。
一度Beaconを検知したあと3回ダイアログが出たら以降は、再度Beaconを検知しなおすまでダイアログを出さないようにしました。間違ってキャンセルボタンを押すことも考慮して3回の余裕を持たせています。なお、バックグラウンド状態では、Beaconを検知してから10秒間しか検知しないので、3回未満のこともありえます。
今回はRegion 1つでBeacon 1台を管理しているだけなので、素直にメンバ変数としてカウンターを定義して管理します。
●カウンターが0回に初期化されるとき

場所内容
アプリが起動したとき一般的な初期化
Beaconのレンジングが終了したときBeaconが見当たらなくなったとき

●カウンターがカウントされるとき

場所内容
Wi-Fi通知ダイアログの「接続」ボタンが押されたときWi-Fi接続に失敗したときのカウント
Wi-Fi通知ダイアログの「キャンセル」ボタンが押されたときキャンセルされたときのカウント

ソースコードの実装

メンバ変数として、_countCancelを定義してください。また、viewDidLoad()の冒頭で初期化してください。

●didDetermineStateデリゲート
state == .outsideのとき(Regionが範囲外になったとき)にカウンタをリセットします。

●notifyMyWifiSpot()
接続ダイアログの表示回数チェック、接続ダイアログ表示回数のカウントを行います。
キャンセル回数をカウントする条件が非常にイケてないのですが、キャンセルされたときのエラーコード7のみをGETできないので苦肉の策です。

ここまで実装できたら、ふたたび動作確認してみてください。表示回数が制限できているでしょうか?もしできていたら、次はバックグラウンドやアプリキル状態でも接続ダイアログが表示されるでしょうか?
そこまでいければ完成です!
おめでとうございます!

あとがき

以上で、なーさんによるBeaconアプリ作成チャレンジ「自宅Wi-Fiお知らせアプリ」編は完了となります。
しばらく実際に使用していますが、毎日帰宅してスマホを見ると「Wi-Fiに接続しますか?」と聞いてくれて助かります。そして、今回作ったアプリはほぼ最低限の機能しかついていないので、接続したときに音を出すとかBeaconの探索やりなおし機能をつけるとか、いろいろ創意工夫してみたいなと感じました。
「Beacon買ったけど〜、使い道ないよ〜」「位置情報じゃない使い方ないの〜」というみなさんのアイディアの一助となれば、なーさんはテンション↑↑↑です。
お付き合いありがとうございました。
以上、ご質問・ご指摘等ありましたら、弊社までお気軽にお問い合わせください。

関連記事

  1. BLEAD-TSHで遊ぼう!【作例集 モーターの無線遠隔制御編】

  2. ねぇClova、開発したい 【サーバアプリ実装編】

  3. Elastic Stack を使った予兆検知結果の可視化 〜Logst…

  4. 電波の届く範囲内だけコンテンツの閲覧を可能に

  5. BLEAD-TSHで遊ぼう!【作例集 サイコロ型IoTデバイス編】

  6. Beaconアプリ開発記【その3 情報取得編】