MultipeerConnectivityって使えるようになりそうなんだが(2023年10月7日)

iOS/Catalyst(Mac)にMultipeer connectivityと言う端末同士を接続するフレームワークがあります。
この記事はこのフレームワークを使った時の情報です。現在は、APPを配信してモニターリング中です。

https://apps.apple.com/jp/app/nearbymonitoring/id1559042128

 ## はじまり
MultipeerConnectivityで端末を接続したり切ったりを繰り返すと死んじゃうとか接続されず放置されるとかがありまして、このフレームワーク実用的でないなぁと思ったのがはじまりです。
他の方もiosのバージョンやうまく動作しない場合があるとの事なのですが諦めきれず、未練たらたーら地味にいろいろテストしてます。闇深いです。
ていうか、誰も問題視してないって・・・でしょうかね

現状の考察結果
1. 設定でのWifi OFFでは通信できない
   →　APPではWifi OFFを検出できるようにして「ONしてね」ってメッセージ表示をしないと永遠に繋がらない。　自機のWifiがONである事が必須。
2. ios14以降ではローカルネットワークのプライバシー許可が必要です。
　 →　禁止しちゃうと延々につながらない(1.と同じ理由)ので ローカルネットワークの状態を検出する必要があると思う。
3. バックグラウンド移動(Homeボタン、電源ボタン)、システム割り込み時にはセッションがとまる。
　 → セッション停止前に自らセッション停止させて復帰時に自動接続させるのが無難かな。
4. **リカバリーは絶対必須である。** 審査で落とされる方々は実装できてないのではないでしょうか？（２台で通信の場合）
 　→　現状接続やり直ししかないんだが・・・・
5. セッション解放した場合は、待った方が問題なさげ( 9.の問題と関連している可能性があり）と思い込んでる。
6. ios11以降デバイス同士のMultiPeerConnectivityは、安定している風
　　(ios7,8,9,10のサポートなしにすべきかも・・・・・・)
7. 一度接続に成功したら、２回目以降の接続がスムーズになるようだ。しかし原因は不明だが突然接続が悪くなる場合があるのでだまされないようにする。（OSが何か覚えてそうに見える)
8. Wifi APに接続しない場合と接続する場合では、転送速度が違います。
　接続した方が早いです。 ただし速度には波があるので安定な通信速度確保にはキャッシュ処理が必要ですね。（ストリーミングで確認してるので・・・）
9. ログに[GCKSession] Select call failed with error (C01A0016).がでたら負けです。アウトです。Connectingまでしかできません。二度とconnectedでません。＼(*｀∧´)／
 　→ どうもセッションの解放と関係がありそう・・・
10. ピア作成は、initWithPeer:securityIdentity:encryptionPreference:で行う（通信できないios過去バージョンがあった。たしかios10か11だったと思う)

11. 作ったアプリ（ios,osX）を　APP storeで配信し、clashをモニタしてーいる（1年以上）が、思いのほか安定しているっぽいです。（旧機種や古いiosを使用していないからかもしれません）
MacOS HighSierraさんとは接続調子良いけど、MacOSVenturaさんとは接続に時間がかかるっぽいね。

 #### Mac Catalystさんです。（Ver14.0)
Macでも動作するのかなぁと思いチャレンジしました。

iPad(iOS simulator)ではMultipeerConnectivityで通信できますが(ios14対応済み)、Catalyst は、そのままではだめでした。Advertisingに失敗しているようです。

**Mac catalyst 対応してみた　いまだに object-C**
https://qiita.com/YearCentury/items/d57e5872636cc26f6f14

→　sigining & Capabilities にて　Incoming Connections(Server)の欄にチェックが入ってないとAdvertisingに失敗するようです。(ログに権限ないやんって出力されて怒られる)
いつもなら権限？ってなるとこですがそこはMacでのお勉強で経験済でございます。チェックをペシ！ペシ！

**bind()でエラーが出はります**
https://qiita.com/YearCentury/items/9c79589128feb3ea9984

しかし、iPhone,iPad同士,シミュレーターではチェックなくても動くんですよ・・・・・・・
とりあえず　Mac Catalystさんでは、派手な派手な通信失敗（ちょっとびっくり）が発生してリトライすれば動作するようですね（リトライ万歳）。（ストリームでの通信にて）

 ## なんでかな

 #### browser:foundPeer:って来ない場合あるの？(ios14)
Browserとして何回か接続していると、browser:foundPeer:なしで
session:peer:didChangeState:が来て　connecting その後　connected する場合があるようです。
→Browsing開始後Advertisingすると起こりやすいです。

 #### 死ぬパート1 read:maxLength:でスレッド死ぬ(現象はios11,12,13,14で発生でios11/Catalystで動作確認できた)
何も考えずストリームの読み込みを行うと死にますね。爆弾です。hasBytesAvailableで確認後に実行すれば大方改善しますが、~~タイミング悪く　notConnectきてしまうと即死です。地雷です。ドッカーン
システムからスレッドが殺されます。
ないわー
スレッド再起動が必要です。・・・・別スレッドで構成するしかないね。~~
Catalyst版で多発しましたので解析しました。（上記取り消し線）
C言語で記述しているのですがマルチスレッド側でusleep()中　メインスレッドで[NSThread　sleepForTimeInterval:] を実行するとマルチスレッド側のタスクがsleep()から帰ってこない場合がありまして　まるでスレッドが死んだようにみえていた容疑がかかっておりますので、「タイミング悪く　notConnectきてしまうと即死です。」は、撤回します。


 #### 死ぬパート２(ios14)
Xcodeでライトニングで接続したiphoneが他のデバイスとストリーム通信中、ライトニングで接続した側で停止（▪️）したら、接続相手のデバイス側のプロセスが死ぬ(crashする)場合がある。
ログ見ると　XPC connection interrupted をログ表示して死にます。なぜか　接続側で停止（▪️）した時だけ通信相手のデバイスでAPPが死ぬ場合があります。死ぬAPPが相手側ってなに？
ホームボタンや電源ボタンなどでは発生ないのですが・・・・・
実動作させて同じ問題が発生していないようだが・・・・・パート１以外にも即死フラグがあるのかもしれません

 #### プライバシーのlocal network OFFされたらどうしようもないのか？(ios14)
ios14から プライバシー関連で　local network の設定が増えたのですが、
最初にOFFされたら、”ONしてね” って言いたいですやん。
どーやってOFFされているのか確認したら良いのだろうねぇ
stack overflowでもあれやこれや出てますが　MultipeerConnectivityは、Wifi アクセスポイントに接続されていなくてもWifiオンだったら動作する（こっちがメインの使用方法と思う)ので難儀になりそうです。
→自分と通信してみて見つければOKで見つからなければlocalNetwork OFFと判断して良いと思います。
（Appleさんが用意してくれれば苦労しないのですが・・・・）

 #### ios14 だんまりで、全く接続する気配がない場合がある。(相手がios9で頻発)
・突然　invitation後　notConnectのやりとりがずーと続く場合がある。
・Browsingしてるのだが、挙動が１分ぐらい何もなかったようなフリして突然反応する場合がある。（アドバタイズはずーとしてるんだが)
凹むわー　どうしろと・・・・・

→電源OFFからios14で接続すると1分ぐらい経過して接続するみたいです。その後は快適に接続もあれば時間がかかる時もある。
→たぶんiOS11あたりでAPIに変更があった様ですがこの影響と思います。

ios9をアクセスポイント接続解除（WifiはON状態）すると改善した。（ios10,ios11,ios,12,ios13では発生してないけど、起こるかもしれない）

 #### connectedのなってから notConnectになる場合がある
なんでなん？せっかく繋がってるのに・・・・
→　再接続(Advertising/Browsing)し直さないとだんまりを決め込むようようです。再接続すべし
→　Advertising/Browsingしても発生するみたいだ。(ios15とios12)

 #### メモリーがちびちび消費される(ブラウザー側からで確認した） ~~＋ クラッシュ~~ (iOS12/iOS15)
接続とセッション解放を繰り返してメモリをみていると　アドバタイス側と同様でちびちび消費されます。そしてクラッシュします。~~クラッシュの原因はたぶん
[browser invitePeer:peerID toSession:self.myBrwSession withContext:**ここ** timeout:1]; 
値を設定するとどうも　循環参照が発生するように見えるのだが・・・・
でもちびちびは治らねぇ~~~
~~→　nilにしてテストしたらうまくいいてるような感じなのだが、別のクラッシュが発生するんだな （うそでした。）~~　
~~→ MCNearbyServiceBrowser で循環が発生してクラッシュしているように見えるのだが、解放をあきらめるしかないのかなぁ。~~
→ retainCounでモニターしていてわかりました。(￣¨￣)/
　解放手順が悪かったようです。
　1.MCNearbyServiceBrowser解放
　2.MCPeerID解放
　が正解でした。これで循環が発生は回避できたようだが、ちびちび消費は治らないし、
　~~ブラウザーテスト（5秒毎に通信切断・接続を１時間ぐらい）最後には、死にます。~~
→ 追加で排他処理もいれました（@synchronizedでデレゲートとスタート停止等の処理)それからは
　ブラウザーテストでクラッシュ現象はなくなりました。でも新たに 連続テスト中にconnectingまでできるがconnectedにならない問題がみつかりました。(ios12とios15)
　いつまでたってもconnectedが来ない現象が延々続くんです。
→Logに
　**[GCKSession] Select call failed with error (C01A0016).**が出るとこの現象がはじなるようです。
　これが出るとも二度とconnectedにはならないです。
→いろいろ試した（連続テストで接続と切断繰り返したら３０分〜５時間で発生）がどうしてもこの現象がでるようだ。
 話はかわりますが、この現象はXcodeシミュレータでも発生するようで、よくできているなぁと関心してます。
 ここまで互換性があるんかい！さっすがっす。
 ということで方針変更です。
 セッションの停止(disconnect)はするが、解放しない方法ではどうか確認することにします。
（過去にセッション解放しないと云々ありましたが置いときます。）
→解放しない方法の場合、複数台で接続(iOS12:2台 ios9:1台　ios15:1台中の２台の組み合わせで接続)テストした時とんでもなくリトライする場合があるのとCrash(ios9)が発生しました。解放してもリトライしてましたが、どちらかというと解放した方がまだましかなぁと感じた。（相当作り込んできたから運気もみかたになってくれるかなと思ったが甘かったです。だめじゃん）
→MultipeerConnectivityってBonjourで通信してるっぽいのですがこのプロトコルって接続したり切ったり頻繁にすることを前提にしてないようにしてるらしい(Wikipedia談)ので考え直さないといけないのかもしれん。


 #### メモリーがちびちび消費される(アドバタイズ側から確認した）(iOS12/iOS15)
接続とセッション解放を繰り返してメモリをみていると0.0?MBずつ消費されていく現象があります。
~~advertiser: didReceiveInvitationFromPeer: withContext: invitationHandler:
で　通常　TRUEの処理を　invitationHandler(FALSE, セッション); にして中断させてテストするとメモリが消費されないようで
session: didReceiveCertificate: fromPeer: certificateHandler:
で certificateHandler(FALSE); にして中断させてテストすると消費されます。
→　なんとかメモリを解放させたいのだが.....
   なにかこの辺でメモリ確保しては解放しない輩がいるようです。（ios10,11で確認)~~
   それでもずっとつづけたら **クラッシュ** した。
→ ~~どうもピアID、セッション、サービスの解放順番があるようです。nilをいれれば解放されると思ってたんだがそうでないらしい。クラッシュはしなくなったのだが~~まだメモリは消費されます。
→ ~~retainCountをモニタしてみたら　MCNearbyServiceBrowserが解放されない場合があるようです。
　このとき　MCPeerIDも解放されてないようです。~~
→ 上記ブラウザー解放手順対応をアドバタイズ側に導入したらクラッシュ現象はなくなりました。
　それでも長時間接続/解放繰り返すと繋がらなくなります。
→ こちらでも**[GCKSession] Select call failed with error (C01A0016).**が出るようですね
　同様に方針変更です。

 #### リカバリー
エラーなどで直ぐにセッション解放した後すぐ再接続開始すると、問題が発生しやすいようです。
→　３秒ぐらいまってからリカバリーの開始すると謎のCRASHや謎のメモリー食い、謎のハングアップが激減しました。（解放関連で何かやってるっぽい。見えないけど）

 #### アクセスポイント接続状態とそうでない場合に転送速度が違う
Wifiでアクセスポイント接続状態だと早いのだが、非接続状態では遅い
→　仕方がないのかなと思うが、アクセスポイントに繋がってない時には**馬鹿でっかい**ラグがたまに発生するので、リアルタイム処理では厄介です。

 ### バッテリーアラート
テスト中バッテリーアラート（バッテリないよーアラート）がきたらセッションが死にました。バックエンドに移行したらセッションがダメなのは知ってたのですがねぇ・・・・
というここは、電話かかってきたら死ぬんですねたぶん。
復活の呪文を装備していないとだめですね。

 ## 当初に比べたらだいぶん改善されました......
謎の３秒ルールと逸脱の接続再起動で、かなり安定的になってきてます。
が、なんせ　MultipeerConnectivityのフレームワークの怪しい挙動を外部でなんとかしようとしてるんで厳しい戦いです。

##少ないね
　ストアの　MultipeerConnectivityを使ったソフトが少ないのも接続の不安定性からきてるかもね
　いくつか使用されているAPPみましたが、接続復帰までしてくれるAPPに出会ってないです。

 ## で、どーする
かなり持ちこたえる様になってきましたね。APPの審査はなんとかなりそうな水準にきました。
Connectedできない問題さえばれなければ（ばれるまで時間がかかるので大丈夫と思う）
それなりに使えそうな感じです。

 ## 過去の情報
・ios15.1,ios15.2は、めずらしく大きな問題なし(従来と変わらない)に見えます。(何か改善されているのだろうか)
・ios14で、接続できない(相手がwifiOFF)のに招待が来る。そしてその後notConnectになる(ios14.4.1)
・ios14では、info.plistにほにゃらら設定しないとダメですね。（詳細はぐぐってね）
・転送レートにラグがある。（映像とか扱う場合、遅延が発生して致命的）
・iosバージョンは、最終リビジョンを使わないと動かない場合が多いようにみえる。（過去ios9,10）
（Appleは、あまり問題視してないようだが）
・知らなかった・・・ios11以降ではBluetoothのみが 使えないみたいだ。　　（https://lists.apple.com/archives/bonjour-dev/2017/Sep/msg00005.html )
　！！！！ソースがなくなってる。なかった事になってるぅ(^_^;)
・ios11.3.1でwifiでは接続できるようだ
・Bluetoothで負荷かけられたらAirPodsが辛いんんでios11からBluetooth外したんかなぁ
・WEB(日本語)では、こんなんあんなんできますだけで、不満とか問題とかされてないみたい。
・StackOverFlowの情報も効果的な情報が意外と少ない(本当か嘘かもわからない)
・ios10.3.3はios7との相性が悪そうだ
・~~wifi と　bluetooth　比較するとwifiの通信にはラグがあるようだ。Bluetoothのほうが安定してる？~~
　間違い＞wifiの方がRunLoop処理が重たくなるようです。
・Browser/advertingは、コネクト後止めないと通信lag多発・・・・当然か
・Browser/advertingは解放しちゃうとよろしくないみたいだ( ios8/9/10)
・セッションは解放しないと再接続できない場合ある。(解放していいのかなぁ)
・一度つかったセッションは解放しないと通信障害になる場合ある。(iosは、なにやってるのだろう？)
・Advertisingと　Browsingを同じPeerIDで行うと　Stopで止まらない場合がある(起動順番に縛られたくないよなぁ）
・アドバタイズとブラウズを行うと自分のアドバタイズがやってくる・・・・当然か
・電源オフしないと（通信が不安定)が戻らない問題があるようだ。IOS7/IOS10（このモードにならなくする方法？原因があるんだるけど？？？？）
・ios7と他のiosの接続で不具合(ios7 Crush)が多く発生するから相性が悪く感じる
・CONNECTING後にCONNECTEDが来なくてNotConnectになる場合が多々ある（え・・・・)
・招待しても接続してこない場合もある んだなこれが( ios8 & else )
・相手がいないのにアドバタイズがくる場合がある(APIがどっかでセッション覚えてるんだろうなぁ）
~~・アドバタイズ中から状態進まない場合あり(ios10:BLuetoothON/OFFで復帰する)~~ （ios11以降向いもなので削除)
・毎回自分のPeerIDを変えて通信したら、だんだん通信速度が遅くなってくバカになるようだ。１つにしよう
~~・BLE FRAMEWORK動かすと通信障害発生する場合あるみたいだ。(正直わからない部分です)~~ (要再確認）
・アドバタイズ　connecting で進捗しない場合ある(ios8とios10)
・session解放って時間が必要みたい。(CRUSH頻度が低くなったから思った)
・セッション削除後にも受信データがくる場合がある。(マルチタスクでよくあることなんだが・・・)
・ios8 Browerでadvertisingを永遠に見つけられないモードがある。（リセット電源現オフ関係ないようだ）
・同じプログラムをいれかえたら突然治る場合もあるから、APIがセッション情報を覚えている部分あるのだろう推測。

・ios9が比較的に安定的な動作してるように見えるんだが・・・
~~・disconnectしてからデータ送ったら繋がるみたいだ~~  (再確認でダメなので削除)
・ios7とios10.2の接続はかなりダメっぽい場合がある。
・ios7はBrowserのほうが調子がいいみたいです。でないと接続シーケンスが異常になる(対ios10)
・ios8はAdvertingのほうがBrowserより調子いいみたいです。
~~・ios8はリカバリしないとios10/9の接続はむずかしいみたいだ。~~ （確定した）