2019年6月8日土曜日

SwiftUI チュートリアル ヘルプ ドキュメント FAQ 困ったらとりあえずここ見ればOK

https://github.com/Juanpe/About-SwiftUI

いろいろ調べたのですが、これよりよくまとまっているドキュメントを現状発見できませんでしたので、2019/06/08現在では上記のドキュメントを参照するのがベストだと思います。

特に以下の記事は役立ち度が高かったです。この2つだけで問題の9割ぐらいは解決できると思います。

SwiftUI by Example
https://www.hackingwithswift.com/quick-start/swiftui

Answers to the most common questions about SwiftUI
https://wwdcbysundell.com/2019/swiftui-common-questions/

あとは直接SwiftUI.frameworkの中身を見るのが良いと思います。正直Appleのドキュメンテーションは未だにSwiftのExtensionベースの実装をきれいにドキュメントに起こす事ができておらず、複数のドキュメントに重複した記載が見られたり、どこで定義されているfunc/varなのかを正しく表現できていなかったりします。なのでシグネチャの名前さえわかっていればSwiftUI.frameworkの中を自分で検索したほうが正確にどういう定義になっているか判断できて便利です。

2018年12月19日水曜日

HTML5 video / audioがiOSデバイスの消音スイッチの状態に従うようにする方法 (UIWebView編 / WKWebView編)

いろいろ調べていたのですが、遥か大昔に私が書いたブログ記事が長い年月を経て今や大間違いになっていたのでここに訂正させていただきたいと思います。

今回ご紹介するのはUIWebViewまたはWKWebViewで表示しているHTML5のvideo要素やaudio要素が音声を出すときに、iOSデバイスについている消音スイッチ (ミュートスイッチ, mute switch, silent switch) の状態を無視してしまう問題を解決する方法です。相変わらず紹介内容がとてもニッチですね。

ちなみにSafariはデフォルトでちゃんと消音スイッチの状態を反映してくれるので、SafariでYouTubeの動画を見ててもいきなり音が流れ出すことはありません。素晴らしい!というわけで我々のアプリもぜひそのようにしたいと思います。

消音スイッチの挙動について

まず基本的なおさらいとして、消音スイッチがどのような挙動を示すかについてこちらにまとめます。
  • 各プロセスごとに、AVAudioSessionが消音スイッチに対してどのように振る舞うかを定義している。
  • 具体的には、AVAudioSession.Categoryの値に応じて挙動が変化する。ドキュメントにも明記されている。
    • AVAudioSession.Category.ambientやAVAudioSession.Category.soloAmbientを指定すると、消音スイッチがONのときは音が出ないようになる。
    • 逆にAVAudioSession.Category.playbackを指定すると消音スイッチを一切無視するようになる。
  • 消音スイッチの現在の物理的な状態を取得するAPIは一切ない。Private APIで以前は可能だったが、穴が塞がれたためその方法も利用できない。そもそもアプリが提出時の審査で蹴られる。当然JS経由でHTMLコンテンツ上から状態を取得するのも不可能。

解決方法・UIWebView編

UIWebViewはWebKit1を利用しており、したがってUIWebViewのエンジンは我々のアプリ内のプロセスで動作します。そこでUIWebViewのインスタンスを生成するより先に、先述の通りAVAudioSessionのcategoryを変更して消音スイッチの状態を反映してやるように示してやるとうまくいきます。
try? AVAudioSession.sharedInstance().setCategory(.soloAmbient, mode: .default, options: [])
try? AVAudioSession.sharedInstance().setActive(true)
ただしご存知の通り、すでにUIWebViewはdeprecated扱いとなっており、WKWebViewへの移行が推奨されています。そこでWKWebViewでも同様の方法が使えないかやってみましょう。

解決方法・WKWebView編

ありません。

繰り返しますが、ありません。WebKit2の設計上のバグです。

今後修正される可能性もほぼ間違いなくありません。諦めてください。

一応、順を追って説明します。
  1. WKWebViewはWebKit2で実装されています。
  2. WebKit2は我々のアプリ内のプロセスで動作するのではなく、「共用の」WebCoreプロセス上で動作し、その結果が我々のアプリ内に転送されてくるような実装になっています。
  3. 「共用の」WebCoreプロセスは(これは推測ですが、audioやvideoを最大限に活かすため)AVAudioSession.Category.playbackとAVAudioSession.Mode.moviePlaybackで動作するように設定されている用に見えます。したがって消音スイッチは無視されます。
  4. 最初にご説明したとおり、AVAudioSessionによる消音スイッチに対する振る舞いは「プロセス単位」で制御されており、これは遥か大昔のiOS 2.0どころか下手するとNeXTSTEPの時代からCarbonレイヤーで決められている挙動だったりします。要するに今からの変更はほぼ不可能です。
  5. もうおわかりだと思いますが、「共用の」WebCoreプロセスの挙動を我々個別のアプリがAVAudioSession経由で勝手に変更することは不可能ですし、これを可能にするのはiOSとWebKitの設計上実質不可能なため、問題は解決されません。
    1. WebCoreプロセスを各個別のアプリごとに立ち上げて対応しろよ!と思うかもしれませんが、WebCoreプロセスは近年AppleのOSに組み込まれている特権階級モードで動作するプロセスとなっており(そのためメモリに無制限なアクセスが可能で、JSをJITコンパイルして高速に動作させる事が可能)、最近のセキュリティとバッテリーライフにうるさいAppleがそれを個別のアプリごとに立ち上げさせるなどということは現経営陣が全滅しない限りありえないと断言できます。
    2. 共用のWebCoreプロセスが使用するAVAudioSession.Categoryを変えてしまえばいいだろ!とも思いますが、実はAVAudioSession.Categoryをplaybackに指定しない限りバックグラウンドで音楽を流し続けることができません。したがって今度はWebViewで音楽プレイヤーを作ったりPicture in Pictureで動画を流し続けるアプリが全滅するため、これも不可能です。
    3. だったら消音スイッチの状態を自分で調べて自分で動画をmuteにすればよいのでは、と思いますが、これも先述の通り消音スイッチの状態を調べる方法は一切ないため、一律でmuteにしてしまうなどの乱暴な方法を用いない限り対応できません。
やばいですね☆

iOS開発でエラーコードを調べるときはOSStatus.comを使おう

久しぶりに書く価値のあるネタが見つかったのでご紹介します。

iOSのフレームワークがNSErrorを返してきたとき、そのcodeが何を意味するのかを調べる必要が出てくることが往々にして発生します。新し目のフレームワークは比較的簡単に調べられるようにまとまっているのですが、厄介なのがOSStatusなどの大昔から存在するエラーコードの場合です。

NSError コードの調べ方 こちらのブログ記事で紹介されている通りに一つ一つヘッダファイル内を探す方法でもいいのですが、もはや平成の終わりが目の前に迫っている2018年の年の瀬にもなってこのような原始的な方法を使っているようではよろしくありません。

そこで OSStatus.com の出番です。こちらの検索欄にcodeをコピペして検索ボタンを押すだけであらゆるエラーの詳細が一発で帰ってきます。実際にやってみましょう。


一発ですね。素晴らしい。


名前での検索もバッチリです。

偶然見つけたのですが、はてブもほとんどついてないし、ネット上で紹介されている記事も全く見つからなかったので書いてみました。便利です!