ねこ島

iOSのこととか、日常について雑に書きます

v6プラス環境下でもWireGuardを使って自宅VPNサーバーを構築する

はじめに

コロナ禍が落ち着き出社や外出する機会が増え、外にいる時間が長くなってきたように感じます。 外出時はモバイル回線を利用するので外部からのアクセスを許容しないNASSSHが利用不可となり不便です。これを解決するためにVPNが必要になります。

当初は手軽に利用できたのでTailscaleを使っていましたが、接続のピア数も現状モバイル一つだけという状況でそこまで高機能である必要がなく、前々からVPNサーバーを建ててみたいという気持ちもありました。

そこで今回、HomeBridgeやらモニタリングツールやら色々管理するために利用している我が家のRaspberry PiくんにサクッとWireGuardを導入して外出先から利用できるようにしてみました。

環境

  • v6プラスを契約したフレッツ光回線
  • VPNサーバーを構築するマシン
    • Raspberry Pi 4 (8GB)を利用します
    • docker composeが導入済み前提です

なぜWireGuardを使うのか

様々な種類のVPNプロトコルが存在しますが、今回選定に当たって重視すべきポイントは以下の3つ。

  • 通信ポートの変更が可能かどうか
  • 安全性・セキュリティが確保されているか
  • モバイルで利用しやすいか(サポートされているか、専用Appが用意されているか)

特に一番上の通信ポートの変更が可能かどうかは自宅ネットワーク上で構築する上では必須の要件でした。

なぜなら自宅回線ではv6プラスを契約しているため、使えるポート番号に限りがあります。 追加費用を払って固定IPを契約していれば自由にポート番号が使えますが、そうでなければ他の回線契約者と共有のグローバルIPv4アドレスと決まったポート番号が割り当てられます。

この決まったポート番号というのが厄介で、まずWell knownポートは割り当てられません。つまりWebサーバーの公開や、IPSecはv6プラス環境下において利用できません。

じゃあ1024番以降の固定ポート番号を要求するアプリケーションは使えるのかというとそうでもなく、どのポート番号が使えるかは特定の計算方法で決まるため、基本的にこちら側からポート番号を指定できる必要があります。

これらの条件を満たすVPNプロトコルOpenVPNとWireGuardの二択になり、パフォーマンスの観点でより高速なWireGuardを使うことを決めました。

構築手順

1. 利用可能なポート番号を把握する

VPNサーバーは外部からリクエストを受けるためにポート開放する必要がありますが、前項でも記述したとおり、v6プラス上ではポート番号が決まっているため事前にどのポート番号が利用できるか把握しておく必要があります。

どのポート番号が利用可能かどうかを調べるためには、以下のサイトを利用することで簡単にわかります。

isecj.jp

利用可能なポート番号がいくつか出てくるのでどれか一つを選んでメモっておきます

2. ポート開放する

筆者はYAMAHA製のNVR510を利用しているためそちらの管理画面を例に紹介します。 ルータによってポート開放方法が異なるため、よくわからない場合は「お手持ちのルータ名 + ポート開放」でググってください

1. ダッシュボードの詳細設定 > NATを開き、NATディスクリプターの設定へ進む

2. 静的IPマスカレード設定に以下のパラメータを追加する

  • 内側アドレス: VPNサーバーのプライベートIPアドレス
  • プロトコル: udp
  • ポート番号: 利用可能なポート番号を把握する で決めたポート番号

設定後こんな感じになっていればOKです

これでルータ側の調整はおしまいです。次に実際にWireGuardを導入していきます。

3. [If needed] Linuxカーネルを5.6以降に引き上げる

Linuxカーネルバージョン5.6からWireGuardモジュールがサポートされており、Docker ComposeでWireGuardを立ち上げるときにモジュールが有効かどうかのチェックが行われます。バージョンを確認して必要であればアップグレードしましょう。

カーネルバージョンは以下のコマンドで確認できます

owen@rp:~ $ uname -a
Linux rp 6.1.21-v8+ ...

アップグレードする場合はaptを叩きます

owen@rp:~ $ sudo apt update
owen@rp:~ $ sudo apt full-upgrade

4. Docker ComposeでWireGuardを立ち上げる

筆者はローカル環境を汚すのを嫌うため、アプリケーション系をコンテナ管理にしています。 docker-compose.ymlを作成し、以下のように記述していきます

version: "3"
services:
  wireguard:
    image: lscr.io/linuxserver/wireguard:latest
    container_name: wireguard
    cap_add:
      - NET_ADMIN
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Asia/Tokyo
      - SERVERURL=auto
      - SERVERPORT=xxxxx
      - PEERDNS=xxx.xxx.xxx.xxx
      - PEERS=1
      - LOG_CONFS=true
    volumes:
      - ./config:/config
    ports:
      - xxxxx:51820/udp
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    restart: unless-stopped

それぞれパラメータについて解説します

environment

  • PUID, PGID
    • コンテナの実行ユーザーIDを指定します
  • TZ
  • SERVERURL
    • サーバーの接続先ドメインを指定できます。筆者はIPv4アドレスを直打ちで使用しているのでautoにしているが、DDNSなどを利用していればここへ
  • SERVERPORT
    • 接続先ポート番号を指定する。前項で決めたポート番号を入力する
  • PEERDNS
    • DNSサーバーの指定があればここに。筆者はルータが持つRecursive DNSを利用しているためルータのIPアドレスを入力しています
  • PEERS
    • ピア(クライアント)の数を指定します。ここは必要台数分指定します
  • LOG_CONFS
    • 設定用のQRコードをログに吐き出すかどうかを設定できます。セットアップで使いたいのでtrue

ports

  • (SERVERPORT):51820/udp
    • サーバー側で待ち受けるポート番号と、コンテナ内部のWireGuardが待ち受けるポート番号をマッピングします
    • ここが漏れてると疎通しないので要注意

sysctls

  • net.ipv4.conf.all.src_valid_mark=1
    • デフォルトでついてたやつ。よくわからなかったので調べてみると、fwmarkとReverse Path Filtering(RPF)を有効にするためのパラメータとのこと

src_valid_mark - BOOLEAN

0 - The fwmark of the packet is not included in reverse path route lookup. This allows for asymmetric routing configurations utilizing the fwmark in only one direction, e.g., transparent proxying.

1 - The fwmark of the packet is included in reverse path route lookup. This permits rp_filter to function when the fwmark is used for routing traffic in both directions.

This setting also affects the utilization of fmwark when performing source address selection for ICMP replies, or determining addresses stored for the IPOPT_TS_TSANDADDR and IPOPT_RR IP options.

IP Sysctl — The Linux Kernel documentation

  • ざっくりした説明としてはパケットの経路を厳密に検証するモードで、IPスプーフィングなど偽装されたパケットを破棄してくれるようになる
  • ただ環境によっては正当なトラフィックであってもパケットが破棄されてしまうケースがあるらしく、デフォルトオンになっていないのでパラメータで有効にしているっぽい

設定できたら docker compose up -d でコンテナを立ち上げます。
しばらくするとログに設定用のQRコードが吐かれるので、docker compose logs でログを確認しましょう

5. モバイルアプリから接続する

有り難いことにAppStoreに専用のアプリケーションが公開されているのでそちらをインストールします。

WireGuard

WireGuard

  • WireGuard Development Team
  • ユーティリティ
  • 無料
apps.apple.com

起動したら右上の+ボタンから前項で確認したQRコードを読み取ります。 プロファイルが作られるのでこれで一旦接続できるか確認してみてください。

6. [If needed] VPNオンデマンドを有効にする

iOSにはWiFi, モバイル回線の接続状態に基づいてVPN接続・切断を自動的に行える仕組みがあります。 WireGuardもこの仕組みに対応しており、プロファイルの編集から設定が可能です。

具体的には以下のように設定できます

  • モバイル回線接続時にVPNへ接続する
  • Wi-Fi接続時にVPNへ接続・切断する
    • 特定のSSIDに接続した場合、VPNを切断するなど

この機能を活用すると、例えば自宅のWiFiに繋がっている間はVPNを切断しておき、外出時にモバイル回線あるいはホットスポットに接続した時はVPNを接続するといった処理を自動で行えるようになるのでおすすめです。

感想

WireGuardを利用し始めて半月ぐらいが経つのですが、ストレスなく利用できて快適です。最初は非力なRaspberry Pi上に構築して大丈夫だろうかと思ってたんですが、軽量なだけあってハングアップすることなく稼働してくれています。

ポート解放の手間だけありますが、それさえ乗り越えてしまえばサクッと導入できて便利なのでこれから自宅VPNの導入を考えている方にぜひお勧めです👍

Kyash iOSアプリのビルド時間を短縮する取り組み

この記事は、 Kyash Advent Calendar 2022 の9日目の記事です。

はじめに

KyashでiOSエンジニアをやってます、@nekowenです。
Kyashモバイルチームの目標の一つに 生産性の向上 があります。
「今後会社の求める事業の成長スピードにモバイルチームが柔軟にスケールしていける状態を作りたい」という経緯があり、今の開発の障壁を減らしていくことを想定しています。
ビルド時間の短縮はその一環として取り組んでいるもので、今回はその内容について紹介いたします。

なぜ取り組むのか 🤔

モバイルの開発では基本、レイアウトの調整や動作の確認のため1日の間に数回〜数十回程度ビルドを行います。
最近ではSwiftUIの導入が進み、プレビューでの開発で事足りるケースもありますが、それでも初回及びSwiftUI View以外を変更した場合もビルドが必要になります。

このビルドにかかる時間はプロダクトの規模によって異なりますが、画面数やロジック、必要となるライブラリなど依存関係が増えれば増えるほど1回にかかるビルド時間が増えてしまいます。

Kyash iOSアプリも例外ではなく、フルビルドで5〜7分、差分ビルドで2〜3分程度時間がかかってしまっていました。 1時間に4〜5回差分ビルドするとしても最大で15分かかります。そうなると開発時間の1/4をビルド時間で埋めてしまう計算になり、そこそこの割合を占めていることがわかります。

待ち時間が長いほど機能開発の時間が伸びてしまいますから、これはチーム内の課題としてビルド時間を短縮していけるように改善していく方針となりました。

調査と対応方針 🗒

「ビルドに時間がかかっている」といっても、どの部分に手を入れれば良いでしょうか? XcodeにはBuild Phase毎にかかった時間をアウトプットしてくれる Build with Timing Summary があるのでこちらを使用しました。

Product -> Perform Action -> Build with Timing Summary を選択するとビルド時に測定してくれます。

実行すると以下のようにレポートが吐き出されます。

CompileC (613 tasks) | 628.295 seconds
CompileSwiftSources (36 tasks) | 323.218 seconds
CompileStoryboard (220 tasks) | 114.536 seconds
CompileXIB (273 tasks) | 95.150 seconds
...

これはフルビルドの場合で、差分ビルドの場合はこのようになりました。

SwiftEmitModule (1 task) | 34.190 seconds
SwiftDriver Compilation (1 task) | 33.775 seconds
SwiftDriver (1 task) | 4.711 seconds
...

なお、各項目の時間表記は「タスクの処理にかかった総時間」を表しています。実際のビルドではタスクが並行処理されるため表記の時間より早く完了します。

このように数値でみると、どの項目で時間がかかっているのかがはっきりとわかるため対策を打ちやすくなります。
今回の場合はCompileC, CompileSwiftSourcesに焦点を当てればよいことがわかりました。

CompileC

ログを見ると、Kyashで利用しているFirebase SDKが含むObj-Cのソースコードコンパイルが半数ほどを占めていました。 これは他のプロジェクトでもちらほら見受けられるのですが、Firebase SDKは多くの機能を持っているため、初回ビルドに時間がかかる傾向にあります。

そのためKyashではソースコードからのビルドではなく、Firebaseが提供するXCFrameworkを扱うことでビルドそのものを無くすことにしました。 XCFrameworkの管理にはCarthageを使ってバージョン管理を行う方法、FirebaseのGithub Releasesから直接ダウンロードする方法がありますが、まずはお試しとして直接ダウンロードして利用してみています。

これによりビルドタスクが半減し、ビルド時間としては30〜60秒ほど短縮できました 🎉

CompileC (613 -> 399 tasks) | 628.295 -> 162.848 seconds

CompileSwiftSources

アプリのコード・ライブラリ含めSwiftコードコンパイル系がこの項目にまとめられています。 ログを見る限り、Kyash iOSアプリのコードでちらほらコンパイルに時間がかかっているように見えました。

そこでメソッドと型推論にかかる時間を全て洗い出すことにしました。 OTHER_SWIFT_FLAGSdebug-time-expression-type-checkingdebug-time-function-bodies を指定することで、ビルドログの中にコンパイルにかかった時間を追記させることができます。

このデバッグ機能を利用して、コンパイル時間が長い順にテキストを吐き出させるようにしました。

xcodebuild -workspace HogeApp.xcworkspace \
-scheme HogeApp \
-destination 'platform=iOS Simulator,name=iPhone 11 Pro Max' \
clean build \
OTHER_SWIFT_FLAGS="-Xfrontend -debug-time-expression-type-checking \
-Xfrontend -debug-time-function-bodies" | 
grep "[0-9]*\.[0-9]*ms" |
sort -nr |
uniq > result.txt

その結果、RxSwiftを利用している一部の複雑なコードで型推論30秒(!)ほどかかっているメソッドが見つかったので、明示的に型を指定することでこれを0.3秒以下に抑えることができました。

今後同じことが起きないように、型推論に300ms以上かかった場合にWarningを表示するようにしました。
設定方法としては、OTHER_SWIFT_FLAGS-Xfrontend -warn-long-expression-type-checking=300 を追加すると動作します。

Compile XIB, Storyboard

Interface Builderを用いて作られたStoryboardやXIBファイルのビルドにも一定時間がかかっていました。 ただKyashでは既存UIも含めてSwiftUIへの移行を進めており、この項目はいずれ改善されていく見込みです(その分Compile Swiftに寄るんじゃないかとは思ってますが。。。)

UIマルチモジュール化計画

こちらはまだ計画途中ではあるのですが、UI部分のマルチモジュール化を検討しています。 Kyash iOSアプリは元々以下のようなマルチモジュール構成で作られています。

[Kyash iOS]
L KyashUI
L KyashUseCase
L KyashRepository
L KyashEntity
L KyashUtility
...

この中でKyashUIは全てのレイアウトや機能を保持するため一番大きなモジュールとなっており、また機能開発による画面の追加などで肥大化が進んでいます。 モジュール分割のメリットの一つとして差分ビルドが働きやすくなる点があるのですが、これはモジュールが適切に分割されている場合において有効です。

今のKyashUIでは大きなUI単位としてモジュールが切られてしまっているので、このメリットが薄れてきてしまっているように思います。 そのため依存関係を見直し、KyashUIそのものをFeature単位などで細分化できないか試しています。こちらに関しては進捗があり次第別途記事を書こうと思います。

M1(Max) Macへの切り替え

ビルド時間の改善に向けて進めていたところ、M1 Macに切り替える機会がありました。 これはM1 MacのパフォーマンスがIntel Macより格段に高いことが知らされていたので、生産性向上にある程度貢献できそうという観点から会社側がモバイルチームに優先的に支給してくれました。

支給されたM1 Macのスペックは

  • チップ: Apple M1 Max
  • メモリ: 64GB
  • ストレージ: 1TB

となかなかハイスペックな構成で組まれていました。

気になるビルド時間への影響ですが、なんとフルビルドで2分きっかりに収まるようになりました。 計測データをとってみても数値が半減していることがわかります。

CompileC (676 tasks) | 628.295 -> 316.034 seconds
CompileSwiftSources (37 tasks) | 323.218 -> 111.771 seconds
CompileXIB (266 tasks) | 95.150 -> 27.195 seconds
CompileStoryboard (212 tasks) | 114.536 -> 25.813 seconds

あとIntel Macだとビルド中ファンが唸っていたのですが、M1 Macだとそれがほぼなくなったのが嬉しい点ですね。
少し悔しい感じもありますが、手っ取り早くビルド時間を短縮化するのであればM1 Max Macに切り替える、というのも一つの手かもしれません。

最後に

以上の取り組みにより、実際にビルド時間が短縮されたことによって快適に開発が進むようになりました。 ビルド時間は自分のみならず他のメンバーの開発効率にもダイレクトに影響する部分なので、調査・改善を継続して進めていくことが大切だと感じました。

Kyashではモバイルエンジニアを始め、プロダクトをより良いものにしていく仲間を募集中です。

興味がありましたらぜひ、ご連絡お待ちしております!

募集職種一覧 / 株式会社Kyash

BLEビーコンを検知してネコチャンのいる部屋を判定してみた

モチベーション

最近おうちを買いました。戸建てで4LDKの平均的なサイズのやつです。
今まで2LDKのマンションっぽいアパートに住んでいたので自室がめちゃくちゃ広くなって割と快適な日々を過ごしているのですが、一つ問題がありました。
それは、「ネコチャンを探すのが大変!」です。

大体どの位置にいるのかはわかるのですが、如何せん部屋の数と階層、隠れ場所が増えたことによりネコチャンがどこで過ごしているのか分かりづらくなりました。 加えて我が家のネコチャンは毎回変なところに隠れてしまうので、こうなると妻と総出で探すハメになります。 猫の位置なんてわからなくてもいいじゃんと思うかもしれませんが、万が一何かのタイミングで脱走していたらやばいですし、出かける時には猫がいることを確認してから家を出るので、毎回1Fと2Fを往復して探していたりします。

そんなわけで猫のいる位置わからん問題というのは我が家ではそこそこのレベル感で抱えていたので、今回技術でどうにかしてみたという記事になります。

どうやるか

当初は部屋のドアにSwitchbotの開閉センサを設けて、ネコチャンがドアを開閉した時に位置を確定させるやり方(我が家のネコチャンはドアを開けられます)を考えていましたが、ドアは人間も利用するため精度が微妙です。

何か良い方法はないかと考えた結果、我が家のネコチャンが利用しているCatlogのデバイスが使えそうだと判断しました。CatlogはCatlog Pendantという首輪をネコチャンにつけることでネコチャンの行動を見守ることができるIoTデバイスです。 水を飲んだ、ご飯を食べたといった細かな挙動が時系列に記録されるので、常日頃の健康チェックに欠かせない存在となっています。

このCatlog Pendantですが、ネコチャンの動きをCatlog Homeという中継機に伝えるために定期的にビーコンを発信しています。これはBLEビーコンなので、Catlog Home以外からも検知が可能です。

この仕組みを使って、BLEスキャンするハードウェアを各部屋に置いて、ビーコンを検知し、電波強度を比較すればネコチャンのいる部屋がわかるのではないかと考えました。

ちなみに今回筆者が使っている関係上Catlogを使用していますが、Catlogに限らずBLEビーコンを発信かつデバイスのアドレスを特定できればなんでも良いです。例えばMAMORIOなど落とし物タグをネコチャンにつけていればそれでも可能です(AirTagは確かその辺情報をランダム化していたような気がしていて難しそうなので除外)

我が家のネコチャンは筆者の部屋(2F洋室A)、妻の部屋(2F洋室B)、1Fのリビングの3ヶ所によく寝っ転がってます。なので、これらの部屋にビーコンを検知できるハードウェアがあればよさそうです。

準備

まずはハードウェアの選定です。当初はRaspberry Pi Zero Wが家に転がってたのでこれを後2つ追加すれば良いかと考えていましたが、現在Zero Wはほとんど在庫がなく、あっても転売価格で販売されていました。
また世界的な半導体不足が影響していてRaspberry Piシリーズそのものも在庫がないようで他のハードウェアを使うのが手っ取り早そうでした。

色々調べた結果、ATOM LiteというWi-FiBluetoothも利用できてかつ安価で小型という至れり尽くせりなマイコンが最適と判断。3つ送料込みでも4000円くらいです。
安すぎて神〜と思っていたのですがこれでも値上げして高くなった方らしく、発売当初は1個900円台で買えていたそうです。マジかい

ちなみに筆者はマイコンを触ったことがありません。M5Stackを使った技術ブログ記事をちょっと読んだことがあるレベルなんですが、このハードウェアは値段と使いやすさの面もあってかなり流通しており、ググればたくさん情報があったので開発中困ることはほとんどなかったです。

ATOM Liteが配達されるまではCatlog Pendantのデバイスアドレスを調べました。 手軽に調べる方法として、iPhonenRF Connect というアプリを使う方法があります。これでデバイスの周り飛んでいるビーコンを可視化することができます。
Scannerでアドレス値を確認しメモしておきます。この値は後ほどクライアントの実装で利用します。

あとBLE周りを触るのも初めてなので、関連知識を得ておきたくこの本を買って読みました。Bluetoothの仕様書は公式のサイトに掲載されているのでそちらを読むのが確実なんですが、如何せん量が膨大すぎて全て読むのが大変です。

この本はざっくりと全体像とそれぞれのデバイスの役割、仕様について解説してくれるので、これだけでもかなり解像度が上がりました。iOS/Androidでの実装例も載っているのでモバイルエンジニアにもおすすめです。

受信機側の実装

そんなこんなで本を読み終わる頃にATOM Liteが届いたので早速コードを書いて動かしてみました。最低限必要な要件は以下の通り。

  • BLEスキャンを継続的に行い、ビーコンを見つけたらRSSI(受信信号強度)を記録する
  • 得られたRSSIを定期的にサーバーに送信する

ざっくり書くと BLEDevice::getScan() で取得したインスタンスから setAdvertisedDeviceCallbacks を呼び出し、デバイスを見つけた時の処理を実装します。この中で事前に調べておいたBLEビーコンのデバイスアドレス(MACアドレス)と照らし合わせ、合致したらRSSIを記録します。

準備ができたらstart メソッドを呼び出しBLEスキャンを定期的に実行するだけです。しばらく待つとRSSIが配列に追加されていくので、これを別のコアで定期的にRSSIを送信するようにしています。

加えて、起動した時にSlackに起動通知を送ったり、Wi-Fi接続が切れた時に再接続する処理を追加しています。コードに関してはこちらのリポジトリにあるので興味ある方は見てみてください。

github.com

工夫した部分として、全てのデバイスからほぼ同じ時間にデータを送信するようにしました。
これはネコチャンのいる場所を判定する際に同じ条件(タイミング)で取得された最新のRSSI値を元に比較する必要があるためです。

実装としてはシンプルで、デバイス起動時にNTPサーバから最新の現在時刻を取得し、その値を元に5分毎にデータを送るようにしています。
時刻は定期的にsyncが行われていてずれることもないので、ほぼ同じタイミングでデータの送信が可能になります。

ちなみに当初はRSSI毎に取得した時間を含めてサーバーに送ることで非同期的に判定することも考えていましたが、クライアント・サーバーともに実装が複雑になりそうだったのでシンプルにできる方法として採用しています。

サーバー側の実装

お次はサーバー側の実装が必要です。今回はセットアップコストも鑑みてHerokuとPostgresを利用して構築しました。中身はGolang + Ginで動かしています。

github.com

起動時やネコチャンの判定位置の通知にIFTTT + Slackを利用しています。IFTTTを使っているのには理由があり、実は当初クライアント側から直接送るようにしてたのですが、文言を変更するときに全部のデバイスを書き換えるのが面倒でIFTTTで制御できるようにしたという経緯があります。

その後サーバーサイドにリクエストを集めることにしたのでIFTTTを使わなくても良くはなったのですが、めんどくさいのでそのままにしています。

またDB上でのビーコンの扱いとして、更新毎に古いデータを削除しています。
これは1デバイス = 1部屋なのが前提にあるのと、HerokuのPostgresは無料枠の場合10000Rowまでという制約があるためこのような仕組みにしました。ただこれだとスケールしづらいので、余裕があるならn件以上になったら消す、みたいな仕組みにしてもいいかもなと思っています。

さて、諸々準備ができたらATOM Liteにプログラムを書き込みます。起動時に受信機のWi-Fi MACアドレスをコンソールに出力するようにしているのでメモしておきます。このアドレスは受信機と受信機を置いている部屋との紐付けに利用しています。DBで管理しているので、今後デバイスを追加した時に柔軟に対応が可能な仕組みになっています。

書き込みが完了してDBの準備もできたら部屋に配置して起動します。受信機から5分おきに収集されたRSSI値がサーバーに送られてSlackに通知が飛ぶようになります。

これでネコチャンがどこにいるかわかるようになりました!めでたしめでたし

困ったこと

ここからは開発中困ったことや悩んだことを書いていきます。

[BLE] ビーコンが見つからない

これは家の構造上仕方がないのですが、ネコチャンがクローゼットや押し入れの中にいると電波が届かないらしくデータ上行方不明になることがありました。

そのため受信機をなるべく部屋の真ん中に寄せたり、ちょっとクローゼット寄りに置いてみたりしてできる限り電波強度を維持できるようにして解決しています。

それでも受信できない時はたまにあるので、苦肉の策としてSlack通知時に最後にいた場所を載せることで探す際のヒントとなるように工夫しています

[BLE] 部屋の判定を間違える

これもある程度仕方がないことなのですが、受信機と受信機の間(廊下など)にネコチャンがいると稀に部屋の判定を間違えることがあります。 これはお互いの受信機から見たRSSI値がほぼ同一かつ受信機の配置場所によって容易に変動するためです。

なるべく誤差を減らすためにサーバー側で集めたRSSIの平均値を用いたり(これは正直気休めにもなるか微妙です)デバイス毎の距離を離したりしています。

より厳密にやるのであれば、以下の記事のような方式でやるのがいいかと思いますが、実際利用してみて間違えているケースがあまりなかったため今回はそこまで踏み込んだ実装はしていません。課題感まで浮上してきたらやってみようと思います。

blog.dcs.co.jp

[ATOM Lite] 書き込みに失敗する

これはマイコンあるあるらしいのですが、シリアル通信の速度が早すぎると書き込みに失敗してしまうことがあるようです。 ボーレートを11520bpsに変更することで解決しました。

Kyash iOSのアプリサイズ削減取り組み

これは Kyash Advent Calendar 2021 17日目の記事です


KyashでiOSエンジニアをしている @nekowen です。

開発をしていて意外と見落としがちなのがアプリサイズの肥大化です。

Kyashでも一時期アプリのサイズが100MBを超える規模となっていましたが、リソースデータを見直すことで大幅にサイズを減らすことができました。

この記事では、アプリサイズをどう減らしたのか、同じような問題を起こさないために行った取り組みをご紹介します。

続きを読む

M1 Macへの移行作業ログ

M1 Mac miniを買ったのでiMac 5Kから移行作業をしている。
主にiOSアプリ開発周りで引っかかったところを挙げていきます。

pod installに失敗する

CocoaPods自体はインストールに成功するが問題はその先で、pod install時に以下のように怒られる

/src/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/lib/ffi.rb:6:in `require': dlsym(0x7fcdf6710400, Init_ffi_c): symbol not found - /src/vendor/bundle/ruby/2.6.0/gems/ffi-1.13.1/lib/ffi_c.bundle (LoadError)

どうやらffi側の修正対応が必要そうで、issue見るとワークアラウンドがコメントされていた。 https://github.com/CocoaPods/CocoaPods/issues/9907#issuecomment-749389632

arch -x86_64 pod install

これで動いた。ちなみにTerminal自体をRosettaで開いても同じ挙動になる。

(WIP)CarthageとXCFramework対応

これが一番めんどくさかった。iOS Simulatorがx86_64向けにビルドされている既存のframeworkではビルドに失敗する。 解決するためには最近Carthageが対応したXCFrameworkに移行する必要がある。

なぜ移行する必要があるのかというと、今までiOS Simulator(x86_64), iOS(arm64)それぞれのアーキテクチャを元にfat binaryを生成していたが、M1 Macの登場によりiOS Simulator(arm64), iOS(arm64)にアーキテクチャが変わった。この影響で同じアーキテクチャ同士競合してしまい、fat binaryの生成に失敗する。

XCFrameworkはXcode11から用意された新しい形式で、サポートしているプラットフォーム毎のFrameworkを一つにまとめることができる。
プラットフォーム、というところがキモで、アーキテクチャの制約を受けないのでM1 Macでも問題なく動作する。

メリットもあって、CarthageだとBuild Phaseで実行していたcopy-frameworksも不要になる。AppStoreに提出するときにiOS Simulator向けのアーキテクチャをFrameworkから取り除く必要がなくなったためで、これによりビルドが更に早くなる。そういった点ではM1 Macを持っていようがいなかろうが、早めに対応するのが吉かもしれない。

前置きが長くなったが、Carthageでの対応自体は簡単で、オプションの--use-xcframeworksを付与してあげるだけ。

carthage bootstrap --platform iOS --cache-builds --use-xcframeworks

これをやるとXCFrameworkがCarthage/Buildの中に作られる。pre-builtされたFrameworkはCarthage/Build/iOSに入っていた。

次にXCFramework化されたライブラリをBuild Phasesで実行しているcarthage copy-frameworksから落としていくという作業が必要になる。これは手作業で行う。

XcodeGenを採用している場合、XCFrameworkはframeworkを使って指定する。
carthageを指定するとcopy-frameworksに追加されてしまうので今は使わないように。

dependencies:
  - framework: "Carthage/Build/APIKit.xcframework"
  - framework: "Carthage/Build/Auth0.xcframework"
  - framework: "Carthage/Build/Lottie.xcframework"

XcodeGen側でもXCFramework対応が進められているので、そのうちこの対応は不要になると思う。 github.com

pre-builtされたFrameworkがまだXCFramework対応されていなかったのでいったん作業はここまで。

(番外編)iCloud Driveが同期しない

おそらくBig Surの問題。 初期設定後いつまで経ってもiCloud Driveの中身が同期されなかった。

まず確認すべき点は同期の対象になっているかどうか。設定→iCloud Driveのオプションを選択して、デスクトップフォルダと書類フォルダにチェックが入っていることを確認する。 筆者の環境では入っていなかったので注意してほしい。

f:id:nekowen:20210223204618p:plain
iCloud設定

それでもダメな場合、brctlコマンドで現在のSync状態を確認と診断を行うと改善する可能性がある。問題なさそうだが念の為自己責任でお願いします。
以下はRedditで紹介されていたコメントを引用する。

まずSync状態を確認する。

brctl status

ログの上部の方に何かエラーが出ていないか確認する。筆者の環境では>>> BROKEN STRUCTUREと出ており、詳しい原因はAppleのサポートに聞かないとわからないが"何かがおかしいです"状態になっていた。iCloudの情報を保持するDBが壊れてるとかなのか?

次に診断を行う。ログを収集するけど良いか?と聞かれるので返答。

brctl diagnose

そうするとiCloudの管理するファイルやシステムなど色々なログが取られてzipに吐き出される。このファイルは使わないので削除してOK
診断を行うこと自体に意味があるらしく、診断後はBROKEN STRUCTUREの文字がなくなりiCloud Driveの同期が行われるようになった。

iCloudわからーん!

余談

M1 Mac miniを使ってみての感想だが、どの処理を行うにしても動作がスムーズで気に入った。移行元がiMac 5KのFusion Driveという点でそもそもSSD onlyとSSD + HDDでは比較にならないというのはあるかもしれないが…
そして移行作業も思ったほど大きな障壁はなさそうに感じた。M1 Macの販売から3ヶ月くらい経っているのでその間に色々なOSSやソフトウェアが早急に対応してくれていた点も大きい。 少なくとも開発環境においてはM1 Macは実用的なものになっているんじゃないかなと思っている。
ちなみにXcodeRosettaで動かせば従来のビルドとして走るのでフレームワークの対応が辛すぎる場合はRosettaメインに切り替えるのもアリ。シミュレータもx86_64で動く。ただネイティブの恩恵は得られなくなるけど。

Among Usで遊んだ

最近界隈で絶大な人気を誇る「Among Us」
以前から友人とちょこちょこプレイしていたのだが、最近前職の方達ともプレイする機会があったので思ったことを書いてみる。

Among Usとは

Among Usは、4人からプレイできるシンプルな人狼ゲーム。宇宙をテーマとしており、「宇宙人狼」とも呼ばれている。 対応プラットフォームはPC(Steam)/Switch/iOSAndroid
個人的にはPCでのプレイを推奨する。キーボード操作なのでキャラクター移動がやりやすいこと、後述するBotの導入が可能になるためだ。

store.steampowered.com

プレイヤーはゲーム開始時にランダムにクルーメイト(村人)とインポスター(人狼)の役割を割り当てられる。これは他の人には通知されない。

クルーメイトは用意されているタスクをすべて終わらせるか、インポスターを全員追放すれば勝ち。
インポスターはタスクが終わる前にクルーメイトをKillしてインポスターと数を同じにするか、特定の妨害タスクを時間切れに追い込めば勝ちになる。

プレイヤーが死体を発見して通報したとき、あるいは緊急ボタンを押すことで緊急会議を始めることができる。 ここで誰が死体を発見したか、そのとき何をしていたかなどを話し合い、疑わしいプレイヤーに投票して追放していく。ちなみにスキップも可能だ。

コミュニケーションの必要性

Among Usに限らず人狼系ゲーム全般にいえるかもしれないが、都度会議が発生するため他のプレイヤーとうまくコミュニケーションをとる必要がある。

f:id:nekowen:20201227135322j:plain
会議中の様子。このとき僕はKillされているので会議には参加できずみてるだけ。

黙っていればインポスターとみなされてしまうので、自分の行動履歴を皆に明確に伝えなければならないし、さらに自分がインポスターだった場合、「○○さんと一緒にいましたよね?」と周りにシロっぽくみせる必要もある。つまり話し方のスキルも問われてくる。

また会議時間もデフォルトでは2分ちょっととなっており、あまりだらだらと話している余裕はない。 誰かがリードをとってうまい時間の使い方をしないと無駄話で終わってしまい、そのままスキップ…ということもある。

他の方達とプレイした時は、全員の最終位置を聞き込み、互いにシロ証明ができればそこは除外、じゃあ怪しい人は?と進行がスムーズだった。手慣れておる。

事故も起きる

このゲームは実際にキャラクターを操作するので、Killするタイミングや場所によっては偶然通りがかったクルーメイトに事件現場をみられてしまうこともある。

これが起きると自分の罪を認めるプレイヤーもいるが、粘り強いプレイヤーは通報したクルーメイトをセルフレポート(自分でKillして自分で通報すること)だと言い張り、互いに生死をかけた議論を始めることになる。

こうなると他のプレイヤーは二人のうちどっちが嘘をついているか?を判断しなければならないので、お互いの証言から見極める必要がある。
特にこの会議で一人インポスターを吊らないと負ける場合なんかは、自分の選択次第で勝敗が決まるのでプレッシャーがヤバイ。そして大抵間違った選択をしてやらかす。

まとめ

モバイルからでもできるAmong Usは気軽にやれてワイワイできるので、なかなか外に出ることに抵抗感があるこのご時世にはちょうどよいゲームだと思った。

前職のゲーム会にもお邪魔させてもらったが、普段あまり話さない方ともガッツリ話す機会にもなったのでコミュニケーション不足を感じているなら是非Among Usを勧めたい。

(Tips)AutoMuteUsの導入

Among Usの会議にはDiscordの使用が推奨されている。

会議の間はマイクをオン、それ以外はオフにするという運用を各プレイヤーが行うわけだが、手動ゆえ、時々マイクがオンになったままという事故(ミュート芸とも言われる)が起きる。

このようなトラブルを避けるため、Among Us向けの自動ミュートBotOSSで公開されている。

github.com

Botを動かすには用意されているサーバーを使うか、セルフホストの2つの選択肢がある。

前者はサーバーが混んでいる時間にプレイをするとミュートが行われるまでにディレイが発生しやすくなるため、現状はセルフホストの方が良いと思う。

セルフホストもDockerを使う方法か、Windows環境であればDockerを使わずに実行ファイルを起動するだけで使えるのでそこまで難易度は高くない。

今のところ友人のDiscord鯖で運用しているが、ミュートする手間がなくなりなかなか快適なのでこれを使わずしてAmong Usができない体になってしまった。
プレイする際はついでに導入することもオススメしたい。

株式会社Kyashに入社しました

この記事はKyash Advent Calendar 2020 - Adventar 15日目の記事です。

Kyash iOSエンジニアの@nekowenです。
Kyashには11月に入社してもうすぐ1ヶ月となります。
この記事では僕がKyashに入社した経緯と、入社してから思ったことを書いていきます。

続きを読む