個人開発のお話

個人開発のお話

こんにちは bannzai です。普段はちょっとしたツールやライブラリを作って営業力でスターをもらうことを生業としていますが今回はプライベートの時間で作っているアプリのお話と宣伝をしていきたいです

作っているアプリ

もともとohanamizukiさんが開発していたPilllというアプリを開発の方を引き継いで一緒に作っています。 Pilllについては下記のnoteを見てもらえたら概要や生まれた経緯が分かります。

note.com

簡単に言うとピルという薬の飲み忘れを防止するためにサポートするアプリになります。

引き継いで開発までの時系列をまとめると

  • ohanamizuki < Pilllに1万人以上ユーザーが付いてもっと良くしたいから開発の部分を引き継いで一緒にやってかない?
  • bannzai < よっしゃ
  • ohanamizuki < スケールさせたいしリプレイスしても良いくらいの気持ち
  • bannzai < よっしゃ
  • ohanamizuki < Androidもあったら良いよね
  • bannzai < よっしゃ

から始まりました。話自体はたしか2020年の6月くらいにもらいました。結果的にがっつりリプレイスもして2021/01/24にiOSアプリを、に少し時間を置いてAndroidのアプリもリリースすることができました 🎉 iOSについてはリプレイス後に一度計測もしたいし評価をリセットして出したのですが、執筆時の2021/5/01では1000件を超えるレビューと平均評価4.6が付けられていてとても嬉しいです。ユーザー数もちょうど2万人くらい(おそらくリプレイス後に+7kくらい)いて需要があることを体感しています

f:id:bannzai:20210429095542j:plain

以下に各プラットフォームのストアのリンクを該当ユーザーで使いたいという方はぜひ使用してみてください。あとスターください。Androidの方は最近出したばかりでもあるのでレビューもまだついていないのですがこちらの方も後述しますが現在はiOSと同じソースコードで動いているのでほぼiOSと体験は変わらないものとなっています。なのでアプリの内容としてはほぼ同一なものになっています。興味がある方は一度落として試してもらえたら嬉しいです。こちらもスターをもらえたら喜びます

AppStore Google Play Store

選定技術の変更

もともと純粋にSwiftのみで書かれていたiOSアプリだったのでそんなに癖はなかったのでそのまま使用しても良かったのですが、クライアントはFlutter、バックエンドはFirebase(系のサービスを中心に)を採用して開発をすることにしました。以下が決めるまでのその時の気持ちです

  • どうせならなにか馴染みのない技術を試したい
  • Androidも出したい気持ちがあった
  • DBがアプリ内のローカルのみだったので変更する必要があった
  • 通知もローカルからの発信/受信のみだったがバックエンド用意してリモートからも送りたいよね
  • 開発者は一人だからFirestore(スキーマレス)でも全く困らなさそう
  • アプリとしての複雑さやスケール具合はある程度天井が見えているからFrameworkやBaaSの普通にできる範囲で必要要件はカバーできそう
  • 例えば「Flutterでxは難しいけどyならすぐできる」と代案の提案等をしても通じやすい相手だし冒険しても良さそう

と、こんな具合でした。実質リプレイス前後含めて開発に半年程の期間で仕事の合間の時間を使って開発している程度ですが、上記の判断基準で選出した結果におそらく後悔は一度もして無く過ごせていると思います。同じように悩む方がいたら参考にしてください。とはいえ、やっぱり躓くところはあるので作りたいものに対して軽く検証はしておくと安心ですね。

その他細かいところでいうとPilllのFlutterのコードはGitHubに公開されています。利点としてはみなさんに スターがもらえる 事とGitHub ActionsやBitriseといったCIが広い範囲の無料枠の中で使えるのがとても嬉しいですね。ちなみにデメリットはbannzaiの綺麗じゃないコードが世の中の誰でも見ることができるところですね。謙遜とかじゃなくて「動いているからヨシ」だったり「あー、ここはもう過度に共通化しちゃえば良いや。オレ一人だし」みたいなコードが散見できる場所になっているので私の恥部を見たい人は覗いてください。

github.com

今使っているもの

使っている技術やツールについて改めてまとめます。bannzaiのことを知っている人たちは話のネタにでもしてください。

Flutter

pubspec を見れば分かるのですが主要なライブラリをざっくりと書きます。

freezed x riverpod x flutter_hooks は組み合わせで State Management で使用しています。楽にかけてとても良きですね。mockitoはUnitTestingの際によく使用します。Flutterはmockitoを使ったmockの準備やWidgetTestingが結構強力で「もうここ動作確認したくないや」ってときにテストを書くハードルが下がってとても良いです。

Firebase(and GCP)

使っているサービスをずらりと。これ以外にもあるかも

  • Analytics
  • Crashlytics
  • Cloud Firestore
  • Firebase Cloud Functions
  • Cloud Messaging
  • Firebase Auth
  • BigQuery

守備範囲が広く取れてFirebaseってやつは良いですね

CI

PRごとのテストはGitHub Actions, Deploy系はBitriseって分け方にしています。BitriseはCodemagicでも良いなと思ったのですが、OSSによるPricingの工面がサポートが連絡しないといけなかったのと、証明書等を預けておけてスクリプトが動けばどこでも良いな。って気持ちだったので慣れているBitriseにしました

Web Pages

FAQやリリースノートはWebで管理したほうが便利かつPilllではドキュメントはNotionを最近使用していました。なのでこういった静的に置くWebページもNotionで置いておけたら良い。かつNotionでもデザインをPilllらしくしていけたら良いよね。などの願望を叶えてくれるツールであるAnotionを使用しています。最近公開したばかりなのでコンテンツ等もまだ少ないのですがデザインがカスタマイズできてNotionで書いておけば気軽にホスティングできてとても良きです

Anotionで作ったPilllのページです pilll.anotion.so

楽しい?

ここまで読んで「と言いつつbannzaiは性別♂だから開発しててどうなの」って思う方がいるかもしれません(というか僕もどうなんだろうと思っていた)。最初はとりあえずohanamizukiさんと久しぶりに一緒に開発するのも楽しいし、やりたい技術を試したりできるのと理性で考えたときに市場規模が良い感じにニッチだけどそこそこ大きく、競合も少ないからそういう意味で可能性を感じて身を乗り出しました。それで実際やってみると機能開発等は二人で開発のコスパ含めて議論するのですが、課題自体は割と明確で(おそらくは)何の障害もなく使用者の気持ちになって良い提案ができていたり、その結果AppStoreでの評価や使用者数の数字が右肩上がりに出ているようにポジティブな評価が目に見えたり、意外だったのがアプリ内で用意している問い合わせ用のフォームから礼を言われることもしばしばあります。また、開発者が募るSlackで「最近こういうの作っているんですよ」っていうと意外とユーザーが身近に何人もいたりしてそういうこともありモチベーションは結構高いです。というわけで今楽しんで開発をしています。

まとめ

以上、最近こういう事もやっているよ活動報告でした。ユーザーの人は今後どんな機能追加するの。とか気になる人もいると思いますがそれは今後のお楽しみということで楽しみにしていてください。また機会があったら開発者向けのブログやらサービスを運用していての知見も記事にしていけたら良いなと思っています。

最後に一言だけ、GitHub,AppStore,Google Play Storeのアカウント持っている方へ。ぜひPilllのページまで行って

スターください 🌟

おしまい\(^o^)/

2020年の振り返り

振り返り

振り返り記事ってやつを書いてみたくなったので書いていく。

前置き

コロナの影響で例年の10分の1くらいしか外出してないようなそんな特殊な年でしたね。旅行とかほとんどできなかったので私の生活に起きて主にTwitterに書いてあることを雑多にまとめる記事です

ライフログ

まずはある程度の時系列順で起きたこと書きます

フリーランスになりました

前職に退職の意思を伝えて初めてのTwitter転職ってやつをしました

1ヶ月くらいで決まりました

退職時の同僚からのプレゼントのセンスには震えた

今はクックパッドマートのサービスを開発をしています。フリーランスとして活動しています

スプラトゥーンにめっちゃハマった

外出する時間が減った代わりに家にいる時間が爆増しました。ゲームに手を出し始めました。たぶん4末か5月くらいからやりはじめたのが妹との会話ログから追えます

f:id:bannzai:20201229084757p:plain
妹との会話

1日5~8時間くらいやってました

ここまでは順調。後述する家が無くなったことにより安定してゲームできる余裕がなくなってしまった

家がなくなりました

東京から出て福岡に引っ越す予定でした

福岡の絵審査会社の審査も通り順調に引っ越しが進んでそうな雰囲気を途中まで出してました

しかし、契約書について説明したら仲介業者から手を切られました。契約書届いたし家を解約しちゃった後です。家を無くしました

家が見つかりました

4ヶ月くらいかけて家を見つけることができました。やったね。このブログを書く1週間前の話ですね。

ハッカソンで最優秀賞取りました

いえーい

戦利品です

その時書いたブログです

bannzai.hatenadiary.jp

人生が漫画になりました

よかったら読んでください

モーメントにまとめてあるのでここから一気に読めます

1800 スターもらいました

経緯。1800スターついているライブラリを譲り受けることになりました bannzai.hatenadiary.jp

3200スターもらいました

1800スターもらった話をSwift愛好会というイベントで話したら、同じ回で登壇していた @k_katsumi さんから 3200スター付いているライブラリを譲り受ける事になりました

love-swift.connpass.com

作ったやつ

作っているやつも含めるとまだあるのですがとりあえず完成したOSSをおいておくのでスターください

switchecker

Gedatsu

XChanger

まとめに入る前にまとめてく

毎年何かしらあるのですが家を無くしたり、漫画にされたりといった体験は初めてでした。家を無くした後しばらく「最近何してる?」と聞かれた時に目を血走らせて「屋根付きの家を探してる!」って言えば微笑くらいは取れたので便利で良かったです。だんだんみんな家が無いキャラに飽きてきたので家を見つけました

とはいえ住環境が安定してないと生活にも悪影響が出て全くやりたいことがはかどらなかったので住環境って大事だなあ。って思いました。OSSだけみても家があるときにしか出してない。自分の好き勝手しほうだいなスペースがある幸せを噛みしめてます。あと住環境整ったしゲーム環境も整備して遊んでこう。OSSも書いてスター乞食していきたい。スターください

過去が描いてある漫画については個人的に「言っても面白くないな」って感じだったのですが、こうやってネガティブなことでもポップにテンポよく話が伝えることができて作品を通じての表現ってすごい。そういった点に感動しましたね。こういう表現ができるの良い

あとフリーランスになりました。これも初めて。職場によるけど正社員時代と何が変わったかと言われるとそこまで変化は無いです。最近思ったことは評価制度がある会社だと評価の対象から外れやすい立場だな。と思いました。個人的には嬉しい点

ハッカソンちょいちょい参加してますが予選・本選を通しての1位というのは初めてでした。ハッカソン自体久しぶりに参加しましたがやっぱり楽しいのでもっと参加していきたいですね

リポジトリごとスターをもらえる体験も初めてです。お二方ありがとうございました。みなさんからのリポジトリごとスターもお待ちしております

振り返り記事も書いたのは初めてです。というわけで初めてがいっぱいの年でした。まだ僕に初スター上げたことない人は今がチャンスです。こちらのアカウントのリポジトリすべてにスターを付けてください

github.com

来年の抱負

スター欲しい

まとめ

いっぱい書いちゃった。最後にひとことだけ

スターください 🌟

おしまい\(^o^)/

引っ越しに失敗して家を失ったけどSPAJAM2020ハッカソンで最優秀賞を獲った話

引っ越しに失敗して家を失ったけどSPAJAM2020ハッカソンで最優秀賞を獲った話

SPAJAM2020というハッカソンに出場し最優秀賞を獲得しました。この記事では作った作品の紹介や今の気持ち、チームへの感謝。それとは別に僕が家を失った話を時系列をあわせて書いていきます。

引っ越しに失敗した話

突然ですが引っ越しに失敗しました(2020/08/17)

ちなみに引っ越しが失敗するとも露知らなかった時の様子です

「おひっこし」

SPAJAM2020ハッカソンに出場したいと思い7月にチームビルディングして、その当初引っ越しをするメンバーが全体の過半数(僕も引っ越す予定だった)を超えていたので「おひっこし」というチーム名にして応募し第二回予選を応募しました。ちなみにこの頃は家がありました。下の画像はDMでチーム名の決定、および予選の申込み直前のDMの発言です。日付は 2020/08/13 です。4日後家がなくなることが決定します

f:id:bannzai:20201109230848p:plain

予選

「おひっこし」の第二回予選の結果は 優秀賞最優秀賞 を獲得できず惜しくも1位にはなれませんでした。

SPAJAMでは出されたお題に沿ったアプリを作り、それをもとに審査基準にそって評価を競う場となっています。予選のときのお題は交流で、「おひっこし」でパシャっと交流できる イマージ というアプリを作成しました。作っている本人たちがわいわい楽しく開発できて本当に楽しい予選でした。

www.youtube.com

ちなみにこのときにはもともと住んでいた家を出ており、私が住む権利を主張できる住居は無くなっていました。一言でいうと言えを失いました。家を失ってから1週間後くらいにハッカソンだったかな

本選出場チームに選出

SPAJAM2020の本選出場への権利の獲得は2つの道がありました。

  • 計5回の予選で選出される最優秀賞を獲得したチーム
  • 1度の予選で最大3チーム選出される優秀賞なのですが、その予選で優秀賞を取った最大15チームからさらに篩をかけられた3チーム

以上の2つの方法から本選出場の権利が付与されるシステムになっています。第二回予選で「おひっこし」は優秀賞だったのですが、この本選出場できる3チームの中の一つに選んでもらい本選出場が決定しました。

この時のステータスは「いろいろなバタバタが落ち着き始めて回復してきたからまた家を探そう」と前向きに思っていた時期でした。そうです。まだ家がないです。

本選

本選出場してまず受付とチーム紹介から始まりました。予選のときにもあり、ほとんど内容が一緒なのですがせっかくなので紹介したいと思います。

チーム「おひっこし」です。僕たちのチーム名の由来はチーム結成時にメンバーの過半数が「おひっこし」をする予定だったので「おひっこし」というチームになりました。しかし、代表者である僕は「おひっこし」を失敗しました。結果家を失いました。他のメンバーは「おひっこし」を成功させています。ここまでが予選でも紹介した時のステータスです。さて、本選当日である今日なのですが... まだ家が見つかってません! よろしくおねがいします!

こんな感じだったと思います。ちなみに審査員の方々の反応です。

本選の最優秀賞の賞品の一つが温泉券10万円(1人に付き)と知り燃えました。1ヶ月分くらいの住まいが確保できると思ったからです。

f:id:bannzai:20201109235639p:plain

肝心の本選でのお題は観光。そして、「おひっこし」では 地元を観光地化してポスター化して共有するアプリ ぽすすめ を作りました。

www.youtube.com

アプリのアイディアを出して、デザインをお越し、アプリを作り、プレゼンの資料づくり、紹介動画の撮影等とにかく24時間以内にメンバー全員がやることが多くてとにかく大変でしたがちゃんと動くアプリ、魅力を目一杯凝縮したプレゼン資料が完成して やりきった 感が強かったのがとても記憶に残っています。

そんな感想を胸のうちに抱えている(普通に口にも出してたけど)アプリだったので結果が出るまで予選以上の緊張がありましたが、審査の結果発表時に 最優秀賞 として選出してもらえたときには久しぶりに嬉しくて「うっしゃ!」って声を出すほど嬉しかったです。言葉通り結果オーライで、予選のときに最優秀賞を逃したこと、本選の前日に乗り物酔いがひどいのでチームメンバーが集合する場所に前泊していたこと、 徹夜で頭を働かせながら作りきったこと、持ち家が無いこと。すべてのここまでに至るネガティブだったことが報われました。最後のだけ嘘です。家は欲しいです。

でもとにかく嬉しかったですね。嬉しすぎて本選の最初の方で「最優秀賞獲ってなにか一言求められたら『賞品の温泉券で1ヶ月分の屋根付きの宿に宿泊する権利がもらえて助かりました!』とボケを入れよう」と思っていましたが、本当に頭の中が喜びに支配されてボケを言う余裕がなくて、実際に 最優秀賞を獲得した気持ちどうぞと審査員に振られたときは普通に喜びを伝えるだけの返答になってしまいました。最後の最後でキャラブレを発生させてしまいました。後悔はしてないですが反省はしています。

反省です

審査員の方からの最優秀賞を獲得の紹介ツイートです。

すぐ下のツイートで芸人枠として捉えられていることを実感しました。

SPAJAM2020のHPにも最優秀賞チームとしても掲載してもらいました

まとめ

予選・本選で個人的にチャレンジしたことや良かったこと・次に生かせること等も色々あるのですが、長くなりそうなので割愛します。また直接会う人もいると思うのでそういう機会があれば話題のネタにでもしてください。

最優秀賞が獲得できて本当に良かった。チームメンバーの gaopin1534 koooootake noa_design51 yutailang0119 を誘って良かった。最高の結果になったし本当に感謝の気持ちがいっぱい。

そして、次は「家が決まった!」のブログが出したい...
お家は見つかっていませんが、 https://github.com/bannzai/ ではいつでもスターを受け付けています。

bannzaiハッカソン優勝おめでとう!家なくなったのどんまい!おまwキャラブレwクソワロタw と思っているそこのあなた!

スターください 🌟

おしまい\(^o^)/

1800スターもらった話

こんにちは。bannzaiです。このブログを読んでいる人の中には私が スター乞食 と呼ばれていることを知る人も少なくないでしょう。
今日は一瞬にして1800スターを獲得できたすごそうなお話をします。

経緯

この1.8kのスターが増えた経緯は実はTwitter上で確認できるので見ていきましょう。

すごそうなツイート

すかさず反応。すごそう

1.8kのリポジトリを持っていた人からのリポジトリ移譲提案。すごそう

移譲

そもそもリポジトリ移譲ってスターも引き継がれるのか。当然の疑問を持ちました。スターが引き継がれなかったら引き継ぐ意味がない(言い過ぎ)なのですが、ちゃんとGitHubのドキュメントにもスターも引き継がれると書いてあり安心して スターをもらう Geccoのメンテを引き継ぐことを承諾しました。

https://docs.github.com/en/github/administering-a-repository/transferring-a-repository

What's transferred with a repository?

When you transfer a repository, its issues, pull requests, wiki, stars, and watchers are also transferred. If the transferred repository contains webhooks, services, secrets, or deploy keys, they will remain associated after the transfer is complete. Git information about commits, including contributions, is preserved. In addition:

f:id:bannzai:20200916202548p:plain

ついに

とはいえさすがに無条件ではなく、メンテとその時に存在していたissueとPRの対応とCloseをお願いされたので え、そんなことで1800スターもらえるんですか!?馬車馬のように対応します! やっていくぞ。って気持ちで対応しました。 そして、移譲の条件を無事クリアして届いたメールがこちらです

f:id:bannzai:20200916203309p:plain

うひょーい f:id:bannzai:20200916203800p:plain

Gecco

詳しくはリポジトリのREADMEに書いてあるので見てください https://github.com/bannzai/Gecco

UIのガイドとして使えます

https://cloud.githubusercontent.com/assets/6880730/12470510/2d1cb602-c038-11e5-8095-a2a0d77f99db.gif

まとめ

スター乞食のロールモデルとして新境地を開けました。
みなさんからの大量にスターがついているリポジトリを移譲するムーブもお待ちしております。

1800スターは大きな成果ですがみなさんからのスターはいつでも受付中なので、Geccoに

スターください 🌟

おしまい\(^o^)/

解脱してAutoLayoutのエラーログをスラスラ読もう

解脱してAutoLayoutのエラーログをスラスラ読もう

解脱(Gedatsu)をすればAutoLayoutのエラーログもスラスラ読めるようになります

解脱前 解脱後

解脱を入れるには

CocoapodsでもSwiftPackageManagerでもCarthageでもマニュアルインストールでもいいですが、まずはGedatsuのInstallセクションを見て、Gedatsuをプロジェクトにインストールしていきましょう

解脱するには

解脱するのは簡単です。AppDelegate.application:didFinishLaunchingWithOptions:Gedatsu.open をして悟りを開くだけです。 DEBUG フラグで囲むのを忘れないように

#if DEBUG
import Gedatsu
#endif

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    #if DEBUG
    Gedatsu.open()
    #endif
    return true
}

解脱の仕組み

どうやって私が解脱まで到達できたのか少し紹介します。まず Gedatsu のエントリポイントとなる関数を見てみましょう。ここでは標準エラー出力までに途中に処理を挟んでいます。次にUIKitのPrivate APIをフックしている関数を見ます。

Gedatsu.open

コードはここです

internal func open() {
    _ = dup2(STDERR_FILENO, writer.writingFileDescriptor)
    _ = dup2(reader.writingFileDescriptor, STDERR_FILENO)
    source = DispatchSource.makeReadSource(fileDescriptor: reader.readingFileDescriptor, queue: .init(label: "com.bannzai.gedatsu"))
    source.setEventHandler { ... }
    source.activate()

普段iOS開発たちでは使わない関数たちばかりですね。僕も初めて使いました。ここらへんの関数たち説明を書こうと思ったのですが思ったよりボリューミーだったので途中から挫折しちゃいました。(てへ

なので簡易的な説明だけ書いておきます。参考のリンクも貼っておくので興味があったら調べてみてください。簡易的な説明すると 1つめの dup2writer.writingFileDescriptor を通して標準エラー出力に書き込めるように。2つ目の dup2標準エラー出力の書き込み先を reader.writingFileDescriptor にしています。DispatchSource.makeReadSource では監視したいファイルディスクリプターを渡して、setEventHandler で監視対象のイベントのハンドリング、activate で監視の開始をしています。

あと途中まで詳しく書こうとして断念した文章をおいておきます。情報量はあまり変わりませんがもう少し丁寧な言葉づかいになっています。

途中まで詳しく書こうとして断念した文章

dup2はファイルディスクリプタを複製するシステムコールの関数です。duplicateの略ですね。2はきっと引数の数です。ファイルディスクリプタはファイルに対して割り当てられている識別子です。参照先のファイルを表すポインタみたいなやつです。少し説明を省略しますが、1つめのdup2writer.writingFileDescriptorを通して標準エラー出力に書き込めるように。2つ目のdup2で 標準エラー出力の書き込み先をreader.writingFileDescriptor` にしています。

readerやらwriterといったものは Pipeのラッパーです。例えばReaderの中身だとこんな具合です。Pipeで用意されたファイルに書き込まれると(2つ目のdup2を思い出してください)ファイルが読み込めるようになります。

internal class ReaderImpl: Reader {
    let pipe: Pipe = Pipe()
    var writingFileDescriptor: Int32 { pipe.fileHandleForWriting.fileDescriptor }
    var readingFileDescriptor: Int32 { pipe.fileHandleForReading.fileDescriptor }

    func read() -> Data {
        pipe.fileHandleForReading.availableData
    }
}

次に DispatchSource.makeReadSource で行っていることは reader.readingFileDescriptor を監視するために渡しています。きっと指定したファイルディスクリプタに向いているファイルに変更がある場合にデータを読み込むように割当られる仕組みなんでしょう(雑な理解。source.setEventHandler で変更を検知したときに実行したい処理内容を書きます。この場合はAutoLayoutでエラーが起きた時点のデータからログを整形してコンソールに流す。それ以外は整形せずにコンソールに流す。といったことをしています。最後の source.activate() で監視を始める流れになっています。

通常何かしらのエラーのログや出力は標準エラー出力書かれてコンソール上に出てきます。もっと噛み砕くと 1. Process X < 標準エラーにログ書くぞ 1. 標準エラー < Process X から書かれるぞ!書かれてるぞ! 1. 標準エラー < 書かれたからコンソール上に出してくれ みたいな流れです。

UIView.engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:

2つ目のUIKitのPrivate APIのフックです。コードはここです こちらはシンプルにObjective-CのRuntime APIを Swiftから呼び出してメソッドを入れ替えることで途中の処理を挟んでいます。

internal static func swizzle() {
    guard let from = class_getInstanceMethod(UIView.classForCoder(), NSSelectorFromString("engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:")) else {
        fatalError("Could not get instance method for UIView.engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:")
    }
    guard let to = class_getInstanceMethod(UIView.classForCoder(), #selector(UIView._engine(engine:constraint:exclusiveConstraints:))) else {
        fatalError("Could not get instance method for UIView.\(#selector(UIView._engine(engine:constraint:exclusiveConstraints:)))")
    }
    method_exchangeImplementations(from, to)
}

このPrivate API UIKitCoreの中のシンボルに含まれていました。 下記のコマンドで確認ができます。

$ nm /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore | grep engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:

上記の2つを組み合わせて AutoLeyoutの制約のエラーが発生したときにコンソールに任意のエラーメッセージを出力する仕組みを実現しています。

解脱した人の声

まとめ

Gedatsuを望む方リポジトリはこちらです。
https://github.com/bannzai/Gedatsu/

そして解脱をした人もこれからする人も

スターください 🌟

おしまい\(^o^)/

Goでenumのswitchをcheckできるツールを作りました

Goでenumのswitchをcheckできるツールを作りました

Goでenumswitchcheckできるツール switchecker を作りました。

switchecker

switchecker は Goのソースコード内でswitch文で enum を用いた値のパターンマッチングにおいてenumで宣言されている case をすべて網羅できているかをチェックしてくれるツールです。と、ここまで言いましたが、Goenum ?と思いますよね。厳密にはGoenumは存在しないです。ここで 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.gouse.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")
    }
}

この場合は -sourceenum.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 の要素を増やす必要が出てきたので増やした。そしたら実行時にちょっとバグった。コンパイルでは検知できないけど、ここも検知してほしいな。作ってみるか。と思って作ってみました。

まあ実のところ languageintに名前がついただけなやつなので case 1000 とか書けてしまいこれは switchecker には引っかかりません。。現状はこういった case は無視して、golang,swift...など、連続で定数定義されたものすべてが一つのswitchの中に入っているか check してくれる機能に絞っています。 名前のついていない case 等は実装社の責任としています。

あとはトップレベルで宣言されたものだけを現在対応しています。func の中で同じ構文で書いていても対応していないです。 func の中で宣言するケースはきっと多くないだろう。というのと ast 等で底を解析して他の型と区別させるのが大変。という理由です。

どこまでサポートしているか、想定しているか。を厳密に知りたい方はコードを読むのが一番正しいとは思うのでぜひ見てみてください。
ちなみにテストを見るとサポート範囲のイメージがつかめると思います。

技術

使ったパッケージや構成をさらっと。

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 のリポジトリはこちらです。

github.com

個人的に ast や 型情報みたいなメタな部分を使って効率化を測るツールを作るのが好きで、 その中でもコード量が少なめかつ割と愛用していきそうななツールができたと思っています。 まだできたばかりなので CI にまで組み込めてませんが手元で動かす感じでは期待した通りの動きで満足です。近いうちにCIにも組み込んでみてもっと改善してい後と思います。

enum のチェッカー欲しかったんだよなあ。これいいかも。switchecker って名前がいいね。と思ったそこのあなた

スターください 🌟

おしまい \(^o^)/

チームのコードレビューをよりスムーズにするツールを作りました

これは DeNA Advent Calendar 2019 12/20 の記事です。

チームのコードレビューをよりスムーズにするライブラリを作りました

チームのコードレビューをよりスムーズにするために notifierというツールを作りました。

notifier って何

notifier は 現在はGitHub→Slackに対応しています。例えばGitHubのPullRequestのReviewersAssigneesを追加される事があると思います。このときに自分宛てのメッセージがSlackに通知される。そういった仕組みになっています。他にもGitHubではコメントでメンションを付けるとSlackに通知が行きます。下の画像はGitHubでメンション付きのコメント → Slack に通知が来る流れをスクショして貼りました。SlackにGitHubで実際にコメントされたURLも付いてくるようになってすぐにメンション内容を確認できてGitHub上でのやりとりがSlackの通知をONにしておけばスムーズになります。

image.png image.png

モチベーション

別の人が書いた記事に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を入れましょう。

それで送るイベントも設定しましょう。下の項目をクリックすると選択できるようになります。

image.png

チェックする項目
  • 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.loginGitHubのIDを入れます。
  • slack.id はSlackのworkspaceごとのユーザーIDを入れます。ユーザーIDはここからテストして取得できます。

環境変数の設定

notifierYAML_FILE_PATHNOTIFIER_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^)/