- 投稿日:2021-03-06T23:34:08+09:00
Flutter pod installでエラー undefined method `each_child'
背景
FlutterとFirebaseを連携させてiOSシュミレーターでビルドすると、一向に成功しませんでした。。
flutter doctor
で確認しても特に問題はなさそうで、、、かなりの時間ハマってたので、ここに記録することにします
環境
MacOS
pubspec.yamlversion: 1.0.0+1 environment: sdk: ">=2.7.0 <3.0.0" dependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.3 firebase_core: ^1.0.0 cloud_firestore: ^1.0.0Rubyのバージョンが問題だった?
こちらをみると、どうやらRubyのバージョンが古いことによって発生するっぽい。。
「なんでRuby??」
と思うだろうが、iOS用のプロジェクトの依存関係マネージャーである「CocoaPods」は Rubyで開発されているため。どうやってRubyのバージョンを上げるか?
ここが何気に苦労しました。。。
rbenvを使う
こちらを参考に、rbenvでRubyのバージョンを上げることにしました。
rbenv install -l rbenv install 2.7.1 rbenv global 2.7.1rbenvは、複数のRubyのバージョンを管理し、プロジェクトごとにRubyのバージョンを指定して使うことを可能としてくれるツールです。
https://mae.chab.in/archives/2612
rbenv install -l
でインストールできるrubyのバージョンを確認します。しかし!!
私の場合は
rbenv install 2.7.1
が、インストールできるrubyのバージョンに存在しなかったのです!brew updateでもエラー発生
rbenvでrubyのバージョンを切り替える
こちらを参考にbrew update
を実行MacBook-Pro:~ gen$ brew update Error: homebrew-core is a shallow clone. homebrew-cask is a shallow clone. To `brew update`, first run: git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow These commands may take a few minutes to run due to the large size of the repositories. This restriction has been made on GitHub's request because updating shallow clones is an extremely expensive operation due to the tree layout and traffic of Homebrew/homebrew-core and Homebrew/homebrew-cask. We don't do this for you automatically to avoid repeatedly performing an expensive unshallow operation in CI systems (which should instead be fixed to not use shallow clones). Sorry for the inconvenience!謎のエラー。。。
ここで30分ぐらい道に迷ったのですが、、、しっかりとエラーメッセージを読むと答えがありましたTo `brew update`, first run: git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow
git -C
から始まるコマンドをそのまま実行するだけでいけました。。これで
rbenv install -l
を実行すると、2.7以上のバージョンがリストに表示されました。rbenvでRubyのバージョンを上げる
rbenv install 2.7.1 rbenv global 2.7.1
cocoapodsを再度インストール
必要なのかはわかりませんが、、念の為再度インストール。
そしてFlutterプロジェクトのios
の階層でpod install
sudo gem install cocoapods cd ios pod install Analyzing dependencies Pre-downloading: `FirebaseFirestore` from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `7.3.0` cloud_firestore: Using Firebase SDK version '7.3.0' defined in 'firebase_core' firebase_core: Using Firebase SDK version '7.3.0' defined in 'firebase_core' Downloading dependencies Installing Firebase (7.3.0) Installing FirebaseCore (7.3.0) Installing FirebaseCoreDiagnostics (7.4.0) Installing FirebaseFirestore (7.3.0) Installing Flutter (1.0.0) Installing GoogleDataTransport (8.2.0) Installing GoogleUtilities (7.2.0) Installing PromisesObjC (1.2.12) Installing cloud_firestore (1.0.0) Installing firebase_core (1.0.0) Installing nanopb (2.30907.0) Generating Pods project Integrating client project Pod installation complete! There are 4 dependencies from the Podfile and 11 total pods installed. [!] CocoaPods did not set the base configuration of your project because your project already has a custom config set. In order for CocoaPods integration to work at all, please either set the base configurations of the target `Runner` to `Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig` or include the `Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig` in your build configuration (`Flutter/Release.xcconfig`).
成功!!(多分)
最後に
色々と試行錯誤したので、所々記憶違いで間違えてるかもしれません。。。
発見したら指摘お願いします。
- 投稿日:2021-03-06T23:31:12+09:00
【個人開発】リモートでビンゴゲームができるアプリを作成しました【iOS,Android】
はじめに
最近Androidアプリ開発の勉強会に参加しAndroid開発に関して学ぶ機会がありました。そこで学んだ知識をアウトプットしたいと思いリモートでビンゴができるアプリを作成しました。
(Androidアプリのみだと実用性がないためiOS版もリリースすることにしました)
この記事ではアプリの概要と使用した技術に関して記述しようと思います。Android
iOS
サービス概要
オンライン・リモートで遊べるビンゴアプリです。
特徴としてはビンゴカードなしで遊ぶことができ、無料で遊べるアプリになっています。使い方
主催者編
主催者としてルームを作成します。
ルーム名、説明(任意)、パスワードを入力します。上記の入力が完了したらルーム作成ボタンをタップしてください。
※パスワードをルームに参加する際に必要になりますので参加者全員に共有してください。
参加者が集まったら「ビンゴを開始する」をタップしてください。
準備完了です。抽選ボタンをタップしたらビンゴが開始されます。参加者編
参加したいルームをタップしてください。
主催者が設定したパスワードを入力し参加するボタンをタップします。
これで参加完了です。
使用した技術
サーバー側の実装はすべてFirebaseに任せました。
Cloud FirestoreやFunctionsの使用経験がほとんどなかったため苦戦しました。
「実践Firestore」という書籍で細かくFirestoreの使い方に関して記載されており非常に参考になりました。
Firebase
- Authentication(匿名認証)
- Cloud Firestore
- Functions
- Hosting (プライバシーポリシーをデプロイするため)
- Crashlytics
- Analytics
- AdMob(インタースティシャル広告)
Android
言語 Kotlin
アーキテクチャー AAC
ライブラリー material-dialogs,lottie等iOS
言語 Swift
アーキテクチャー MVVM
ライブラリー RxSwift,RxCocoa,RxDataSources,lottie-ios等その他
プライバシーポリシー
下記サイトで自動でプライバシーポリシーのHTMLを生成しました。そのHTMLをFirebaseのhostingを用いてデプロイしました。
https://app-privacy-policy-generator.firebaseapp.com/問合せページ
アプリを公開するには問合せ先のwebページを作成します。
1から作成するのはかなり手間がかかるので「ペライチ」というサービスを用いてお問い合わせページを作成しました。無料で使えるかつ15分ぐらいで問合せフォームが作れるので非常に便利です。
https://peraichi.com/landing_pages/view/remotebingoおわりに
Androidアプリ開発をしてみるとiOSとの共通点が多くあり簡単なアプリを作れるレベルには成長できた気がします。(コードは汚いので時間があるタイミングで綺麗にしたい泣)
また実際にアプリを作って公開しようと思うとプライバシーポリシーの作成やストア用の画像の準備、証明書関係の整理等思ったよりやるべき事が多く苦労しました。
しかしながら自分の作りたいアプリを好きな時間で作れるのが個人開発のいい点だと思います。
今後も時間を見つけて個人開発をしていきたいです。
- 投稿日:2021-03-06T19:41:36+09:00
CareKitのコードから学ぶCore Dataの活用方法
はじめに
CareKitはアップルが開発しているオープンソースのフレームワークです。iOSとMacで動作します。
また、内部の永続化、検索処理にCore Dataを活用しているという事で、昔CoreDataを使いまくっていた人間としては活用方法に非常に興味があります。ただ、Core Data自体を使う事は今はあまりお勧めしません。ソースコード
CareKitのソースコードはここから入手可能です。
理解する上で参考となる文献
CareKitで使っている各種設計パターンについてはマーチンファウラーの有名な文献が参考になります。
基本的な設計思想
前述の通り、Core Dataを活用しています。ただし、利用者には隠蔽しています。理由はCore Dataを剥き出しにする設計にすると、利用者はCore Dataを学習する必要があるからです。これは他のフレームワーク、EventKit, ContactsKit, HealthKitも大体同じ考えです。
OCKStore周りのクラス構成
OCKStoreクラスは所謂リポジトリクラスです。プロトコル指向で実装しており、図以外にもプロトコルを継承しています。この辺についてはこの記事では深堀しません。
OCKStoreクラスは内部にCoreDataフレームワークのインスタンスを内部に持っています。OCKCarePlan周りのクラス構成
ごちゃごちゃしていますが、利用者に見せるOCKCarePlanクラスとCoreDataとのやりとりに使うOCKCDCarePlanクラスに分かれています。
OCKStoreから検索、保存等を行う際に相互に変換します。所謂データマッパーパターンです。NSManagedObjectから派生している方のクラスは所謂DTOとして使っています。どうやって変換しているかについては現段階では深堀しません。OCKCarePlanQuery周りのクラス構成
OCKCarePlanQueryクラスは所謂Queryオブジェクトです。検索する際に、OCKStoreがOCKCarePlanQueryのメソッドを使ってNSPredicateやSortDescriptorクラスに変換します。このクラスがあるおかげで利用者はNSPredicateの文法やクエリーの詳細を覚えなくてすみます。検索処理の流れ
検索処理の流れです。わかりやすくするためにOCKCarePlanStoreとOCKStoreと分けて書いています。OCKStoreクラスが内部でCore Dataとのやりとりに必要なデータに変換、Core Dataに渡しています。Core Dataから帰ってきたデータを利用者に公開しているOCKCarePlanオブジェクトに変換しています。意外な事にNSManagedObjectContextを複数使って処理負荷を下げる事はしていませんでした。追加、更新、削除処理の流れ
WIP
まとめ
CareKitの永続化周りのコードを追うことによって、参考文献に載っている様々な設計パターンが活用されている事がわかりました。ここでは説明していませんが、これまでの説明以外の様々なパターンが活用されています。
なお、開発メンバー全員がCore Dataに精通している場合はこのような複雑な設計を採用する必要はないかもしれません。
- 投稿日:2021-03-06T19:10:06+09:00
[Flutter] Flutter 2.0 Migration方法
Migration
- 方法
> dart migrate
- Process
C:/Users/itsme/Documents/GitHub/flutter_migration> dart migrate Migrating C:/Users/itsme/Documents/GitHub/flutter_migration See https://dart.dev/go/null-safety-migration for a migration guide. Analyzing project... [------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\] No analysis issues found. Generating migration suggestions... [-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------] Compiling instrumentation information... [-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------] View the migration suggestions by visiting: http://127.0.0.1:59295/C:/Users/itsme/Documents/GitHub/flutter_migration?authToken=SLX1RWDeq6Y%3D Use this interactive web view to review, improve, or apply the results. When finished with the preview, hit ctrl-c to terminate this process. If you make edits outside of the web view (in your IDE), use the 'Rerun from sources' action.migrateをすると、Null safety Previewのリンクかでます。
Proposed null safety changes
- リンクはプロジェクトによって、違います。
http://127.0.0.1:59295/C:/Users/itsme/Documents/GitHub/flutter_migration?authToken=SLX1RWDeq6Y%3DApply Migration
- マイグレーションにすると、environmentの変えます。
- 投稿日:2021-03-06T18:57:52+09:00
【初心者】Swift UIを勉強する その② ーーーSymbolsとList
はじめに
今回のはシンボルとリストの作成方法を学習していきます。
目次
SF Symbols
・appleさんはシンボルアプリを提供していますので、SF Symbolsをダウンロードしましょう。
・Swift UIファイルを新規作成し、SF Symbolsを使ってみましょう。
SF Symbolsアプリにてシンボルの名称をImageとして作成します。
また、デフォルトのシンボルはかなり小さいので、imageScale()
を使って大きさを決めましょう。
だたし、こうやるとシンボルのみサイズが変わるために、シンボルとセットになっているパーツを揃ってデザインを変えるのがおすすめです。CourseRow.swiftstruct CourseRow: View { var body: some View { HStack(alignment: .top) { Image(systemName: "paperplane.circle.fill") .renderingMode(.template) .frame(width: 48.0, height: 48.0) .imageScale(.medium) .foregroundColor(.white) } } } }・シンボルのフレームを48x48に、背景色と
clipShape(Circle())
で円形に切り取りで完成です。
CourseRow.swiftstruct CourseRow: View { var body: some View { HStack(alignment: .top) { Image(systemName: "paperplane.circle.fill") .renderingMode(.template) .frame(width: 48.0, height: 48.0) .imageScale(.medium) .background() .clipShape(Circle()) .foregroundColor(.white) VStack(alignment: .leading, spacing: 4.0) { Text("SwiftUI") .font(.subheadline) .bold() Text("Description") .font(.subheadline) .bold() } Spacer() } }リスト作成
・新規Swift UIファイルを作成し、先ほど作った
CourseRow()
を呼び出します。
・List{}
に詰め込めば完成しました。(UIKitのようにscrollViewがなくでもスクロールできて感動しました)
・Listのスタイルは何かあるのかをみたい時に、
option
を押しながらlistStyle
のドキュメントからチェックできます。
まとめ
・UIKitの半分のコードでlistを作れました。再びSwift UIの強さを感じました。
ソースコードはGithub
参考文献
- 投稿日:2021-03-06T17:53:18+09:00
iOSで電話番号がリンクになった時の対処法
備忘録として残します。
iOSで確認したところ、aタグを使っていないのにも関わらず電話番号がリンクとして表示されていたので調べてみました。
なぜリンクになるのか
iOSは、ユーザが電話番号をタップやクリックすると電話を掛けられるように
自動的にaタグを挿入する仕様になっているそう。対処法
以下のコードをheadタグ内に記述します。
<meta name="format-detection" content="telephone=no">CSSでは効かないのか?
以下の方法で試しましたが、やり方が悪いのか私の場合は効きませんでした。
a { text-decoration: none; color: #色; }参考:
- 投稿日:2021-03-06T17:44:15+09:00
【Swift】UISearchBarを利用してTableViewの表示を絞る
UISearchBarでTableViewの表示を絞る方法
最初にstoryboardでUISearchBarを配置し、ViewControllerと接続します。
@IBOutlet weak var searchBar: UISearchBar!次にViewControllerにUISearchBarDelegateプロコトルを適用させます。
class ViewController: UIViewController, UISearchBarDelegate{最後にsearchBarの処理をコードで実装します。
// searchButtonを押した時に実行されるメソッド func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { // tableViewに表示するデータリストをsearchBarに入力されたテキストで絞り込む members = members?.filter("name CONTAINS[cd] %@", searchBar.text!).sorted(byKeyPath: "name", ascending: true) tableView.reloadData() } // searchBarの入力が無くなったら絞り込みを解除する func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { if searchBar.text?.count == 0 { loadMembers() DispatchQueue.main.async { searchBar.resignFirstResponder() } } }
- 投稿日:2021-03-06T17:44:15+09:00
UISearchBarを利用してTableViewの表示を絞る
UISearchBarでTableViewの表示を絞る方法
最初にstoryboardでUISearchBarを配置し、ViewControllerと接続します。
@IBOutlet weak var searchBar: UISearchBar!ViewControllerにUISearchBarDelegateプロコトルを適用させます。
class ViewController: UIViewController, UISearchBarDelegate{
searchBarの処理をコードで実装します。// searchButtonを押した時に実行されるメソッド func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { // tableViewに表示するデータリストをsearchBarに 入力されたテキストで絞り込む members = members?.filter("name CONTAINS[cd] %@", searchBar.text!).sorted(byKeyPath: "name", ascending: true) tableView.reloadData() } // searchBarの入力が無くなったら絞り込みを解除する func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { if searchBar.text?.count == 0 { loadMembers() DispatchQueue.main.async { searchBar.resignFirstResponder() } } }
- 投稿日:2021-03-06T15:52:01+09:00
【Swift】Realmでproblem 'Index is out of bounds (must be less than 1)とエラーが出たときの対処法
RealmでTableViewにデータを表示しようとした時、タイトルのエラーが起きたので対応方法を記します。
このエラーはRealmのデータ更新時、Realmに保存されているモデルの件数と表示されているTableViewの件数(TableViewの認識している件数が以前のままで)の整合性がとれなくエラーになるようです。var participants: Results<Participant>? // cellにデータを表示 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: CELL_ID) as! AttendanceTableViewCell cell.delegate = self cell.index = indexPath cell.textLabel?.text = participants?[save:indexPath.row].name } // Resultsを拡張 extension Results { subscript (save index: Int) -> Element? { if index < self.count { return self[index] } else { return nil } }}
- 投稿日:2021-03-06T15:52:01+09:00
Realmでproblem 'Index 1 is out of bounds (must be less than 1)とエラーが出たときの対処法
RealmでTableViewにデータを表示しようとした時、タイトルのエラーが起きたので対応方法を記します。
このエラーはRealmのデータ更新時、Realmに保存されているモデルの件数と表示されているTableViewの件数(TableViewの認識している件数が以前のままで)の整合性がとれなくエラーになるようです。var participants: Results<Participant>? // cellにデータを表示 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: CELL_ID) as! AttendanceTableViewCell cell.delegate = self cell.index = indexPath cell.textLabel?.text = participants?[save:indexPath.row].name } // Resultsを拡張 extension Results { subscript (save index: Int) -> Element? { if index < self.count { return self[index] } else { return nil } }}
- 投稿日:2021-03-06T13:55:27+09:00
[SwiftUI]Widgetでサイズごとに値を変える
iOSのWidgetでサイズごとに文字サイズなどを変えるためのメモ。
実装
View内でWidgetFamilyの値を取得
@Environment(\.widgetFamily) var widgetFamilyWidgetFamilyごとにFontサイズを変える
var font: Font { switch self.widgetFamily { case .systemSmall: return .system(size: 10) case .systemMedium: return .system(size: 20) case .systemLarge: return .system(size: 30) @unknown default: return .system(size: 10) } } var body: some View { Text(entry.date, style: .time) .font(self.font) }結果
WidgetFamilyごとに文字サイズを変えることができました。
参考
- 投稿日:2021-03-06T11:32:00+09:00
Initialization Closureとglobal定数を組み合わせて、computedライクなglobal定数を作る
Initialization Closureとは?
Initialization closureは、以下の特徴を持ちます
- closure内でpropertyに操作を加えられる
- stored propertyである(値が割り当てられるタイミングはclassからinstanceが生成された時)
If a stored property’s default value requires some customization or setup, you can >use a closure or global function to provide a customized default value for that >property. Whenever a new instance of the type that the property belongs to is >initialized, the closure or function is called, and its return value is assigned >as the property’s default value.
主にUIをコードで記述する際に用いられます。
参考記事Globalな定数(変数)の特徴
Globalな定数(変数)は以下の特徴を持つ
- lazyである(割り当てられるタイミングは値が呼ばれた時)
Global constants and variables are always computed lazily, in a similar manner to >Lazy Stored Properties. Unlike lazy stored properties, global constants and >variables don’t need to be marked with the lazy modifier.
Local constants and variables are never computed lazily.それぞれの特徴を組み合わせると、computedライクなglobal定数を作ることができます。
ViewController.swiftlet somePattern: FetchedValue = { let defalutValue = "hoge" return isFetchedCompleted ? fetchedValue : defaultValue }()これは、外部サービスを使って、複数画面でABテストを行いたい/アプリの挙動を変えたい場合に以下の性質を持つので便利です。
- lazyなので、アプリ起動後にfetchが間に合わない場合でも、対象画面に遷移するまで時間を稼げる(=テストパターンに割り振られるユーザー数を増やせる)
- globalなので、各画面でfetchを行わなくて良い
- 各画面でfetchを行わないので、画面毎に挙動が異なることがない
- 投稿日:2021-03-06T10:41:35+09:00
UITableViewの高さをcell数に応じて可変にしたい
課題事例
- 元々、画面全体がUIScrollViewになっていて、真ん中にUITableViewを差し込みたい(※新規画面なら全体をUITableViewにすれば済む話ではあります)。
- 画面全体をスクロールさせる点に変わりはないので、UITableViewはスクロールさせない。
- UITableViewはcell数が可変、かつcellごとの高さも違っていて、それに応じてUITableViewの高さを変えたい(そうしないと全てのcellが表示されなかったり、余白ができてしまったりするので)。
解決法
(1) 高さのconstraintを変数に格納。
private weak var tableViewHeightConstraint: NSLayoutConstraint?(2) KVO(Key Value Observing)でUITableViewのcontentSize.heightを監視。
NSKeyValueObservationを格納するための変数を宣言し、UITableViewのcontentSize.heightをobserve。
heightが変わった時に上の制約のconstantをセットする処理を定義する。private var observation: NSKeyValueObservation? override func viewDidLoad() { // 略 observation = tableView.observe(\.contentSize, options: [.new]) { [weak self] (_, _) in guard let self = self else { return } self.tableViewHeightConstraint?.constant = self.tableView.contentSize.height } }(3) 後始末
deinit { observation?.invalidate() observation = nil }私は以上のような解決法で課題をクリアできましたが、もっとシンプルな解決法をご存知でしたら、ぜひ教えてください。
- 投稿日:2021-03-06T10:04:09+09:00
[WIP]WKHTTPCookieStoreコード解析
はじめに
中々開発者泣かせなWebKitのクッキー管理、色々はまりどころがあり、とうとうAppleもWKWebViewの移行期限を無期限に伸ばしました。と言うわけで中がどうなっているか解析していきます。
WebKitのソースコード
WebKitのプロセス構成
セキュリティ対策や重たいコンテンツを読み込んでもアプリが落ちないように、WebKitはここやここ、ここにある通り、UIProcess, WebProcess, NetworkProcess, StorageProcess(WorkerProcess)と4つのプロセスに分かれています。それぞれのプロセスはIPCでやりとりしています。
Cookieの取得
Cookieの取得は以下のメソッドを通じて行います。
class WKHTTPCookieStore func getAllCookies(_ completionHandler: @escaping ([HTTPCookie]) -> Void)UIプロセス側のコールスタック
取得完了ハンドラーに復帰した時のUIスレッド(プロセス)のコールスタックは以下になります。イベントループ(RunLoop)から
WebKit〜IPC受信〜WebKit APIと呼び出しているのが分かります。#1 0x000000018e6b5250 in WTF::Detail::CallableWrapper<WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>, void, WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&>::call(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&) () #2 0x000000018ea2b6c4 in WTF::Detail::CallableWrapper<API::HTTPCookieStore::filterAppBoundCookies(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)>&&)::$_0, void, WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&>::call(WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&) () #3 0x000000018ea0fa68 in WTF::Detail::CallableWrapper<WebKit::WebsiteDataStore::getAppBoundDomains(WTF::CompletionHandler<void (WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&)>&&) const::$_12, void, WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&>::call(WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&) () #4 0x000000018ea0c028 in WTF::CompletionHandler<void (WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&)>::operator()(WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&) () #5 0x000000018ea0c1a0 in WebKit::WebsiteDataStore::getAppBoundDomains(WTF::CompletionHandler<void (WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&)>&&) const () #6 0x000000018ea28ab0 in API::HTTPCookieStore::filterAppBoundCookies(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)>&&) () #7 0x000000018ea2bfc4 in WTF::Detail::CallableWrapper<API::HTTPCookieStore::cookies(WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>&&)::$_2, void, WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&>::call(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&) () #8 0x000000018ec0e3c8 in Messages::WebCookieManager::GetAllCookies::callReply(IPC::Decoder&, WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)>&&) () #9 0x000000018e92a8f4 in WTF::Detail::CallableWrapper<WebKit::AuxiliaryProcessProxy::sendMessage(std::__1::unique_ptr<IPC::Encoder, std::__1::default_delete<IPC::Encoder> >, WTF::OptionSet<IPC::SendOption>, WTF::Optional<std::__1::pair<WTF::CompletionHandler<void (IPC::Decoder*)>, unsigned long long> >&&, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::$_0, void, IPC::Decoder*>::call(IPC::Decoder*) () #10 0x000000018e683dd8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) () #11 0x000000018e6866c8 in WTF::Detail::CallableWrapper<IPC::Connection::enqueueIncomingMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >)::$_7, void>::call() () #12 0x000000018c3ac5e0 in WTF::RunLoop::performWork() () #13 0x000000018c3ad2c8 in WTF::RunLoop::performWork(void*) () #14 0x000000018203d76c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () #15 0x000000018203d668 in __CFRunLoopDoSource0 () #16 0x000000018203c960 in __CFRunLoopDoSources0 () #17 0x0000000182036a8c in __CFRunLoopRun () #18 0x000000018203621c in CFRunLoopRunSpecific () #19 0x0000000199c02784 in GSEventRunModal () #20 0x0000000184a76ee8 in -[UIApplication _run] () #21 0x0000000184a7c75c in UIApplicationMain () #22 0x0000000104b308a0 in main at /app/main.m:22 #23 0x0000000181cf66b0 in start ()Webプロセス側の解析
前述のコールスタックにある
WTF::Detail::CallableWrapper<API::HTTPCookieStore::cookies(WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>&&)::$_2, void, WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul,より、APIHTTPCookieStore.mを見ると、
auto* cookieManager = pool->supplement<WebKit::WebCookieManagerProxy>(); cookieManager->getAllCookies(m_owningDataStore->sessionID(), [this, protectedThis = makeRef(*this), pool = WTFMove(pool), completionHandler = WTFMove(completionHandler)] (const Vector<WebCore::Cookie>& cookies) mutable { filterAppBoundCookies(cookies, WTFMove(completionHandler)); });WebCookieManagerProxyと言う記載があります。コールスタックにもありますね。〜Proxyと言うのはProxyパターンの事を指します。Proxyを外して
WebCookieManagerで検索すると、WebCookieManager.h、WebCookieManager.cppと言うファイルがでてきました。ここからはNetworkProcess側の解析になります。コールスタックをさらに解析するとgetAllCookiesと言う記載があります。多分↓のメソッドを呼び出しているのでしょう。
void WebCookieManager::getAllCookies(PAL::SessionID sessionID, CompletionHandler<void(Vector<WebCore::Cookie>&&)>&& completionHandler) { Vector<Cookie> cookies; if (auto* storageSession = m_process.storageSession(sessionID)) cookies = storageSession->getAllCookies(); completionHandler(WTFMove(cookies)); }storageSession->getAllCookies()でクッキを取り出しています。と言うわけでm_process、storageSessionを見てみます。
m_processを調べるとNetworkProcessクラスのインスタンス変数です。NetworkProcessクラスのstorageSessionメソッドを見てみます。WebCore::NetworkStorageSession* storageSession(const PAL::SessionID&) const;NetworkStorageSessionと言うクラスを返すようになっています。クラス名、NetworkStorageSessionで定義しているメソッドを見るとCookieやキャッシュのに永続化に関するいろんな仕事をやるようです。
後、NetworkStorageSessionCocoa.mmと言うファイル名から、プラットホーム依存のクラスらしいです。
で、↑よりgetAllCookiesメソッドを調査するとVector<Cookie> NetworkStorageSession::getAllCookies() { ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies)); return nsCookiesToCookieVector(nsCookieStorage().cookies); }↑よりさらにnsCookieStorage()を調べると
WEBCORE_EXPORT NSHTTPCookieStorage *nsCookieStorage() const;と言う宣言がありました。実装は見当たりませんでした。よく知っているNSHTTPCookieStorageクラスです。
まとめ
Cookieの取り出しを見ると、WebProcessにあるNSHTTPCookieStorageからCookieを取り出してUIProcessに返しているだけでした。
Cookieの設定
Cookieの設定は以下のメソッドを通じて行います。
class WKHTTPCookieStore func setCookie(_ cookie: HTTPCookie, completionHandler: (() -> Void)? = nil)UIプロセス側のコールスタック
設定完了ハンドラーに復帰した時のUIスレッド(プロセス)のコールスタックは以下になります。
#1 0x000000018ea2c3f8 in WTF::Detail::CallableWrapper<auto API::HTTPCookieStore::setCookies(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::CompletionHandler<void ()>&&)::$_4::operator()<WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> >(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)::'lambda'(), void>::call() () #2 0x000000018e9821a8 in WTF::Detail::CallableWrapper<void WebKit::AuxiliaryProcessProxy::sendWithAsyncReply<Messages::WebCookieManager::SetCookie, WTF::CompletionHandler<void ()> >(Messages::WebCookieManager::SetCookie&&, WTF::CompletionHandler<void ()>&&, unsigned long long, WTF::OptionSet<IPC::SendOption>, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::'lambda'(IPC::Decoder*), void, IPC::Decoder*>::call(IPC::Decoder*) () #3 0x000000018e92a8f4 in WTF::Detail::CallableWrapper<WebKit::AuxiliaryProcessProxy::sendMessage(std::__1::unique_ptr<IPC::Encoder, std::__1::default_delete<IPC::Encoder> >, WTF::OptionSet<IPC::SendOption>, WTF::Optional<std::__1::pair<WTF::CompletionHandler<void (IPC::Decoder*)>, unsigned long long> >&&, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::$_0, void, IPC::Decoder*>::call(IPC::Decoder*) () #4 0x000000018e683dd8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) () #5 0x000000018e6866c8 in WTF::Detail::CallableWrapper<IPC::Connection::enqueueIncomingMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >)::$_7, void>::call() () #6 0x000000018c3ac5e0 in WTF::RunLoop::performWork() () #7 0x000000018c3ad2c8 in WTF::RunLoop::performWork(void*) () #8 0x000000018203d76c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () #9 0x000000018203d668 in __CFRunLoopDoSource0 () #10 0x000000018203c960 in __CFRunLoopDoSources0 () #11 0x0000000182036a8c in __CFRunLoopRun () #12 0x000000018203621c in CFRunLoopRunSpecific () #13 0x0000000199c02784 in GSEventRunModal () #14 0x0000000184a76ee8 in -[UIApplication _run] () #15 0x0000000184a7c75c in UIApplicationMain () #16 0x0000000104b308a0 in main at /app/main.m:22 #17 0x0000000181cf66b0 in start ()Webプロセス側の解析
WIP
Cookieの削除
Cookieの削除は以下のメソッドを通じて行います。
class WKHTTPCookieStore func delete(_ cookie: HTTPCookie, completionHandler: (() -> Void)? = nil)UIプロセス側のコールスタック
削除完了ハンドラーに復帰した時のUIスレッド(プロセス)のコールスタックは以下になります。
#1 0x000000018ea2c638 in WTF::Detail::CallableWrapper<API::HTTPCookieStore::deleteCookie(WebCore::Cookie const&, WTF::CompletionHandler<void ()>&&)::$_6, void>::call() () #2 0x000000018e981ebc in WTF::Detail::CallableWrapper<void WebKit::AuxiliaryProcessProxy::sendWithAsyncReply<Messages::WebCookieManager::DeleteCookie, WebKit::WebCookieManagerProxy::deleteCookie(PAL::SessionID, WebCore::Cookie const&, WTF::CompletionHandler<void ()>&&)::$_0>(Messages::WebCookieManager::DeleteCookie&&, WebKit::WebCookieManagerProxy::deleteCookie(PAL::SessionID, WebCore::Cookie const&, WTF::CompletionHandler<void ()>&&)::$_0&&, unsigned long long, WTF::OptionSet<IPC::SendOption>, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::'lambda'(IPC::Decoder*), void, IPC::Decoder*>::call(IPC::Decoder*) () #3 0x000000018e92a8f4 in WTF::Detail::CallableWrapper<WebKit::AuxiliaryProcessProxy::sendMessage(std::__1::unique_ptr<IPC::Encoder, std::__1::default_delete<IPC::Encoder> >, WTF::OptionSet<IPC::SendOption>, WTF::Optional<std::__1::pair<WTF::CompletionHandler<void (IPC::Decoder*)>, unsigned long long> >&&, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::$_0, void, IPC::Decoder*>::call(IPC::Decoder*) () #4 0x000000018e683dd8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) () #5 0x000000018e6866c8 in WTF::Detail::CallableWrapper<IPC::Connection::enqueueIncomingMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >)::$_7, void>::call() () #6 0x000000018c3ac5e0 in WTF::RunLoop::performWork() () #7 0x000000018c3ad2c8 in WTF::RunLoop::performWork(void*) () #8 0x000000018203d76c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () #9 0x000000018203d668 in __CFRunLoopDoSource0 () #10 0x000000018203c960 in __CFRunLoopDoSources0 () #11 0x0000000182036a8c in __CFRunLoopRun () #12 0x000000018203621c in CFRunLoopRunSpecific () #13 0x0000000199c02784 in GSEventRunModal () #14 0x0000000184a76ee8 in -[UIApplication _run] () #15 0x0000000184a7c75c in UIApplicationMain () #16 0x0000000104b308a0 in main at /app/main.m:22 #17 0x0000000181cf66b0 in start ()Webプロセス側の解析
WIP
疑問
ソースを読むとよくでてくるWTFってなんの略? WebKit Transport Frameworkの略?
- 投稿日:2021-03-06T10:04:09+09:00
WKHTTPCookieStoreクラスコード解析
はじめに
中々開発者泣かせなWebKitのクッキー管理、色々はまりどころがあり、とうとうAppleもWKWebViewの移行期限を無期限に伸ばしました。と言うわけでソースは公開されているので中がどうなっているか解析していきます。
WebKitのソースコード
WebKitのプロセス構成
セキュリティ対策や重たいコンテンツを読み込んでもアプリが落ちないように、WebKitはここやここ、ここにある通り、UIProcess, WebProcess, NetworkProcess, StorageProcess(WorkerProcess)と4つのプロセスに分かれています。それぞれのプロセスはIPCでやりとりしています。
Cookieの取得
Cookieの取得は以下のメソッドを通じて行います。
class WKHTTPCookieStore func getAllCookies(_ completionHandler: @escaping ([HTTPCookie]) -> Void)UIプロセス側のコールスタック
取得完了ハンドラーに復帰した時のUIスレッド(プロセス)のコールスタックは以下になります。イベントループ(RunLoop)から
WebKit〜IPC受信〜WebKit APIと呼び出しているのが分かります。#1 0x000000018e6b5250 in WTF::Detail::CallableWrapper<WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>, void, WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&>::call(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&) () #2 0x000000018ea2b6c4 in WTF::Detail::CallableWrapper<API::HTTPCookieStore::filterAppBoundCookies(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)>&&)::$_0, void, WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&>::call(WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&) () #3 0x000000018ea0fa68 in WTF::Detail::CallableWrapper<WebKit::WebsiteDataStore::getAppBoundDomains(WTF::CompletionHandler<void (WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&)>&&) const::$_12, void, WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&>::call(WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&) () #4 0x000000018ea0c028 in WTF::CompletionHandler<void (WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&)>::operator()(WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&) () #5 0x000000018ea0c1a0 in WebKit::WebsiteDataStore::getAppBoundDomains(WTF::CompletionHandler<void (WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&)>&&) const () #6 0x000000018ea28ab0 in API::HTTPCookieStore::filterAppBoundCookies(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)>&&) () #7 0x000000018ea2bfc4 in WTF::Detail::CallableWrapper<API::HTTPCookieStore::cookies(WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>&&)::$_2, void, WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&>::call(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&) () #8 0x000000018ec0e3c8 in Messages::WebCookieManager::GetAllCookies::callReply(IPC::Decoder&, WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)>&&) () #9 0x000000018e92a8f4 in WTF::Detail::CallableWrapper<WebKit::AuxiliaryProcessProxy::sendMessage(std::__1::unique_ptr<IPC::Encoder, std::__1::default_delete<IPC::Encoder> >, WTF::OptionSet<IPC::SendOption>, WTF::Optional<std::__1::pair<WTF::CompletionHandler<void (IPC::Decoder*)>, unsigned long long> >&&, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::$_0, void, IPC::Decoder*>::call(IPC::Decoder*) () #10 0x000000018e683dd8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) () #11 0x000000018e6866c8 in WTF::Detail::CallableWrapper<IPC::Connection::enqueueIncomingMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >)::$_7, void>::call() () #12 0x000000018c3ac5e0 in WTF::RunLoop::performWork() () #13 0x000000018c3ad2c8 in WTF::RunLoop::performWork(void*) () #14 0x000000018203d76c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () #15 0x000000018203d668 in __CFRunLoopDoSource0 () #16 0x000000018203c960 in __CFRunLoopDoSources0 () #17 0x0000000182036a8c in __CFRunLoopRun () #18 0x000000018203621c in CFRunLoopRunSpecific () #19 0x0000000199c02784 in GSEventRunModal () #20 0x0000000184a76ee8 in -[UIApplication _run] () #21 0x0000000184a7c75c in UIApplicationMain () #22 0x0000000104b308a0 in main at /app/main.m:22 #23 0x0000000181cf66b0 in start ()Webプロセス側の解析
前述のコールスタックにある
WTF::Detail::CallableWrapper<API::HTTPCookieStore::cookies(WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>&&)::$_2, void, WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul,より、APIHTTPCookieStore.mを見ると、
auto* cookieManager = pool->supplement<WebKit::WebCookieManagerProxy>(); cookieManager->getAllCookies(m_owningDataStore->sessionID(), [this, protectedThis = makeRef(*this), pool = WTFMove(pool), completionHandler = WTFMove(completionHandler)] (const Vector<WebCore::Cookie>& cookies) mutable { filterAppBoundCookies(cookies, WTFMove(completionHandler)); });WebCookieManagerProxyと言う記載があります。コールスタックにもありますね。〜Proxyと言うのはProxyパターンの事を指します。実態クラスがあるはずなんで、Proxyを外して
WebCookieManagerで検索すると、WebCookieManager.h、WebCookieManager.cppと言うファイルがでてきました。ここからはNetworkProcess側の解析になります。コールスタックをさらに解析するとgetAllCookiesと言う記載があります。多分↓のメソッドを呼び出しているのでしょう。
void WebCookieManager::getAllCookies(PAL::SessionID sessionID, CompletionHandler<void(Vector<WebCore::Cookie>&&)>&& completionHandler) { Vector<Cookie> cookies; if (auto* storageSession = m_process.storageSession(sessionID)) cookies = storageSession->getAllCookies(); completionHandler(WTFMove(cookies)); }storageSession->getAllCookies()でクッキを取り出しています。と言うわけでm_process、storageSessionを見てみます。
m_processを調べるとNetworkProcessクラスのインスタンス変数です。NetworkProcessクラスのstorageSessionメソッドを見てみます。WebCore::NetworkStorageSession* storageSession(const PAL::SessionID&) const;NetworkStorageSessionと言うクラスを返すようになっています。クラス名、NetworkStorageSessionで定義しているメソッドを見るとCookieやキャッシュのに永続化に関するいろんな仕事をやるようです。
後、NetworkStorageSessionCocoa.mmと言うファイル名から、プラットホーム依存のクラスらしいです。
で、↑よりgetAllCookiesメソッドを調査するとVector<Cookie> NetworkStorageSession::getAllCookies() { ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies)); return nsCookiesToCookieVector(nsCookieStorage().cookies); }↑よりさらにnsCookieStorage()を調べると
WEBCORE_EXPORT NSHTTPCookieStorage *nsCookieStorage() const;と言う宣言がありました。よく知っているNSHTTPCookieStorageクラスです。実装は見当たりませんでした。
まとめ
Cookieの取り出しを見ると、WebProcessにあるNSHTTPCookieStorageからCookieを取り出してUIProcessに返しているだけでした。
Cookieの設定
Cookieの設定は以下のメソッドを通じて行います。
class WKHTTPCookieStore func setCookie(_ cookie: HTTPCookie, completionHandler: (() -> Void)? = nil)UIプロセス側のコールスタック
設定完了ハンドラーに復帰した時のUIスレッド(プロセス)のコールスタックは以下になります。
#1 0x000000018ea2c3f8 in WTF::Detail::CallableWrapper<auto API::HTTPCookieStore::setCookies(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::CompletionHandler<void ()>&&)::$_4::operator()<WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> >(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)::'lambda'(), void>::call() () #2 0x000000018e9821a8 in WTF::Detail::CallableWrapper<void WebKit::AuxiliaryProcessProxy::sendWithAsyncReply<Messages::WebCookieManager::SetCookie, WTF::CompletionHandler<void ()> >(Messages::WebCookieManager::SetCookie&&, WTF::CompletionHandler<void ()>&&, unsigned long long, WTF::OptionSet<IPC::SendOption>, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::'lambda'(IPC::Decoder*), void, IPC::Decoder*>::call(IPC::Decoder*) () #3 0x000000018e92a8f4 in WTF::Detail::CallableWrapper<WebKit::AuxiliaryProcessProxy::sendMessage(std::__1::unique_ptr<IPC::Encoder, std::__1::default_delete<IPC::Encoder> >, WTF::OptionSet<IPC::SendOption>, WTF::Optional<std::__1::pair<WTF::CompletionHandler<void (IPC::Decoder*)>, unsigned long long> >&&, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::$_0, void, IPC::Decoder*>::call(IPC::Decoder*) () #4 0x000000018e683dd8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) () #5 0x000000018e6866c8 in WTF::Detail::CallableWrapper<IPC::Connection::enqueueIncomingMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >)::$_7, void>::call() () #6 0x000000018c3ac5e0 in WTF::RunLoop::performWork() () #7 0x000000018c3ad2c8 in WTF::RunLoop::performWork(void*) () #8 0x000000018203d76c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () #9 0x000000018203d668 in __CFRunLoopDoSource0 () #10 0x000000018203c960 in __CFRunLoopDoSources0 () #11 0x0000000182036a8c in __CFRunLoopRun () #12 0x000000018203621c in CFRunLoopRunSpecific () #13 0x0000000199c02784 in GSEventRunModal () #14 0x0000000184a76ee8 in -[UIApplication _run] () #15 0x0000000184a7c75c in UIApplicationMain () #16 0x0000000104b308a0 in main at /app/main.m:22 #17 0x0000000181cf66b0 in start ()Webプロセス側の解析
取り出しとほぼ同じ機構のはず。
Cookieの削除
Cookieの削除は以下のメソッドを通じて行います。
class WKHTTPCookieStore func delete(_ cookie: HTTPCookie, completionHandler: (() -> Void)? = nil)UIプロセス側のコールスタック
削除完了ハンドラーに復帰した時のUIスレッド(プロセス)のコールスタックは以下になります。
#1 0x000000018ea2c638 in WTF::Detail::CallableWrapper<API::HTTPCookieStore::deleteCookie(WebCore::Cookie const&, WTF::CompletionHandler<void ()>&&)::$_6, void>::call() () #2 0x000000018e981ebc in WTF::Detail::CallableWrapper<void WebKit::AuxiliaryProcessProxy::sendWithAsyncReply<Messages::WebCookieManager::DeleteCookie, WebKit::WebCookieManagerProxy::deleteCookie(PAL::SessionID, WebCore::Cookie const&, WTF::CompletionHandler<void ()>&&)::$_0>(Messages::WebCookieManager::DeleteCookie&&, WebKit::WebCookieManagerProxy::deleteCookie(PAL::SessionID, WebCore::Cookie const&, WTF::CompletionHandler<void ()>&&)::$_0&&, unsigned long long, WTF::OptionSet<IPC::SendOption>, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::'lambda'(IPC::Decoder*), void, IPC::Decoder*>::call(IPC::Decoder*) () #3 0x000000018e92a8f4 in WTF::Detail::CallableWrapper<WebKit::AuxiliaryProcessProxy::sendMessage(std::__1::unique_ptr<IPC::Encoder, std::__1::default_delete<IPC::Encoder> >, WTF::OptionSet<IPC::SendOption>, WTF::Optional<std::__1::pair<WTF::CompletionHandler<void (IPC::Decoder*)>, unsigned long long> >&&, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::$_0, void, IPC::Decoder*>::call(IPC::Decoder*) () #4 0x000000018e683dd8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) () #5 0x000000018e6866c8 in WTF::Detail::CallableWrapper<IPC::Connection::enqueueIncomingMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >)::$_7, void>::call() () #6 0x000000018c3ac5e0 in WTF::RunLoop::performWork() () #7 0x000000018c3ad2c8 in WTF::RunLoop::performWork(void*) () #8 0x000000018203d76c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () #9 0x000000018203d668 in __CFRunLoopDoSource0 () #10 0x000000018203c960 in __CFRunLoopDoSources0 () #11 0x0000000182036a8c in __CFRunLoopRun () #12 0x000000018203621c in CFRunLoopRunSpecific () #13 0x0000000199c02784 in GSEventRunModal () #14 0x0000000184a76ee8 in -[UIApplication _run] () #15 0x0000000184a7c75c in UIApplicationMain () #16 0x0000000104b308a0 in main at /app/main.m:22 #17 0x0000000181cf66b0 in start ()Webプロセス側の解析
取り出しとほぼ同じ機構のはず。
疑問
- ソースを読むとよくでてくるWTFってなんの略? WebKit Transport Framework?
- Cookie管理について。Androidは同期呼び出しで、iOSは非同期呼び出しなのはなんで? どっちもプロセス間通信が走っているけど。
- iOSの非公開APIにアクセスしまくっているけど、これはアップル主導のプロジェクトからなのか。
- 投稿日:2021-03-06T10:04:09+09:00
WKHTTPCookieStoreクラスのコード解析
はじめに
中々開発者泣かせなWebKitのクッキー管理。色々はまりどころがあり、とうとうAppleもWKWebViewの移行期限を無期限に伸ばしました。と言うわけでソースは公開されているので中がどうなっているか解析していきます。
Androidについては以前にこの記事で解説しています。
WebKitのソースコード
WebKitのプロセス構成
セキュリティ対策や重たいコンテンツを読み込んでもアプリが落ちないように、WebKitはここやここ、ここにある通り、UIProcess, WebProcess, NetworkProcess, StorageProcess(WorkerProcess)と4つのプロセスに分かれています。それぞれのプロセスはIPCでやりとりしています。
Cookieの取得
Cookieの取得は以下のメソッドを通じて行います。
class WKHTTPCookieStore func getAllCookies(_ completionHandler: @escaping ([HTTPCookie]) -> Void)UIプロセス側のコールスタック
取得完了ハンドラーに復帰した時のUIスレッド(プロセス)のコールスタックは以下になります。イベントループ(RunLoop)から
WebKit〜IPC受信〜WebKit APIと呼び出しているのが分かります。#1 0x000000018e6b5250 in WTF::Detail::CallableWrapper<WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>, void, WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&>::call(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&) () #2 0x000000018ea2b6c4 in WTF::Detail::CallableWrapper<API::HTTPCookieStore::filterAppBoundCookies(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)>&&)::$_0, void, WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&>::call(WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&) () #3 0x000000018ea0fa68 in WTF::Detail::CallableWrapper<WebKit::WebsiteDataStore::getAppBoundDomains(WTF::CompletionHandler<void (WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&)>&&) const::$_12, void, WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&>::call(WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&) () #4 0x000000018ea0c028 in WTF::CompletionHandler<void (WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&)>::operator()(WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&) () #5 0x000000018ea0c1a0 in WebKit::WebsiteDataStore::getAppBoundDomains(WTF::CompletionHandler<void (WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&)>&&) const () #6 0x000000018ea28ab0 in API::HTTPCookieStore::filterAppBoundCookies(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)>&&) () #7 0x000000018ea2bfc4 in WTF::Detail::CallableWrapper<API::HTTPCookieStore::cookies(WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>&&)::$_2, void, WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&>::call(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&) () #8 0x000000018ec0e3c8 in Messages::WebCookieManager::GetAllCookies::callReply(IPC::Decoder&, WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)>&&) () #9 0x000000018e92a8f4 in WTF::Detail::CallableWrapper<WebKit::AuxiliaryProcessProxy::sendMessage(std::__1::unique_ptr<IPC::Encoder, std::__1::default_delete<IPC::Encoder> >, WTF::OptionSet<IPC::SendOption>, WTF::Optional<std::__1::pair<WTF::CompletionHandler<void (IPC::Decoder*)>, unsigned long long> >&&, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::$_0, void, IPC::Decoder*>::call(IPC::Decoder*) () #10 0x000000018e683dd8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) () #11 0x000000018e6866c8 in WTF::Detail::CallableWrapper<IPC::Connection::enqueueIncomingMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >)::$_7, void>::call() () #12 0x000000018c3ac5e0 in WTF::RunLoop::performWork() () #13 0x000000018c3ad2c8 in WTF::RunLoop::performWork(void*) () #14 0x000000018203d76c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () #15 0x000000018203d668 in __CFRunLoopDoSource0 () #16 0x000000018203c960 in __CFRunLoopDoSources0 () #17 0x0000000182036a8c in __CFRunLoopRun () #18 0x000000018203621c in CFRunLoopRunSpecific () #19 0x0000000199c02784 in GSEventRunModal () #20 0x0000000184a76ee8 in -[UIApplication _run] () #21 0x0000000184a7c75c in UIApplicationMain () #22 0x0000000104b308a0 in main at /app/main.m:22 #23 0x0000000181cf66b0 in start ()Webプロセス側の解析
前述のコールスタックにある
WTF::Detail::CallableWrapper<API::HTTPCookieStore::cookies(WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>&&)::$_2, void, WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul,より、APIHTTPCookieStore.mを見ると、
auto* cookieManager = pool->supplement<WebKit::WebCookieManagerProxy>(); cookieManager->getAllCookies(m_owningDataStore->sessionID(), [this, protectedThis = makeRef(*this), pool = WTFMove(pool), completionHandler = WTFMove(completionHandler)] (const Vector<WebCore::Cookie>& cookies) mutable { filterAppBoundCookies(cookies, WTFMove(completionHandler)); });WebCookieManagerProxyと言う記載があります。コールスタックにもありますね。〜Proxyと言うのはProxyパターンの事を指します。実態クラスがあるはずなんで、Proxyを外して
WebCookieManagerで検索すると、WebCookieManager.h、WebCookieManager.cppと言うファイルがでてきました。ここからはNetworkProcess側の解析になります。コールスタックをさらに解析するとgetAllCookiesと言う記載があります。多分↓のメソッドを呼び出しているのでしょう。
void WebCookieManager::getAllCookies(PAL::SessionID sessionID, CompletionHandler<void(Vector<WebCore::Cookie>&&)>&& completionHandler) { Vector<Cookie> cookies; if (auto* storageSession = m_process.storageSession(sessionID)) cookies = storageSession->getAllCookies(); completionHandler(WTFMove(cookies)); }storageSession->getAllCookies()でクッキを取り出しています。と言うわけでm_process、storageSessionを見てみます。
m_processを調べるとNetworkProcessクラスのインスタンス変数です。NetworkProcessクラスのstorageSessionメソッドを見てみます。WebCore::NetworkStorageSession* storageSession(const PAL::SessionID&) const;NetworkStorageSessionと言うクラスを返すようになっています。クラス名、NetworkStorageSessionで定義しているメソッドを見るとCookieやキャッシュのに永続化に関するいろんな仕事をやるようです。
後、NetworkStorageSessionCocoa.mmと言うファイル名から、プラットホーム依存のクラスらしいです。
で、↑よりgetAllCookiesメソッドを調査するとVector<Cookie> NetworkStorageSession::getAllCookies() { ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies)); return nsCookiesToCookieVector(nsCookieStorage().cookies); }↑よりさらにnsCookieStorage()を調べると
WEBCORE_EXPORT NSHTTPCookieStorage *nsCookieStorage() const;と言う宣言がありました。よく知っているNSHTTPCookieStorageクラスです。実装は見当たりませんでした。
まとめ
Cookieの取り出しを見ると、WebProcessにあるNSHTTPCookieStorageからCookieを取り出してUIProcessに返しているだけでした。
Cookieの設定
Cookieの設定は以下のメソッドを通じて行います。
class WKHTTPCookieStore func setCookie(_ cookie: HTTPCookie, completionHandler: (() -> Void)? = nil)UIプロセス側のコールスタック
設定完了ハンドラーに復帰した時のUIスレッド(プロセス)のコールスタックは以下になります。
#1 0x000000018ea2c3f8 in WTF::Detail::CallableWrapper<auto API::HTTPCookieStore::setCookies(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::CompletionHandler<void ()>&&)::$_4::operator()<WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> >(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)::'lambda'(), void>::call() () #2 0x000000018e9821a8 in WTF::Detail::CallableWrapper<void WebKit::AuxiliaryProcessProxy::sendWithAsyncReply<Messages::WebCookieManager::SetCookie, WTF::CompletionHandler<void ()> >(Messages::WebCookieManager::SetCookie&&, WTF::CompletionHandler<void ()>&&, unsigned long long, WTF::OptionSet<IPC::SendOption>, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::'lambda'(IPC::Decoder*), void, IPC::Decoder*>::call(IPC::Decoder*) () #3 0x000000018e92a8f4 in WTF::Detail::CallableWrapper<WebKit::AuxiliaryProcessProxy::sendMessage(std::__1::unique_ptr<IPC::Encoder, std::__1::default_delete<IPC::Encoder> >, WTF::OptionSet<IPC::SendOption>, WTF::Optional<std::__1::pair<WTF::CompletionHandler<void (IPC::Decoder*)>, unsigned long long> >&&, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::$_0, void, IPC::Decoder*>::call(IPC::Decoder*) () #4 0x000000018e683dd8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) () #5 0x000000018e6866c8 in WTF::Detail::CallableWrapper<IPC::Connection::enqueueIncomingMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >)::$_7, void>::call() () #6 0x000000018c3ac5e0 in WTF::RunLoop::performWork() () #7 0x000000018c3ad2c8 in WTF::RunLoop::performWork(void*) () #8 0x000000018203d76c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () #9 0x000000018203d668 in __CFRunLoopDoSource0 () #10 0x000000018203c960 in __CFRunLoopDoSources0 () #11 0x0000000182036a8c in __CFRunLoopRun () #12 0x000000018203621c in CFRunLoopRunSpecific () #13 0x0000000199c02784 in GSEventRunModal () #14 0x0000000184a76ee8 in -[UIApplication _run] () #15 0x0000000184a7c75c in UIApplicationMain () #16 0x0000000104b308a0 in main at /app/main.m:22 #17 0x0000000181cf66b0 in start ()Webプロセス側の解析
取り出しとほぼ同じ機構のはず。
Cookieの削除
Cookieの削除は以下のメソッドを通じて行います。
class WKHTTPCookieStore func delete(_ cookie: HTTPCookie, completionHandler: (() -> Void)? = nil)UIプロセス側のコールスタック
削除完了ハンドラーに復帰した時のUIスレッド(プロセス)のコールスタックは以下になります。
#1 0x000000018ea2c638 in WTF::Detail::CallableWrapper<API::HTTPCookieStore::deleteCookie(WebCore::Cookie const&, WTF::CompletionHandler<void ()>&&)::$_6, void>::call() () #2 0x000000018e981ebc in WTF::Detail::CallableWrapper<void WebKit::AuxiliaryProcessProxy::sendWithAsyncReply<Messages::WebCookieManager::DeleteCookie, WebKit::WebCookieManagerProxy::deleteCookie(PAL::SessionID, WebCore::Cookie const&, WTF::CompletionHandler<void ()>&&)::$_0>(Messages::WebCookieManager::DeleteCookie&&, WebKit::WebCookieManagerProxy::deleteCookie(PAL::SessionID, WebCore::Cookie const&, WTF::CompletionHandler<void ()>&&)::$_0&&, unsigned long long, WTF::OptionSet<IPC::SendOption>, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::'lambda'(IPC::Decoder*), void, IPC::Decoder*>::call(IPC::Decoder*) () #3 0x000000018e92a8f4 in WTF::Detail::CallableWrapper<WebKit::AuxiliaryProcessProxy::sendMessage(std::__1::unique_ptr<IPC::Encoder, std::__1::default_delete<IPC::Encoder> >, WTF::OptionSet<IPC::SendOption>, WTF::Optional<std::__1::pair<WTF::CompletionHandler<void (IPC::Decoder*)>, unsigned long long> >&&, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::$_0, void, IPC::Decoder*>::call(IPC::Decoder*) () #4 0x000000018e683dd8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) () #5 0x000000018e6866c8 in WTF::Detail::CallableWrapper<IPC::Connection::enqueueIncomingMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >)::$_7, void>::call() () #6 0x000000018c3ac5e0 in WTF::RunLoop::performWork() () #7 0x000000018c3ad2c8 in WTF::RunLoop::performWork(void*) () #8 0x000000018203d76c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ () #9 0x000000018203d668 in __CFRunLoopDoSource0 () #10 0x000000018203c960 in __CFRunLoopDoSources0 () #11 0x0000000182036a8c in __CFRunLoopRun () #12 0x000000018203621c in CFRunLoopRunSpecific () #13 0x0000000199c02784 in GSEventRunModal () #14 0x0000000184a76ee8 in -[UIApplication _run] () #15 0x0000000184a7c75c in UIApplicationMain () #16 0x0000000104b308a0 in main at /app/main.m:22 #17 0x0000000181cf66b0 in start ()Webプロセス側の解析
取り出しとほぼ同じ機構のはず。
疑問
- ソースを読むとよくでてくるWTFってなんの略? WebKit Transport Framework?
- Cookie管理について。Androidは同期呼び出しで、iOSは非同期呼び出しなのはなんで? どっちもプロセス間通信が走っているけど。
- iOSの非公開APIにアクセスしまくっているけど、これはアップル主導のプロジェクトからなのか。
- 投稿日:2021-03-06T08:45:12+09:00
[iOS] ダークモードを考慮したAsset Catalogの管理
本記事では、Swiftの画像の命名規則とAsset Catalogのフォルダ構成について、個人で最適だと考える方法について説明します。
画像の命名規則について
注意点
本記事では,画像のパターンが2種類あります。
例)sampleという画像の名前の場合
Asset Catalog内の画像
→ sampleAsset Catalogに追加する前の画像
→ sample.png命名規則
画像の命名は以下で統一
スネークケースを使用
ic_画像名_修飾.png
- 小さめなアイコンの画像
- 例:
ic_star_fill.png
img_画像名_修飾.png
- 大きめなillust, logoなどの画像
- 例:
img_logo.png
修飾について以下の例があります。
- fill
- off/onダークモード用の画像の場合
語尾にdarkを付ける
-ic_画像名_修飾_dark.png
-img_画像名_修飾_dark.png
ダークモード用の画像分けて命名する理由
iOSでは,同じ命名で各モードの画像をAsset Catalog内に追加すると,〇〇-1.pngのように重複によって番号が付与されてしまう.
内部的に不都合が生じるため,ダークモード用の画像の命名が必要であるAsset Catalogのフォルダ
現在のAsset Catalog内のフォルダ構成は以下の通りです。
Assets
- iconフォルダ
ic_画像名
- imageフォルダ
img_画像名
- colorフォルダ