Goでenumのswitchをcheckできるツールを作りました
Goでenumのswitchをcheckできるツールを作りました
Goでenumのswitchをcheckできるツール switchecker を作りました。
switchecker
switchecker は Goのソースコード内でswitch文で enum
を用いた値のパターンマッチングにおいてenumで宣言されている case
をすべて網羅できているかをチェックしてくれるツールです。と、ここまで言いましたが、Go
で enum
?と思いますよね。厳密にはGo
でenum
は存在しないです。ここで enum
と表現してしまっているものは下記のようなコードになります。連続した定数定義と iota
を使用したりするアレです。 おそらくみなさん一度は Go enum
でググって出てきたコードではないでしょうか。
type language int const ( golang language = iota swift objectivec ruby typescript ) func function(x language) { switch x { case swift: println("swift") case ruby: println("ruby") case golang: println("golang") case typescript: println("typescript") case objectivec: println("objectivec") default: panic("unexpected default") } }
これをこの記事では形式的に enum
と表現しちゃいます。やりたいことに対してわかりやすいのと「連続した定数定義と iota
を使用したりするアレ」って書くのは長いし(このテクニックに名前がついていたら教えて下さい)
用法
例えば先程の例に出したコードを2分割にして enum.go
と use.go
と2つに分かれているとします。
enum.go
package main type language int const ( golang language = iota swift objectivec ruby typescript )
use.go
package main func function(x language) { switch x { case swift: println("swift") case ruby: println("ruby") case golang: println("golang") case typescript: println("typescript") case objectivec: println("objectivec") default: panic("unexpected default") } }
この場合は -source
に enum.go
を渡して、 チェックしたいファイルである use.go
を -target
に渡して使用をします。
$ switchecker -source=enum.go -target=use.go
globも使えます
$ switchecker -source=*.go -target=*.go
デフォルトでは *.go
渡すようになっているのでこの場合は省略も可能です。
$ switchecker
実行して成功するとこんな感じです。
$ ls enum.go main.go use.go $ switchecker Succesfull!! $ switchecker -source=*.go -target=*.go
ファイルが増えてきたら ,
区切りで対象を増加できます。
$ switchecker -source=enum.go -target=use.go,use2.go
そしてhelpです。
$ switchecker --help Usage of switchecker: -source string Source go files are containing enum definition. Multiple specifications can be specified separated by ,. e.g) *.go,pkg/**/*.go (default "*.go") -target string Target go files are containing to use enum. Multiple specifications can be specified separated by ,. e.g) *.go,pkg/**/*.go (default "*.go") -verbose Enabled verbose log
用量
プロダクトで使っているCIに導入してこれにチェックさせるのが一つ。もう一つは単純に手元で走らせるのも一つの使いみちかな。と思います。ただエディタで編集したときにチェック。とかは考えてないです。
モチベーションとか仕様とか
使いたいシチュエーションとしてはツールを少し雑に作っていたときに enum
を用いて関数の引数に渡して switch
で分岐して。みたいな書き方をしていたら、あとから enum
の要素を増やす必要が出てきたので増やした。そしたら実行時にちょっとバグった。コンパイルでは検知できないけど、ここも検知してほしいな。作ってみるか。と思って作ってみました。
まあ実のところ language
はint
に名前がついただけなやつなので case 1000
とか書けてしまいこれは switchecker
には引っかかりません。。現状はこういった case
は無視して、golang,swift...
など、連続で定数定義されたものすべてが一つのswitch
の中に入っているか check
してくれる機能に絞っています。 名前のついていない case
等は実装社の責任としています。
あとはトップレベルで宣言されたものだけを現在対応しています。func
の中で同じ構文で書いていても対応していないです。 func
の中で宣言するケースはきっと多くないだろう。というのと ast
等で底を解析して他の型と区別させるのが大変。という理由です。
どこまでサポートしているか、想定しているか。を厳密に知りたい方はコードを読むのが一番正しいとは思うのでぜひ見てみてください。
ちなみにテストを見るとサポート範囲のイメージがつかめると思います。
- https://github.com/bannzai/switchecker/tree/master/testdata
- https://github.com/bannzai/switchecker/blob/master/check_test.go
- https://github.com/bannzai/switchecker/blob/master/parser_test.go
技術
使ったパッケージや構成をさらっと。
go/ast を -source
の引数の解析部分で golang.org/x/tools/go/packages を -target
で渡したファイルの型情報と -source
から作った構造体の情報とマッピングしてチェックする。そういった流れになっています。
-target
の型情報取得はgo/typesでも行けるのかなあ。と思ったのですがConfig.Checkを使ってみた所、渡した引数のGoファイルの型情報は取得できているが、標準のパッケージではない自作のパッケージ等がimportされている場合はそっちの型情報が取得できずに Config.Error
が返ってきてしまいました。少し試行錯誤しましたがイマイチimport先の型情報の取得を go/types
を使って取得するやりかたがわからなくて、golang.org/x/tools/go/packages
の方ではimport先の型情報まで取得できたのでこちらを使用しています。
まとめ
switchecker のリポジトリはこちらです。
個人的に ast
や 型情報みたいなメタな部分を使って効率化を測るツールを作るのが好きで、 その中でもコード量が少なめかつ割と愛用していきそうななツールができたと思っています。
まだできたばかりなので CI
にまで組み込めてませんが手元で動かす感じでは期待した通りの動きで満足です。近いうちにCIにも組み込んでみてもっと改善してい後と思います。
enum
のチェッカー欲しかったんだよなあ。これいいかも。switchecker
って名前がいいね。と思ったそこのあなた
スターください 🌟
おしまい \(^o^)/
チームのコードレビューをよりスムーズにするツールを作りました
これは DeNA Advent Calendar 2019 12/20 の記事です。
チームのコードレビューをよりスムーズにするライブラリを作りました
チームのコードレビューをよりスムーズにするために notifierというツールを作りました。
notifier って何
notifier は 現在はGitHub→Slackに対応しています。例えばGitHubのPullRequestのReviewers
やAssignees
を追加される事があると思います。このときに自分宛てのメッセージがSlackに通知される。そういった仕組みになっています。他にもGitHubではコメントでメンションを付けるとSlackに通知が行きます。下の画像はGitHubでメンション付きのコメント → Slack に通知が来る流れをスクショして貼りました。SlackにGitHubで実際にコメントされたURLも付いてくるようになってすぐにメンション内容を確認できてGitHub上でのやりとりがSlackの通知をONにしておけばスムーズになります。
モチベーション
別の人が書いた記事にSlackの返信とGitHubのReviewを爆速でやる理由という記事があります。その中に
Slackで質問したり、GitHubでレビュー依頼をしたりすると、その作業は返信が来ない限り先に進めることができなくなります。 その待っている間にできる別の作業があれば良いのですが、基本的に仕事は直列で進めるのが最速なことが多い。 (特にエンジニアだと[要出典])スイッチングコストが掛かりがちなので、複数のブランチで並列作業したりはあんまりやらない(そうだよね?)。 つまり、自分がボールを持っている間は他の人の作業の作業を止めていることになり、時間のロスになる。
という文章があります。この意見には同意でコードレビューやその他の意見を求められているコミュニケーションは反応が早いほうが良いと思っていて、ただGitHub上のやりとりを検知する方法が通常だと(私は)普段開いていないメーラー経由での通知から対象のページ開くことになります。それよりもSlackで単純なリンクだけ送られてきてそこからすぐに開きたいな。というモチベーションで作りました。
ちなみにここまで読んだ方でこういったものを自作している方だったり、pullpandaの存在を思い浮かべる方がいると思いますが、弊社はGitHub Enterpriseの制約がありpullpanda使えなかったり と思ったら使えるみたいですね。執筆後に気づいたので今回はできないと思っていた前提で読んでもらえると。
Enterpriseだとpullpandaを使う上で制約があると思っていた & 今回は個人レベルというよりもチーム全体に一度導入したかったので設定(yaml)ファイルでチームのメンバー全員のGitHubとSlackを結びつけるものを用意したいな。と思い notifier を作ることにしました。(実を言うと作る段階ではpullpanda自体使ったことなかったのですが。だいたい一緒な事できそうだなと思っている)
あとこのタイミングで謝辞を述べたいのですが、実を言うとほとんど同じ機能を持っているツールがすでに存在します。mentions ってツールです。こっちの方が対応サービスも多いです。これは以前の職場でも使っていて大変助けられたツールです。ただ、自分の環境に合わせて、例えば通知先にDiscordを追加してSlackではなくDiscordに通知送るようにしたいな。となったときにシュッと自分で作れそうな Go
で作られた物が欲しい。と思って自作しました。逆にRuby on Railsのほうが慣れている方は mentions から拡張しても良さそうですね。mentions 今までお世話になりました(まだ使うことあるかもしれないけど)。
使い方
簡単に notifier の使い方、設定の仕方を紹介します。しかし、これ案外やることが多い。そのうち設定を簡単にするサポート機能みたいなのもつけたいな。と思っています。
GitHub
GitHubのイベントを notifier に通知する必要があります。というわけで webhooksの追加を行います。Payload URLは実際に notifier をホスティングするサーバーのURLのendpointを入れましょう。
それで送るイベントも設定しましょう。下の項目をクリックすると選択できるようになります。
チェックする項目
- Issue comments
- Issues
- Pull requests
- Pull request reviews
- Pull request review comments
GitHub側の設定は以上です。
Slack
Slackのアプリケーションを作ります。 https://api.slack.com/apps から Create New App
を押しましょう。
次にどの操作をするアプリケーションかの設定を行います。OAuth Scopeを設定しましょう。Web上からだと下の操作で設定できます。
必要なOAuth Scopeは下記のとおりです。設定できる項目があるので入力しましょう。
必要なOAuth Scope
最後に OAuth Access Token
を控えておきましょう。Webの画面からcopyできます。
Slack側の設定は以上です。
IDのマッピング
次にGitHubとSlackのIDをmappingするためのyamlファイルを用意します。
id: owata github: login: owata slack: id: YYYYYYYYY id: bannzai github: login: bannzai slack: id: XXXXXXXXX
- トップレベルの
id
はユニークであれば何でも良いです。 github.login
はGitHubのIDを入れます。slack.id
はSlackのworkspaceごとのユーザーIDを入れます。ユーザーIDはここからテストして取得できます。
環境変数の設定
notifier は YAML_FILE_PATH
と NOTIFIER_SLACK_TOKEN
という2つの環境変数を用意する必要があります。
- YAML_FILE_PATH には用意したyamlファイルのファイルパスを入れましょう。ちなみにリモートのファイルパスも対応しているので、例えばgistにあげて
https://gist.githubusercontent.com/~
とか書いても動きます。 - NOTIFIER_SLACK_TOKEN ではcopyしておいた
xoxp
から始まるOAuth Access Token
をここに入れます。
ホスティング
はい。ここまでやればあとはホスティングだけです。ホスティングは自由にしてください(雑)。
ただ、notifierを開発していときの動作確認のためにherokuを使用して開発していました。
なので、herokuのアカウント持っていて一つホスティングできる場所を用意しておくとすぐに動作まで持っていくことができると思います。
$ make heroku
ってやるとDeployされます。これで良ければ使用してください。詳しくはREADMEもみてください
https://github.com/bannzai/notifier#deploy
実際導入してみて
まだ日が導入して浅かったりするのですが、GitHubを使っている開発者全員に通知を飛ばすようにして運用しています。 個人的にはすぐにReviewerに巻き込まれたりしたことがわかってすぐにレビューできるので作ってよかったな。と思います。 またこれの第一目的が端的に言うと迅速なレビューしようぜ。って感じだったのですが人によっては「すぐにレビューできないことが多いから、まだ見ていないレビューを一覧できるチャンネル代わりになる」みたいな使い方も見出してそうです。あと「もう少しリッチな表示にしてほしい」とか要望もあったりするので、少し手が空いたときにですが継続的に開発はしていこうと思います。 あとは特にチームから不満も聞いていないです。が、未だに急ぎのレビューがSlackのメンション使われているな。と悲しい時があるのでこれからは布教活動にもう少し精を出すか。とも思っています。
終わりに
Discord対応とかPRお待ちしてます。あと個人的に今PullPanda使う機会が無かったりするのでPullPanda(とかその他のサービス)はもっと便利。逆にこれはできないからこういうのあると良いんじゃないの。とか意見ももらえたら嬉しいです。
notifierのリポジトリはこちらです。 良いな。と思ったそこのあなた。
スターください 🌟
おしまい \(^o^)/
treeコマンドをいい感じにしたツールを作ろうと思っていました
treeコマンドをいい感じにしたツールを作ろうと思っていました
違和感のあるタイトルから始めます。どうもbannzaiです。趣味はコーディングでよくGitHubに作ったもの。作りかけた物をあげています。
treeコマンドをいい感じにしたツールを作ろうと思っていました というタイトルなのですが、今回は「なんか一応動くものできたし、作ろうと思っていた機能は実装したけど、たぶんこれ俺使わないな」ってものができてしまいさてどうやって供養しようか。と思っていてこのようなタイトルになっています。
しかしこの事実と作ったソフトウェアである itree をOSSにすることは無関係だったのでOSSにはしてあります。みんなよかったら何かの参考にしてね。ってテンションでこのブログを書いていこうかと思います。
tree
まず tree コマンドの説明ですが、このwikipedia)を読んでください(雑)。*tree コマンドの実行結果を見るとわかりやすいです。下の例では2階層分だけ表示しています。
$ tree -L 1 . ├── LICENSE ├── Makefile ├── README.md ├── go.mod ├── go.sum ├── main.go └── pkg ├── file ├── parser └── ui
tree コマンドは上記のように $ tree [options]
でファイルをツリー構造で標準出力してくれるツールになっています。
itree
さて、次に itree の紹介です。これがこのブログで紹介する私が作ったOSSのツールになります。 $ tree
は $ ls
よりもファイルシステムの階層構造を確認するのに適しているコマンドと言えます。これをターミナル上からインタラクティブに操作できたら便利なのでは。というモチベーションで作りました。
詳しくはREADMEをご覧になってください。 できることを過剰書きで書くと
- ツリー構造を維持し続ける
- ファイル・ディレクトリのパスをコピー
- ファイル・ディレクトリ追加
- ファイル・ディレクトリの名前変更
- ツリー構造を移動する時に文字列検索で対象を絞り込める
- 選択している項目に対して
$ open
の実行 - 選択している項目を
$EDITOR
で開く
何が足りないのか
元も子もない事を言うようですが、このライブラリ作成の発端はtview というGo製の TUI を簡単に組むためのライブラリに興味があって使ってみたかった。からだったのです。
tview→なにか作るか→なんか tree とかいい感じにできるんじゃね。って流れで作りました。
ちなみに個人開発なので手段(技術)が目的になってしまうのは特に悪いことだと思っておらず、むしろチャレンジする物によっては良いことだと思っているで何も後悔も反省もないのですが、いざ作ってある程度形になっていくうちに「tviewも大体分かったしこれもうここで終わりでいいんじゃね」となり、一応は形としてできているしブログ書いて残しておきたいな。って気持ちになっており今に至ります。
あと振り返ると「これが手段(技術)が目的となった物の末路かあ」っていうのを(何もリスクが無い場面でですが)身を持って体験できたのは良かったかなと(ポジティブ)
というわけで 何が足りないのか って問に対しては 私自身が普段ターミナル上で
$ treeを使用する上で何を課題に感じていたのかの把握
がそもそも足りなかった。って感じですね
まとめ
なんか 供養 と聞くと失敗作の成れの果て、みたいなイメージですがこのプロジェクトは成功です。なぜなら使いたかったライブラリの使い方は一通りわかったから。うん。そう思おう。
個人的に TUI はちょっと心くすぶるものがあるので何か便利ツールを量産していけたら良いなと思っています。
みなさんも TUI でツールを作る機会があったら itree のことを思い出してみてください。何か参考になれば嬉しいです。
はい。つまり僕が言いたいのは次の一言です。
スターください 🌟
おしまい\(^o^)/
SwiftUIで開発したアプリをOSSにしました
SwiftUIで開発したアプリをOSSにしました
WWDC 2019 で発表されたSwiftUI でアプリを作り、昨日AppStoreにもリリースされました。
NotificationHub: https://apps.apple.com/jp/app/notificationhub/id1484099869?l=en.
また、この NotificationHubはGitHubでOSSとして公開しています。
こちらも合わせて見てもらえるとわかりやすいと思います。
リポジトリ: NotificationHub
記事を書いている時点のコミット: fd39bd11b5bdb937186b2390f86cdae997b37f36
この記事ではどんなアプリを作ったのか紹介と、このプロダクトで使用している技術的な部分をさらっと紹介してちょっとだけ使ってみた感想とか書いていきます。SwiftUI + αを紹介していきますが、このリポジトリを見たらその技術の使い方や、少なくとも NotificationHub ではどうやっているのかを知ってもらえたら困った時に参考にできる状態であると思います。 躓いた部分もいっぱいあって最終的に今の形に落ち着いたのですが、それらを書いていくとブログがすごい長さになると思うので軽くかいつまんで書いていきます。項目によっては後日詳細な記事も書きたいな。と思っています。
NotificationHub
まずはどんなアプリを作ったのか。から紹介していきます。
NotificationHub では GitHubの未読の通知をアプリ内で一覧して見れる機能を提供しています。
Webからだとここから見れるものですね
これを作るかあ。と思った要因として
がありました。メールで通知を受け取って確認したり、確認したいタイミングでWebの通知を見てもまあ良かったのですが、メールは件数が多くてあまり通知として来てほしくない。Webの通知画面も導線が少し奥で一覧性があまりよくない。
サクッとアクセスして Organization・個人アカウントごとの通知を一覧できる画面も追加できる。確認したい時に開いて見たいのだけ見る。みたいなアプリがほしいな。と思って作りました。
右上の + アイコンみたいなところからどのGitHubアカウントの通知一覧画面を作るか選択することが出来ます。ONになっているやつの通知一覧画面が追加されます。主要画面は下記の2つですね
通知一覧 | + ボタン押した時 |
---|---|
あとはタップするとお知らせに対応したGitHubのPRやIssueのページが開きます。 機能としては以上になりますね。
次にこのリポジトリで採用した技術について書いていきます。
Swift Package Manager
NotificationHub ではサードパーティ製のツール導入のために Swift Package Manager を採用しました。
Swift Package Manager も 今年のWWDCでXcodeからライブラリを導入できてiOSアプリ開発でも使える。と紹介されていました。
NotificationHub では Nuke と OAuthSwift の導入に使いました。
Nuke では画像をリモートサーバーから取得して表示する用途で使っています。
特に躓いた点はありませんでしたが、Swift 5.0 から入った Result
型に Combine
拡張として public var publisher: Result<Success, Failure>.Publisher { get }
が生えていたことに地味に感動しました。Result.Publisherを使っている場所はここ 画像取得してViewに表示するまでの主要なロジックは下のclass達を見れば分かるかと思います。
OAuthSwift ではGitHub API v3を使用するためにOAuth2のフローを踏んでAccessTokenを取得するために使用しています。このライブラリはもともとiOSアプリで使用する場合はUIKitを使用する前提のライブラリです。SwiftUIでUIKitの世界に踏み込む場合は UIViewControllerRepresentable、もしくはUIViewRepresentable を使用します。これらはググれば使い方等が普通に出てくるのであえてここでは解説しません。話を戻してOAuthSwiftでは UIViewController
を使用するのが都合が良かったため UIViewRepresentable
経由で UIViewController
を使用しています。
UIViewController
を使うついでにiOS13からStoryboardでUIが組まれた UIViewController
も、その UiViewController
のクラスのinitializerが使用できるAPIが生えていたので使うことにしました。新しく生えたAPIは instantiateInitialViewControllerとinstantiateViewController ですね。使ってみた感想は素直にええやん。ってなりましたね。Storyboardを使っていてiOS13対応ができるアプリ開発者の皆さん。使っていきましょう。
あと書きながら思い出したのですが、 Swift Package Manager 経由で OAuthSwift
はそのまま使えませんでした(1ヶ月ほど前は)。自分でも原因調査ちゃんとしたかもはや記憶がさだかではないのですが、暫定対応として無理やりビルドを通すようにしたブランチを本家からforkして NotificationHub では使用しています。たぶんPRにメモがきしてあるあたり OTHER_SWIFT_FLAG
に値が設定できなくて積んだからforkしたみたいな感じらしいですが1mmも覚えていません。ここで躓いたかたはよかったら見てみてください。forkしたブランチから上げたPR
どうやらすでに master
では入っているみたいですね。6月時点で入っているのでなぜ自分で別ブランチ用意したのか全然わからなくなってしまいましたが、みなさん安心して OAuthSwift
使ってください
この時点でmasterに入ってるよ https://t.co/Zrxnxqtc5i
— Yutaro Muta (@yutailang0119) October 24, 2019
OAuthSwiftを使っている場所はここを見れば全体的にどう使っているのか分かると思います。
Redux
NotificationHub では最終的にアーキテクチャとしてReduxを採用しました。NotificationHubではEmbededFramework化しており、Redux用のフレームワーク、ディレクトリが用意されています。さて、ここで参考にしたOSSを紹介したいと思います。SwiftUI x Reduxを実現する上で SwiftUIFlux というライブラリを大いに参考にしました。これを Swift Package Manager 経由でインストールすることも考えたのですが、ObservableObjectになっているStore に objectDidChange ほしいなあ。とか、スレッド管理少しロジック足したいなあ。とか思ったりして移植という形で記述してあります。ちなみに objectDidChange
の方は特に下手するとBad Practiceの可能性もあります。誰か objectDidChange
使わなくてもいい感じに NotificationHub の機能を保ってくれるPRを送ってください(他力本願)。
SwiftUIFlux で感心したのが @EnvironmentObject
の使い方です。僕は1ファイルにまとめました。1ファイルにまとまっている方のリンクを貼り SwiftUIFluxを 紹介します。 SwiftUI
では通常 View
プロトコルに準拠して View
の構成を決めていきますが、この RenderableView
を準拠することで Redux
で管理されている全ての情報源である Store
が @EnvironmentObject
経由で DI
されることになります。これは一度DIしてしまえば同じ階層下にいる細かい View
にも @EnvironmentObject
が受け継がれ、@EnvironmentObject
の宣言をすることで変更通知を受け取ることができるようになります。さらにこの Store
は ObservableObject
であり、 @Published public var state: State
が変更されるたびに RenderableView
に準拠している View
に変更通知が行きます。少し話が飛びますが、これにより Single Source of Truth を実現する上での強制力も上がり、アプリ全体での記述方法の統一化、View
の構成のパターン化もできそうだな。と思いました。もちろん上手くコントロールしなければならない場合もあります。そうしたい時に私がとった選択は SwiftUIでよく出てくるような ViewModel
みたいなものに一度Storeのイベントをハンドリングさせて必要なイベントだけ View
に伝えるという方法を選択しています。
Redux
を採用した経緯は @ObservableObject
や @State
, @Binding
を通した変更通知のみだと苦しいな。と感じたからです。 具体的に厳しいな。という場面があって採用したのですが、これも長くなりそうなのでここでは書きません。さらっとだけ書くと NotificationHub では横のページングに応じて NavigationBar
のタイトルをリポジトリの名前にするというロジックを入れているのですが、ページングした時に親までページングのイベントを伝えつつ、親の View
を更新しない。という書き方が @State
や @ObservableObject
を使用した場合に素直に思いつかずに Redux
の採用に踏み切りました。最終的には NavigaitonBar
のタイトルを変更するロジックはこうなり、また自分が実現したい機能も実現できたので採用してよかったです。
というのとあまりこのアーキテクチャ最高!っていうのは思考停止なので言いたくないのですが、 SwiftUI
と Redux
というものはある程度親和性があるのではないのだろうか。と感じていたりします。 Single Source of Truth
の実現しやすさとか。
Bitrise
OSSのアプリケーションで使ううえでは無料ということもありCIサービスはBitrise を採用しました。主にPRごとのテストとApp Store Connectへのアプリのバイナリのアップロードを自動化しています。今回始めてiOSアプリをOSSに試験的にしてみたこともあり他のアカウントが勝手に build とか出来ないかなー。とか思っていたのですが、BitriseのGUI上からはたぶん出来なさそう。ということがわかりました。次もiOSアプリをOSS化する時にも使えそうです。秘匿情報はを隠す必要はもちろんありそうですが...
あとBitriseでArchiveのステップを特に意識せずにArchiveできてしまいました。xcodebuildするだけでSwift Package Managerからライブラリ落として来てくれたりしてくれのかな。と思いました。便利だな。と思って何も検証も確認もしてなかったのですが、Swift Package Manager を使用したプロダクトでもBitriseからアプリのバイナリをアップロードするのは簡単でした。
このアプリにBitriseを導入した時にAppleアカウントの2FAで躓いた知見を記事)にしました。よければこちらもご覧になってください。
まとめ
結局技術的な部分についてダラダラといつまでも書いてしまいそうなので強引ですがここで一旦記事を終わりにします。
お気づきの方もいるでしょうがこの NotificationHub は SwiftUI 使って何かアプリ作れるかなー。っていうのが原点で始まりました。少し言い訳めいてますが、それもあっていわゆる UX
的な設計はだいぶ適当になりがちです。ですが欲しい機能は実装できたので個人的には満足しているというのは本音です。とはいえちょっとずつブラッシュアップはしていきます。もういくつかこうしたほうがいいな。と考えている点はあったりします。
個人的な SwiftUI で小さなアプリケーションを組んでみた感想はまあやっぱりまだまだ足りない機能はあるのはもちろん、ワークアラウンド的な解決策も多く、Xcodeのエラーもよくわからない場所によくわからない感じに出たりして、目玉のPreviewも @Binding
とかが絡むとすぐ動かなくなったりとかして良くない点をあげるといっぱいあります。既存のアプリに導入 すべき かと言われればNOだし、新規アプリに関しても現状の SwiftUI で現実的に実装が出来る範囲で使う分には面白いとは思う。って感じの答えになりそうです。
散々だめな部分を言っておいてなんですが、やっぱり新しいパラダイムとして未来は感じています。宣言的UIや Single Source of Truth という方針も個人的には好きです。単純にUIをコードで書けるのは気持ちいですし、安定はしてませんがPreviewもやはり便利です。あと今は手を付けませんがCatalystも良いですね。Mac App も書いてみたいです。成熟していくにつれ便利にはなっていくはずなので出来ることが広がっていく今後に期待ですね。
最後にこの記事に書いてあることや NotificationHub いいじゃん。OSSも後で見よう。そう思ったそこのあなた。
スターください 🌟
おしまい\(^o^)/
bitriseでAppleIDの2段階認証を突破してiOSアプリを申請
bitriseでAppleIDの2段階認証を突破してiOSアプリを申請
個人開発していたiOSアプリで、bitriseを使ってiOSアプリをApp Store Connectにあげるまで少しややこしいプロセスを踏んだので備忘録がてらブログを書こうかと
AppleIDの2段階認証
主なハードルはこの AppleIDの2段階認証 です。少し前に AppleIDの2段階認証が必須になりました。個人でアプリを開発して申請しようとすると自身のアカウントが Account Holder の役割を担うはずなので、2段階認証の設定が必須です。
Only developers with the Account Holder role (formerly the “Team Agent”) in the Apple Developer Program, Apple > Developer Enterprise Program, or iOS Developer University Program need to enable two-factor authentication. Developers who are registered for a free account or who have other team roles are not required to enable two-factor authentication.
この2段階認証を有効にした状態でCIからApp Store Connectにアプリのバイナリをあげるときに、端末に届いた6桁のチェックディジットをCIで使われているマシンから入力する術がなく、App Store Connectにアプリのバイナリをアップロードできねえ。と言うことで困ってました。
たぶん一番素直で恒久的な対策としてはもう一つAppleIDを2段階認証を無効化した状態で作って、そいつに Account Holder よりも弱い開発者の権限を与えて、CIではその弱い権限のアカウントでログインしてApp Store Connectにアプリをアップロードする。ってやり方もありますが、なんかアカウントもう一つ作るの嫌だな。と思って Account Holder を持つアカウントで App Store Connectにbitrise経由でアプリのバイナリをアップロードする方法を調べました。
解決策
bitriseの公式ブログにそのやり方が乗っていました。これで解決。やったね。って思ったのですが、それでも少しつまづいたので簡単にまとめます。(順不同だったり、逆に順番が関係するものもあるかも。そこまでは未検証)
1 . Provisioning等をbitriseにあげておく。具体的な方法はここでは書きませんが。WorkflowのCode Signing から確認してください。
2 . Appleのアカウントの管理ページに行って App用パスワード と言う項目からパスワードを発行しましょう。後ほど使うので適当な名前をつけたらパスワードを何処かにメモっておきます。
3 . Deploy to iTunesConnectを使います。事前にアプリのArchiveはしてある状態で使うので、Archiveのステップよりも後に追加しましょう。ちなみにArchiveのバイナリのExport形式は app-store である必要があります。
4 . Account Holderの権限を持つ AppleIDとPasswordを Deploy to iTunesConnect に必要な変数として入力します。
5 . 同じくDeploy to iTunesConnect に App Apple ID か App Bundle ID を入力しましょう。どちらかが必須項目です。
6 . 同じくDeploy to iTunesConnect で Application Specific Password を入力しましょう。これは 2
の Appleのアカウントの管理ページ でメモったパスワードを使用します。fastlaneで内部的に使用する みたいですね。fastlaneの環境変数 FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD
に該当するものだと思います。
7 . 画面変わって bitriseのブログにも載っていた方法に移ります。マイページのApple Developer Connectionから Account Holder の権限を持つAppleIDでログインしましょう。
8 . ログインが終わったら同じ画面上にアカウント情報が出ていると思います。右上の ... リーダーをクリックして Re-authentificate (2SA/2FA) を選択します。その後に2段階認証のフローが始まるので画面に沿って入力してください。
9 . 画面が変わってアプリケーションの設定ができる画面に行きます。Teamタブを選択して Connected Apple Developer Portal Account から先ほど接続して Apple アカウントを選択します。これでこのbitriseに登録しているアプリケーションでは、ここで設定した2段階認証済みのSessionを持ったAppleIDのアカウントが使われることになります(という意味になると思っています)
あとは個人的に Teamが複数あったのでTeamIDかTeam Name が入力が必要でしたが、上記の 0~9までのステップを行うことで 2段階認証が有効化されていないAppleIDの開発者アカウント の用意をしなくてもbitriseからiOSアプリをApp Store Connectにアップロードすることに成功しました。やったね
補足
6 . 同じくDeploy to iTunesConnect で Application Specific Password を入力しましょう。これは
2
の Appleのアカウントの管理ページ でメモったパスワードを使用します。fastlaneで内部的に使用する みたいですね。fastlaneの環境変数FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD
に該当するものだと思います。
と書いてありますが、これは直接関係なさそうですが必要なステップになります。
というのもこのステップを実施しなかった場合は、下記のエラーに遭遇してしまいました。エラーメッセージから読み取った内容を解釈して Application Specific Password を用意して、Deploy to iTunesConnect に設定したところ上手くいった。ので、おそらく bitriseで使われているfastlaneが FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD
を使うような処理になっているのだと思います。
[15:29:39]: Making sure the latest version on App Store Connect matches '1.0' from the ipa file... [15:29:40]: '1.0' is the latest version on App Store Connect [15:29:41]: Uploading binary to App Store Connect [15:30:07]: [Transporter Error Output]: Sign in with the app-specific password you generated. If you forgot the app-specific password or need to create a new one, go to appleid.apple.com (-22938) [15:30:07]: Transporter transfer failed. [15:30:07]: [15:30:07]: Sign in with the app-specific password you generated. If you forgot the app-specific password or need to create a new one, go to appleid.apple.com (-22938) [15:30:07]: [15:30:07]: Your account has 2 step verification enabled [15:30:07]: Please go to https://appleid.apple.com/account/manage [15:30:07]: and generate an application specific password for [15:30:07]: the iTunes Transporter, which is used to upload builds [15:30:07]: [15:30:07]: To set the application specific password on a CI machine using [15:30:07]: an environment variable, you can set the [15:30:07]: FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD variable
まとめ
Organizationであれば 2段階認証が有効化されていないAppleIDの開発者アカウント もわりと気軽に用意できそうですが、個人という単位だとなんとなく自分が管理するアカウントを増やす行為自体抵抗がある方もいると思います。
そんな方には上記のプロセスを踏めばアカウントを新しく用意しなくても済むよ。という記事でした。
おそらく上記の方法だと2段階認証のSessionが切れる時があると思うので、その場合は都度 8.
のステップで Re-authentificate
を設定する必要があると思います。私自身まだ問題に直面していないのでこれはあくまで予想になります。
今回は少し興味本位でbitrise上の設定だけでうまくできないかな。と思い、権限の弱いアカウント新しく作るとか。fastlaneの環境変数をローカルでベタに用意したりとか。という方法は選択せずにやりました。思ったよりも踏む手順が多いのとこの方法は特に正攻法というわけではないと思っているので、特にこだわりがなければ個人開発者でも 2段階認証が有効化されていないAppleIDの開発者アカウント を発行してしまうほうが楽だし恒久対応になるな。と思っているところです。fastlaneでも簡単に対応するにはこれ的な書き方として載っているくらいなのでより一般的な方法でもあるのでしょう。AppleIDがCI上等でも扱いやすいようなアカウント管理になるようになってくれると大変助かりますね。今後に期待です。
冒頭でも軽く触れましたがこれは個人的な 備忘録 の用途が一番大きいので少し走り書きで自分が後から見返してもわかる程度の粒度で書いております。間違ったことを書いている、とは思ってはないのですが参考にする際は個人的な備忘録ということも考慮してみてください。細かい説明漏れがある可能性はあります。
おしまい \(^o^)/
UserDefaultsに関するライブラリを作りました
UserDefaultsに関するライブラリを作りました
iOSアプリ開発をしていると誰もが一度はお世話になったことがある UserDefaults
に関するライブラリを作りました。この記事はそのライブラリの紹介記事になります。
公式ドキュメント: https://developer.apple.com/documentation/foundation/userdefaults
UserDefaultsGenerator
UserDefaultsGeneratorというライブラリを作りました。これは UserDefaults
のKeyとValueの組み合わせを便利に管理するための手法を提供しています。
早速ですが使い方を説明してどういうライブラリかを紹介していきます。
使い方
まずは udg.yml
という名前のyamlファイルを用意します。
そして下記の設定項目があるので適切なものを設定していきます。
- name: numberOfIndent type: Int - name: UserSelectedDarkMode type: Bool key: DarkMode - name: XYZ type: Array
ymlが用意できたら $ udg generate
というコマンドを実行します。コマンドを実行したら UserDefaultsGenerator.generated.swift
というSwiftファイルができています。
事前にUserDefaultsGeneratorをインストールしておきましょう。 mintによるインストールが可能です。
$ mint install bannzai/UserDefaultsGenerator # install $ udg generate $ ls UserDefaultsGenerator.generated.swift UserDefaultsGenerator.generated.swift
中身を見るとUserDefaultsに関する関数やenumが生成されています。
public enum UDGArrayKey: String { case XYZ } public enum UDGBoolKey: String { case UserSelectedDarkMode = "DarkMode" } public enum UDGIntKey: String { case numberOfIndent } // MARK: - UserDefaults Array Extension extension UserDefaults { public func array(forKey key: UDGArrayKey) -> [Any]? { return array(forKey: key.rawValue) } public func set(_ value: [Any]?, forKey key: UDGArrayKey) { set(value, forKey: key.rawValue) synchronize() } } // MARK: - Bool Extension extension UserDefaults { public func bool(forKey key: UDGBoolKey) -> Bool { return bool(forKey: key.rawValue) } public func set(_ value: Bool, forKey key: UDGBoolKey) { set(value, forKey: key.rawValue) synchronize() } } // MARK: - Int Extension extension UserDefaults { public func integer(forKey key: UDGIntKey) -> Int { return integer(forKey: key.rawValue) } public func set(_ value: Int, forKey key: UDGIntKey) { set(value, forKey: key.rawValue) synchronize() } }
各yamlの設定については下記の用途になっています。
yamlの設定
Key | Description | Required/Optional |
---|---|---|
name | 生成されるEnumのcaseの名前。case xxxx の xxxx の部分 | Required |
type | どの型で保存取得をするためのものか | Required |
key | name とは違うkeyで保存したい場合に設定することでEnumのrawValueをここで設定した値にできる | Optional |
あとは生成されたSwiftファイルをiOSのプロジェクトに含めましょう。それで使用することが出来ます。
メリット/デメリット
このライブラリを使用するメリットは以下が考えられます。
- 単純なyamlの構造でUserDefaultsに関するメソッドが生成できる
- TypeSafeなUserDefaultsの仕組みが簡単に用意できる
- 用意されるメソッド自体も単純
逆にデメリットは以下が考えられます
といったところですかね。 UserDefaultsの数によって辛いか辛くないか。というのは大きく変わってきそうですね。
作ったきっかけ
iOSDC Reject Conference Day2というイベントで UserDefaultsに関するライブラリをライブコーディングします
という発表をしてこのライブラリができました。
こちらのイベントやライブコーディング等に興味がある方は別のブログがあるのでそちらをご覧になってください。
もともと上記のイベントに発表するか。なんのライブラリを作って発表しよう。と30分でだいたいできるところまで持っていけそうなお題を探していたのがきっかけと言えます。 その中で UserDefaultsGenerator のようなアプローチを取っているライブラリは無いのでは。ということに気づき作るに至りました。
しかし、実際に自他共需要があまりないものを作ったり、技術的なチャレンジができないものを作るほどのモチベーションは持っていませんでした。今回は前者の「需要」というものが知りたいな。と感じて事前にTwitterでアンケートを取ったりした結果が結構面白く、かつ需要ももしかしたら生まれるかもな。と思い作成を決めました。
みなさんが開発に携わっているアプリにはおおよそいくつのUserDefaultsのKeyがありますか
— \(^o^)/ (@_bannzai_) September 16, 2019
これがTwitterで集計したアンケート結果です。アンケートの取り始めた当初はもちろんアプリによりますが、冷静に考えたら1~10個とかそんなアプリがほとんどではないだろうか。と思っていました。実際はそのとおりではあったんですけど、2番目に多い結果として 30 よりも多い
だったのが意外でした。
また、申告しもらった中では100個を超える人も2人いました。100以上のプロダクトもまああるだろう。とは思っていたのですが、周囲だけでも2人いるのか。と思うと自分の想像力の貧弱さを感じましたね。 発表で使ったissueにも書いてあるので気になる方はそちらをご覧になってください。
技術的なチャレンジ
前述で「需要」があるから作ろう。がきっかけだったのですが、技術的に試したこともあるのでよければソースコードも呼んでみてください。個人的に初めて使ったな。っていうものは下記の2つでした。また違う機会にブログに個別にまとめようかと思います。
- ライブラリの内部実装でStencilを使ってコード生成しました。
- Stencilの中でも SwiftGen で使われている StencilSwiftKit を使用しました。
- 使っている場所はここ
- Swift Package Manager プロジェクトでGitHub ActionsとCodecovを使用してのCIの構築
まとめ
- スターください
UserDefaults
のKeyとValueの管理に便利なライブラリUserDefaultsGeneratorを作りました- スターください
- 世の中には多大なUserDefaultsを使用するアプリもあることがわかりました
- スターください
- ライブコーディングでコア機能を開発した動画もあります
- スターください
- 技術的に初めてのことにも取り組めて満足
- スターください
- スターください
- スターください
この UserDefaultsGenerator
最高だぜ。ソースコードも興味ある。俺もこんなライブラリ作りたい。とにかくスターをあげたい。そう思ったそこのあなた。
スターください 🌟
おしまい\(^o^)/
楽しかったiOSDC
iOSDC JAPAN 2019 参加してきました
iOSDC JAPAN 2019に参加してきました。とても楽しかった。
iOSDCではSpeakerとして参加してきました。そしてiOSDCのReject Conferenceでも2日間発表をさせてもらいました。(リジェクトとは) このブログではiOSDCやiOSDC Reject Conferenceでお話した内容やiOSDCの感想を綴っていきます。
iOSDC / LT
本編のiOSDCの方では5分のLT枠としてCfPが採択されました。そこでは先生のことをお母さんって呼んでも大丈夫。そうObjecive-Cならねというタイトルで発表をしました。
平成時代にもあった「先生のことを【先生】と認識しているのに【お母さん】と呼んじゃう問題」をObjective-CのRuntimeの仕組みを駆使して「先生をお母さんにする」ことで解決することに成功しました。完全にネタ枠の内容です。ダダ滑りだったらどうしよう。と思っていたのですが、発表が終わったあとに「面白かった」「頭がおかしい」「変なタイトル賞があればお前がNo.1だ」と言ってもらえてホッとしました。
味をしめたので来年もネタをひねり出して応募していきたいです。あとこれタイトルTypoしているんですけど去年のトークもタイトルがTypoしているんですよね。次からは気をつけて行きたいですね。
今年の発表の動画(たぶんyoutube?)はまだ上がっていないと思うのですが、興味がある方は公開されたら是非ご覧になってください。
そして、bannzaiくんがトイレ行けたかどうかに思いを馳せてください。
ちなみに3年連続でObjective-Cの発表をしています。そろそろObjective-C大好きっ子のキャラが定着してきそうですね。Objective-Cはいいぞ。
speakerdeck
https://speakerdeck.com/bannzai/iosdc-2019
iOSDC Reject Conference Day 1 / LT
iOSDC Japan 2019 Reject Conference days1でもLTをしました。枠が空いていたので結構直前くらいにLTを申し込んだ記憶が。 実は登壇みたいな場面で話すの好きな性格みたいなので話せる機会があると話たくなってしまいます。今回は通常トーク枠があってiOSDCで応募した内容に限らなくて良くて気軽に応募できました。
こちらでもObjective-Cが関係した内容をお話しました。そしてタイトルもTypoしました。このトークでは「子は親を選べない」そんな常識をObjective-CのRuntime APIを使って覆しました。
bannzaiの親である Typoman
から NotTypoman
にすることでbannzaiのTypoを修正する。そんな内容になっています。
この発表はyoutubeに上がっています。気になる方は見てみてください。
https://youtu.be/zpKZwo-bcuU?t=3311
iOSDC Reject Conference Day 2 / LT
iOSDC Japan 2019 Reject Conference days2でも発表をしました。前日のDay1の方で枠が空いているから話したい人は話して。という感じだったので軽率に申し込みました。
この発表では事前の資料準備は ~面倒くさかった~ 前日に30分の発表を決めたこともあり、時間の都合上 ライブコーディングをすることにしました。タイトルは UserDefaultsに関するライブラリをライブコーディングします
です。前日にNotTypomanクラスを継承したおかげでTypoしていないタイトルで望みました。iOSDC系のイベントは3年間参加してますが初めてiOSエンジニアに最も愛されている言語であるSwiftを使っての発表になります。僕も大好きです。Objective-C大好きっ子?知らん。
この時に作成したUserDefaultsGeneratorではこの発表あとも開発を続けてversion 1.0.0 をリリースすることが出来たのでまた違うブログにでもライブラリの詳細を書こうと思います。 ライブコーディングで目標にしていた機能については発表時間内に完成することができました。初めて望む発表形式でしたが上手く行ったみたいでよかったです。
発表資料みたいなのはほとんどありませんがGitHubの方でこういう風に作るよ。という構想リリースした時点のREADMEを紹介し、ざっくりissueにしたものを用意して発表に挑みました。時間内で書いたコードのPRはこれです。 giginet
という方からたくさんのレビューコメントをもらっていますが、ライブコーディングした内容をそのまま残したかったので一旦そのままデマージして後のPRで修正している。という形になっています。
こちらの発表もyoutubeに上がっています。気になる方は見てみてください。 https://youtu.be/K6tkZjxdji0?t=1741
そしてこのライブラリ・UserDefaultsGeneratorがいいね!と思った方は
スターください 🌟
iOSDCどうだったか
タイトルにもあるし冒頭にもあるし楽しかった。
自分が登壇するのが好きで発表を3回もした。という事実に加えて技術者としてのインプットもたくさん出来てよかったです。またiOS系のイベントで出会った人や、業務を通じて知り合った人と遭遇する機会も多く「久しぶり」の方との交流が出来ることもとても良いことですね。
来年もまた参加したいです。
おしまい\(^o^)/