Context とあるコードベースでXcodeのビルド時間がインクリメンタルビルドですら3分近くかかる問題がある 「ビルド時間おそいですね」課題感は共通認識なものの、総量として定量的にみてどれくらいの問題の大きさなのかが把握できていない状況でもある ビルド時間を開発速度のhealthcheck的な指標としてモニタリングしたい どうやるか XCMetricsというTHEソレのようなOSSをspotifyが公開していたりする github repo: https://github.com/spotify/XCMetrics (v0系ではあるものの)1st releaseが2021/01とわりと最近 vapor製 Swift大統一 dashboard UIがイケてる サービス然りアプリ然りspotifyちょいちょいイケてる 日本からリモートで働き口ないか探してみたが、BizDev/マーケ方面でしか募集してなさそう(Engineering職はもっぱら欧米リージョン。格差社会) https://www.lifeatspotify.com/jobs?l=tokyo XCMetricsはつかえるか🤔 Backend Deploymentをみると、アプリケーション/DBサーバの運用が必要 localhostでつかう分にはDocker imageも提供されているしサクッと動かせる 業務での運用を考えると、サーバをお守りするのはセキュリティ事がついてまわるので話が変わってくる アプリケーションサーバについてはCloud Runにオフロードする構成もとれるっぽいが、別でCloud SQLもとなるとコストの割高感が否めない.. インフラ管理不要なマネージドに寄せつつ低コストでおなじようなことをやりたいというモチベーションがあったりする XCMetricsはビルドログ収集どうやってるの XCLogParserをつかっている とは Xcodeがビルドログとして~/Library/Developer/Xcode/DerivedData/に吐き出す.xcactivitylog(gzip圧縮されたSLF encodedなファイル(?))があって、 これをparseするとビルドの詳細情報(どのschemeで/どのtargetで/どのソースファイルで/どれだけビルド時間がかかったか情報etc)がとれる 解析方法の情報源として@vincentisambart氏によるcookpad社のblog記事がlinkされてる(つよい..) このparse処理をラップしてビルドメトリクス情報をhtmlやjsonで出力できる、というツール なにげにmain contributorがXCMetricsと同じspotifyの人 ビルドログ情報はXCLogParserから取得できるから、以下が揃えばXCMetricsでやってるようなビルド時間可視化はいける ログデータの加工(postBuildScript) ログデータの保存(DB) ログデータの可視化(のfrontend) 「データがあってそれを可視化」といったらBigQuery+Data Portalの構成が降ってくる(というかWebアプリ以外だとそれしかおもいつかない.
Read more
github repo: https://github.com/hrfmmr/swiftui-sandbox
題材 「twsearch」という、Twitter APIのApplication’s only authenticationをつかったシンプルなtweet検索アプリ
モジュール構成 module type description twsearch application 本体アプリ AppFeature dynamic framework 各FeaturesをまとめあげるUmbrella的なFeatureモジュール SearchStatusFeature dynamic framework 検索機能のFeatureモジュール APIClient dynamic framework Twitter APIクライアント SwiftUIHelpers dynamic framework SwiftUIのヘルパ Core dynamic framework ドメインモデル定義など Umbrella static framework 共通で使用されるライブラリ群がembedされたモジュール 依存関係 Embedded Frameworkをつかったモジュール構成 XcodeGen project.yml
Read more
ツールを書いた。
https://github.com/hrfmmr/oasbuilder
Context シゴトでiOSアプリ開発をしていて、既存(REST)APIクライアントがSwiftyJSONやPromiseなど現在となってはSwift標準ライブラリで置き換えが可能なライブラリに依存している且つアプリケーションレイヤで扱うモデルにもこれらへの依存が発生していたので、これらをうまいことリファクタしたいなぁというモチベーションがあった、というコンテキスト。
APIクライアントとそれらにぶら下がる層、わりと巨大なので、地道なリファクタ以外の方法でシュッとやれないものか、と考えていたところ、(今更ながら)Open API Generatorの存在(+Swift w/ Alamofireでのクライアントコード生成も可能なこと)を知り、「APIスキーマさえ手に入ればコード生成したものでレガシーコード諸々置き換えイケそう」という考えに至った。一方で、API&モデルのボリュームがそこそこあるので、「動作しているアプリのAPIスキーマの自動生成」がテーマとなって今回のツール作成につながったかんじ。
どうやるか APIスキーマの自動生成、実はバックエンド側でREST APIサーバのライブラリでつかわれているGrape関連ツールのgrape-swaggerを用いたスキーマ生成が、deploy済APIを参照する用のSwagger UI定義としてすでに用いられていたので、このツールで吐かれたスキーマをつかえばクライアントコード生成も楽勝かとおもったが、実際のところpaginationなどの表現が欠けているなどして、そのまま流用することはできず、他の方法でスキーマを得る必要があった。
「アプリの実際の動き」を忠実に再現するスキーマデータを得るには、proxyツールを挟んで実際に流れるリクエスト/レスポンスのデータから生成するアプローチがいちばん手っ取り早いだろうと考えた。
イメージは以下
proxyツールとしてはmitmproxyを使い、以下のような流れでスキーマを生成
1 ~ 5 mitmproxyのPython APIフックで流れてくるリクエスト/レスポンス情報をElasticsearchにdumpするmitmproxy-elasticagentを介して、アプリに必要となるAPIスキーマのrawデータを収集する 6 ~ 8 Elasticsearchに保存されたデータをつかって、OpenAPI Specification(v3)準拠なyamlを生成する デモ Elasticsearch起動 $ elasticsearch ~/.mitmproxy/config.ymlにmitmproxy-elasticagent addonスクリプトを追加 scripts: - /path/to/mitmproxy-elasticagent/jsondump.py es_dest_url: http://127.0.0.1:9200/test-api/_doc es_target_host: api.example.com $ git clone https://github.com/hrfmmr/oasbuilder oasbuilder/.envをいじる ELASTICSEARCH_HOST=http://localhost:9200 ELASTICSEARCH_INDEX=test-api OAS_VERSION=0.1.0 OAS_TITLE=test api OAS_DESCRIPTION=test api OAS_SERVER_URLS=https://api.example.com make run && make previewでdumpされたJSONをOAS yamlにビルド後、プラウザでRedocドキュメント表示 実行風景 なにやってるの 右ペイン $ watch -n2 "curl -XGET -H 'Content-Type: application/json' 'http://127.
Read more
モバイルアプリ開発をしていて、REST APIのリクエスト/レスポンスのペアをログ保存しておいて、 あとでリクエスト内容やレスポンス内容別にサクッと検索したいことがあったので、 mitmproxyを介してサーバレスポンスをフックして、リクエスト/レスポンス情報をマシンlocalに立てたElasticsearch向けにdumpするといったaddonを書いた
どうやるの mitmproxyのHTTPイベントのEvent Hooksをつかう。 requestheaders/request/responseheaders/responseなどのフックが用意されていて、今回はレスポンス値を取り出したいので、responseフックをつかう。 responseフックは以下のようなシグネチャ def response(self, mitmproxy.http.HTTPFlow): HTTPFlowは、以下のようなclass class HTTPFlow(flow.Flow): """ An HTTPFlow is a collection of objects representing a single HTTP transaction. """ request: Request """The client's HTTP request.""" response: Optional[Response] = None """The server's HTTP response.""" error: Optional[flow.Error] = None """ A connection or protocol error affecting this flow. Note that it's possible for a Flow to have both a response and an error object.
Read more
には、PyChromecastなどのライブラリをつかえばかんたんにできるが、 中身が気になったので、castデバイスとソケットを張ってやりたいこと(=今回はとあるmp3を再生させるだけ)ができるまでに必要となるプロトコルなどいろいろあったのでそのメモ(現時点でこのメモが未来に役立つイメージはゼロ。)
Chromecast API v2プロトコル senderとreceiver sender castデバイスを操作するクライアント receiver castデバイス castクライアントは、TCP(w/TLS) 8009ポートでcastデバイスに接続する TCPコネクション確立後、senderとreceiverは固定長のバイナリメッセージを送り合う パケット構造は単純で、 Packet length | Payload(message body) の2部構成 Packet length uint32(ビッグエンディアン) Payload protobufでシリアライズされたバイナリ protoファイルは以下のようなもの message CastMessage { // Always pass a version of the protocol for future compatibility // requirements. enum ProtocolVersion { CASTV2_1_0 = 0; } required ProtocolVersion protocol_version = 1; // source and destination ids identify the origin and destination of the // message.
Read more
これはなに BigQueryでFirebase Analyticsイベントをファネル分析する上で、これをwalkthroughしたメモ。
デモアプリ 分析事、実務プロジェクトでやってもよいが、リアルに課金が発生するのを気にしてしまうのと、個人プロジェクトでは分析対象のデータがそもそも揃えるのが難しい。 sandboxな環境で分析事を試せる十分なデータセットがあるプロジェクト、Google先生が公開してくれているFirebaseデモプロジェクトがある。親切。 Firebase デモ プロジェクト - Firebase ヘルプ デモプロジェクトのiOSアプリでは、Flood-It!という実在するパズルゲームアプリのアナリティクスデータをつかって分析を行う 指定された手数以内で盤面を一色に塗りつぶすパズルゲームです ゴール no_more_extra_stepsを受けたユーザの内、どれだけspend_virtual_currencyにつながったか、を知りたい 2stepファネルをつくる カダイ はFirebase ConsoleでつくるとOpen Funnelになること(以下画像は上記ブログ記事から拝借したもの) 実際にデモプロジェクトのFirebase Consoleでファネルをつくろうとしたが、現在は権限が制限されているのか、新規の目標到達プロセスの作成ができなかった.. ファネルをみると、step1よりstep2の方が大きい値になってしまっている。step1に関係なくstep2イベントがカウントされている。 いわゆるオープンファネルになってしまっている。いまつくりたいのはクローズドファネル。 ちなみに、GA4ではクローズドな目標到達プロセスも作成できる(後述のイベント間の時間区間指定もできるし、イベントパラメータ指定などもできる。GUIポチポチで完結するので便利。) GA4 目標到達プロセスの分析 - アナリティクス ヘルプ 今回はFirebase EventsログをBigQueryで弄ぶ主旨なのでBQでやっていく BigQueryでどうやる 以降はFlood-It!アプリイベントのBigQueryデータセットのクエリエディタで叩いて進めていく
1.) まずは1日分のデータでユーザIDベースで発生イベントを時系列に出してみると SELECT event_name, user_pseudo_id, event_timestamp FROM `firebase-public-project.analytics_153293282.events_20180720` ORDER BY user_pseudo_id, event_timestamp LIMIT 1000 こんなふうに 2.
Read more
がわからなかったが、わかった気がした人間のメモ “わかる"とは 定義がわかる 作用のしくみがわかる ありがたみがわかる Context すごいHaskell本を読んだ Functional Swiftを読んだ わかっていること(たぶん) ファンクター(関手) 「文脈をもつ」値(以降、「箱」で表現する。個人的にイメージがしっくりくる為) 文脈what 「値があるかもしれないし、ないかもしれない」文脈 HaskellのMaybe SwiftのOptional 「成功しているかもしれないし、失敗しているかもしれない」文脈 HaskellのEither SwiftのResult fmap実装をもつ fmap Haskell class Functor f where fmap :: (a -> b) -> f a -> f b Swift Optional func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U? 通常の関数を受け取って、箱の中身に適用した結果で箱の中身を差し替える わかった気がすること a.
Read more
Situation 調べ物をしてる際、記事URLのメモを取ることがある メモはmarkdown記法をサポートしているエディタをつかっている 記事URLだけでは後で見返すときに識別しづらいので、[title](url)の形式で貼ることが多い ブラウザ(Chrome)で[title](url)をclipboardにコピーするには以下の手間を伴う 記事タイトルコピー 👈 キーボードで完結させづらくいちばんつらい アドレスバーへフォーカス [title](url)へ整形 全選択&コピー 億劫になってきた Motivation もっとラクに[title](url)をcopyしたい 念じるだけでできるのが理想 せめてコマンド1発 Solution Shift+Yで[title](url)をclipboardにコピーするChrome拡張を書く Alternative Considered 代替手段はとくにみてない chrome拡張書いたことなかったし、書いてみるよい機会とおもったのが大きい Action manifest.jsonを追加 { "manifest_version": 2, "name": "ymdlink", "author": "hrfmmr", "version": "0.0.1", "description": "yank current tab's title and link as markdown notation" } chrome://extensionsでロードする Load Unpackedでfile explorerを起動 ロードするextension prjディレクトリを選択 extensionsに追加される permissionsを設定 Chrome extensionアプリがchromeのAPIアクセスするには、パーミッションを明示的に設定する必要がある 📖 see Declare permissions 今回は「現在のタブ」のtitleとurlを「clipboardにコピー」するので、以下をmanifest.
Read more
Prerequisites foo.comのドメイン AWSアカウント blog.foo.com(ないしワイルドカード)のACM証明書(us-east-1 regionのもの) aws cli jq Goal Hugoで生成したstatic siteコンテンツをS3でホストしてCloudFrontで独自ドメイン配信 独自ドメイン= blog.foo.com 配信はhttpsへリダイレクト Hugoプロジェクト作成 brew install hugo hugo new site site cd site hugo --minify static site用S3バケット作成 s3bucketpolicy.json { "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::blog.foo.com/*" } ] } aws s3 mb s3://blog.foo.com --region="ap-northeast-1" aws s3api put-bucket-policy --bucket blog.foo.com --policy file://s3bucketpolicy.json aws s3 website s3://blog.
Read more