treeコマンドをいい感じにしたツールを作ろうと思っていました

treeコマンドをいい感じにしたツールを作ろうと思っていました

違和感のあるタイトルから始めます。どうもbannzaiです。趣味はコーディングでよくGitHubに作ったもの。作りかけた物をあげています。

treeコマンドをいい感じにしたツールを作ろうと思っていました というタイトルなのですが、今回は「なんか一応動くものできたし、作ろうと思っていた機能は実装したけど、たぶんこれ俺使わないな」ってものができてしまいさてどうやって供養しようか。と思っていてこのようなタイトルになっています。

しかしこの事実と作ったソフトウェアである itreeOSSにすることは無関係だったので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はGitHubOSSとして公開しています。
こちらも合わせて見てもらえるとわかりやすいと思います。

リポジトリ: NotificationHub
記事を書いている時点のコミット: fd39bd11b5bdb937186b2390f86cdae997b37f36

この記事ではどんなアプリを作ったのか紹介と、このプロダクトで使用している技術的な部分をさらっと紹介してちょっとだけ使ってみた感想とか書いていきます。SwiftUI + αを紹介していきますが、このリポジトリを見たらその技術の使い方や、少なくとも NotificationHub ではどうやっているのかを知ってもらえたら困った時に参考にできる状態であると思います。 躓いた部分もいっぱいあって最終的に今の形に落ち着いたのですが、それらを書いていくとブログがすごい長さになると思うので軽くかいつまんで書いていきます。項目によっては後日詳細な記事も書きたいな。と思っています。

NotificationHub

まずはどんなアプリを作ったのか。から紹介していきます。
NotificationHub では GitHubの未読の通知をアプリ内で一覧して見れる機能を提供しています。

Webからだとここから見れるものですね

これを作るかあ。と思った要因として

  1. WatchingしているOSSの通知が多い。目を通しておきたいのも多い
  2. 公開したリポジトリに稀にissueやPRが 飛んでくることがある。1 のこともあって埋もれたりする。

がありました。メールで通知を受け取って確認したり、確認したいタイミングでWebの通知を見てもまあ良かったのですが、メールは件数が多くてあまり通知として来てほしくない。Webの通知画面も導線が少し奥で一覧性があまりよくない。

サクッとアクセスして Organization・個人アカウントごとの通知を一覧できる画面も追加できる。確認したい時に開いて見たいのだけ見る。みたいなアプリがほしいな。と思って作りました。

右上の + アイコンみたいなところからどのGitHubアカウントの通知一覧画面を作るか選択することが出来ます。ONになっているやつの通知一覧画面が追加されます。主要画面は下記の2つですね

通知一覧 + ボタン押した時

あとはタップするとお知らせに対応したGitHubのPRやIssueのページが開きます。 機能としては以上になりますね。

次にこのリポジトリで採用した技術について書いていきます。

Swift Package Manager

NotificationHub ではサードパーティ製のツール導入のために Swift Package Manager を採用しました。 Swift Package Manager も 今年のWWDCXcodeからライブラリを導入できてiOSアプリ開発でも使える。と紹介されていました。
NotificationHub では NukeOAuthSwift の導入に使いました。

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が生えていたので使うことにしました。新しく生えたAPIinstantiateInitialViewControllerinstantiateViewController ですね。使ってみた感想は素直にええやん。ってなりましたね。Storyboardを使っていてiOS13対応ができるアプリ開発者の皆さん。使っていきましょう。

あと書きながら思い出したのですが、 Swift Package Manager 経由で OAuthSwift はそのまま使えませんでした(1ヶ月ほど前は)。自分でも原因調査ちゃんとしたかもはや記憶がさだかではないのですが、暫定対応として無理やりビルドを通すようにしたブランチを本家からforkして NotificationHub では使用しています。たぶんPRにメモがきしてあるあたり OTHER_SWIFT_FLAG に値が設定できなくて積んだからforkしたみたいな感じらしいですが1mmも覚えていません。ここで躓いたかたはよかったら見てみてください。forkしたブランチから上げたPR

どうやらすでに master では入っているみたいですね。6月時点で入っているのでなぜ自分で別ブランチ用意したのか全然わからなくなってしまいましたが、みなさん安心して OAuthSwift 使ってください

OAuthSwiftを使っている場所はここを見れば全体的にどう使っているのか分かると思います。

Redux

NotificationHub では最終的にアーキテクチャとしてReduxを採用しました。NotificationHubではEmbededFramework化しており、Redux用のフレームワーク、ディレクトリが用意されています。さて、ここで参考にしたOSSを紹介したいと思います。SwiftUI x Reduxを実現する上で SwiftUIFlux というライブラリを大いに参考にしました。これを Swift Package Manager 経由でインストールすることも考えたのですが、ObservableObjectになっているStoreobjectDidChange ほしいなあ。とか、スレッド管理少しロジック足したいなあ。とか思ったりして移植という形で記述してあります。ちなみに objectDidChange の方は特に下手するとBad Practiceの可能性もあります。誰か objectDidChange 使わなくてもいい感じに NotificationHub の機能を保ってくれるPRを送ってください(他力本願)。

SwiftUIFlux で感心したのが @EnvironmentObject の使い方です。僕は1ファイルにまとめました。1ファイルにまとまっている方のリンクを貼り SwiftUIFluxを 紹介します。 SwiftUI では通常 View プロトコルに準拠して View の構成を決めていきますが、この RenderableView を準拠することで Redux で管理されている全ての情報源である Store@EnvironmentObject 経由で DI されることになります。これは一度DIしてしまえば同じ階層下にいる細かい View にも @EnvironmentObject が受け継がれ、@EnvironmentObject の宣言をすることで変更通知を受け取ることができるようになります。さらにこの StoreObservableObject であり、 @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 のタイトルを変更するロジックはこうなり、また自分が実現したい機能も実現できたので採用してよかったです。

というのとあまりこのアーキテクチャ最高!っていうのは思考停止なので言いたくないのですが、 SwiftUIRedux というものはある程度親和性があるのではないのだろうか。と感じていたりします。 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で躓いた知見を記事)にしました。よければこちらもご覧になってください。

まとめ

結局技術的な部分についてダラダラといつまでも書いてしまいそうなので強引ですがここで一旦記事を終わりにします。
お気づきの方もいるでしょうがこの NotificationHubSwiftUI 使って何かアプリ作れるかなー。っていうのが原点で始まりました。少し言い訳めいてますが、それもあっていわゆる 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 iTunesConnectApp Apple IDApp Bundle ID を入力しましょう。どちらかが必須項目です。

6 . 同じくDeploy to iTunesConnectApplication Specific Password を入力しましょう。これは 2Appleのアカウントの管理ページ でメモったパスワードを使用します。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 iTunesConnectApplication Specific Password を入力しましょう。これは 2Appleのアカウントの管理ページ でメモったパスワードを使用します。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の仕組みが簡単に用意できる
    • key の typo 防止
    • keyとvalueの組み合わせの乖離
    • 上記2つを自分で用意しなくて良い
  • 用意されるメソッド自体も単純

逆にデメリットは以下が考えられます

  • Swiftの方が書きなれている
  • yamlとSwiftで同じ構造を表しているものが2つできる。
  • yamlファイルの管理が増える

といったところですかね。 UserDefaultsの数によって辛いか辛くないか。というのは大きく変わってきそうですね。

作ったきっかけ

iOSDC Reject Conference Day2というイベントで UserDefaultsに関するライブラリをライブコーディングします という発表をしてこのライブラリができました。 こちらのイベントやライブコーディング等に興味がある方は別のブログがあるのでそちらをご覧になってください。

もともと上記のイベントに発表するか。なんのライブラリを作って発表しよう。と30分でだいたいできるところまで持っていけそうなお題を探していたのがきっかけと言えます。 その中で UserDefaultsGenerator のようなアプローチを取っているライブラリは無いのでは。ということに気づき作るに至りました。

しかし、実際に自他共需要があまりないものを作ったり、技術的なチャレンジができないものを作るほどのモチベーションは持っていませんでした。今回は前者の「需要」というものが知りたいな。と感じて事前にTwitterでアンケートを取ったりした結果が結構面白く、かつ需要ももしかしたら生まれるかもな。と思い作成を決めました。

これがTwitterで集計したアンケート結果です。アンケートの取り始めた当初はもちろんアプリによりますが、冷静に考えたら1~10個とかそんなアプリがほとんどではないだろうか。と思っていました。実際はそのとおりではあったんですけど、2番目に多い結果として 30 よりも多いだったのが意外でした。

また、申告しもらった中では100個を超える人も2人いました。100以上のプロダクトもまああるだろう。とは思っていたのですが、周囲だけでも2人いるのか。と思うと自分の想像力の貧弱さを感じましたね。 発表で使ったissueにも書いてあるので気になる方はそちらをご覧になってください。

技術的なチャレンジ

前述で「需要」があるから作ろう。がきっかけだったのですが、技術的に試したこともあるのでよければソースコードも呼んでみてください。個人的に初めて使ったな。っていうものは下記の2つでした。また違う機会にブログに個別にまとめようかと思います。

  • ライブラリの内部実装でStencilを使ってコード生成しました。
  • Swift Package Manager プロジェクトでGitHub ActionsCodecovを使用しての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^)/

🍵を入れました

🍵を入れました

表題の通りOchaTeapotに入れました。
この記事ではTeapotを紹介したいと思います。

Teapotについて

TeapotはSwiftで作ったライブラリです。 macOSCLIのコンソール上で動くプログラムになります。 Teapotは指定した(1以上の)ファイルの変更を検知して、予め設定しておいたシェルスクリプトを実行します。 Teapotでできることはファイルの何らかの変更(編集・削除等)をもとに任意のシェルスクリプトを簡単に走らせることができる。そういった役割のプログラムです。 たとえばローカルでブログを編集し保存した時に $ git add して git commit をする等を自動で行うことができます。

インストール

Mint 経由でインストールできます。

$ mint install bannzai/teapot

使い方

Teapotの使い方を簡単に紹介します。 Teapotではteapot.ymlという設定ファイルが必要になってきます。下記のように ignore, source, execute の3要素があります。

source:
- build/*
- Sources/*.*
- Sources/Teapot/*.*
ignore:
- ".git"
- ".gitignore"
- tests/*
execute: 
- ls -la __FILE__
- echo $HOME
  • source が 変更点を検知したいファイル指定です。
  • ignore が 検知した変更点を無視するファイル指定です。
  • execute が 変更があったファイルに対して実行するシェルスクリプトです。
  • __FILE__ は変更されたファイルのパスが渡ってきます。

これは $ teapot setup でカレントディレクトリに作成することができます。

$ teapot setup
🍵 Teapot setup completion. You can edit ./teapot.yml 🍵

そして、この用意された teapot.yml を自分の都合に合わせて編集して保存します。これで設定ファイルの準備ができました。続いて $ teapot start をすることで teapot が動き出します。

$ teapot start
🍵 Teapot start 🍵

$ ls -a を実行するように execute に設定していた場合、./Sources/Teapot/main.swift を再度保存した場合に下記のように標準出力されます。

$ teapot start                                                        
🍵 Teapot start 🍵
-rw-r--r--  1 bannzai  1522739515  367  7  6 22:43 /Users/bannzai/develop/oss/Teapot/Sources/Teapot/main.swift

なぜ Teapot なのか

以前にOchaというライブラリを作ってブログにも記事を書きました。今回の TeapotOchaimport して実現しています。Ocha が入っているので Teapot にしました。

作りたかった理由としてはファイル変更時に実行したいことってたまにあるなー。でもOchaは自分でSwiftのコードを改めて書く必要があるなー。もっと気軽に使いたいなー。と思って作った感じです。

細かく制御したい場合は Ocha, 気軽にシェルスクリプトを走らせたい場合は Teapot という使い分けができると思います。

素敵なロゴを描いてもらいました

TeapotのREADMEを見るとわかるのですが素敵なロゴを井上乃彩 / Noa Inoueさんに描いていただきました。実はOchaの時も井上さんにロゴを描いてもらっていました。今回のTeapotにもロゴが欲しかったので引き続きお願いさせてもらいました。Ochaとの親和性も考えられててとても面白くかわいいロゴになっておりとても良いです。今回の自分の作品である OchaTeapot の歴史的な流れがあって、それがロゴというわかりやすい形で関連付けされているのが自分にとっても嬉しいです。

噂のロゴです。

f:id:bannzai:20190707000120p:plain

おまけ

実はすでにoverlookという似たようなツールが存在しています。が、これを動かしたりはしていないです。これを知ったタイミングが少なくともOchaを完成させたあとだったのでTeapotも思いついちゃったし作りきっちゃおう。と思って結果的に車輪の再発明になりました。overlookのほうはちゃんとウォッチしていないので詳細な機能の比較はできないのですが、自分でコードを知っているかどうかで扱いやすさや安心感は違うので車輪の再発明だとしても無駄ではなかったかなと思っています。

最後に

今回はTeapotというライブラリを紹介しました。これも自作であるOchaというライブラリに強く関連されています。TeapotSwiftで動くCLIツールとなっており、簡易的なyaml を用意することで簡単に変更されたファイルに対してシェルスクリプトを実行できるようになっています。コード量自体はそんなに多くないのですらっと読めるかもしれないです。Teapot を使ってバグ等を発見したら日本語でいいのでissuePRを作ってもらえたら嬉しいです。

あとOSSにロゴ乗っているの最高です。テンション上がります。

このライブラリが便利!最高!クール!ロゴかわいい!と思ったそこの貴方へ

スターください 🍵

おしまい \(^o^)/

GoでOSS書いてみました

GoでOSS書いてみました

表題の通りこの記事では Go で書いた OSS である constructor を紹介します。

constructor

constructorGo製のGoのコードを生成してくれるシンプルなCLIツールになっています。 constructorはいわゆるコンストラクタ関数を生成してくれます。

コンストラクタについてはEffective Goにも書かれているので気になる方はこちらもどうぞ。

例えば struct FooBar という stirng型のフィールドが定義されている場合を考えます。

type Foo struct {
  Bar string
}

これをファイルに保存して、 constructor generate を実行します。すると下記のようなコンストラクタが自動で生成されます。

$ constructor generate

で、下のような Foo に対応したコードが生成されます。

func NewFoo(bar string) Foo {
    return Foo {
        Bar: bar,
    }
}

constructor を使う流れ、大まかな機能はこれで以上です。

なんで作ったの

今回このOSSを書いたモチベーションはGoの構造体を安全に new したかったからです。ここでいう安全とはstructを使用する側は定義されているフィールドをすべて使用してインスタンス化したい。そうでない場合はコンパイルエラーになってほしい。このコンパイルエラーで検知できる状態を安全と定義しています。確認のためにベタにstructを書いた場合に置き得る想定している課題を書いていきます。例には先程の Foo を使用します。

func main() {
  foo := Foo {
    Bar: "#main",
  }
}

こちらは Fooインスタンスを作って変数に代入しています。このFooインスタンス化をしているコードがいくつかプロジェクト内に存在していたとしましょう。ここで Foo にフィールドを一つ追加します。

type Foo struct {
  Bar string
  Piyo int // <- New field
}

Piyo というフィールドを追加しました。さて、このフィールドは 0 埋めじゃない値が入っている必要がある。そういうフィールドにする必要があると仮定します。この場合各 Foo を使っている該当コードを検知する術はありますでしょうか。grepだったりUnitTestかもしれませんね。しかしこれらは見つけるのにはいささか人に依存した方法になります。つまりミスや対応し忘れを招く可能性が大きくなります。 constrructor はこの問題の早期発見、またはどのフィールドが RequiredOptional かの判断材料を提供できるライブラリになっています。

constructor で生成されるコードは基本はstructが持っているフィールドを網羅する形で関数が引数を取って、構造体に代入して返す関数を作ります。constructor generate を再度実行することで Piyo フィールドを追加したときも先程の NewFoo の関数は引数を取る数が変わることになります。

func NewFoo(bar string, piyo int) Foo {
    return Foo {
        Bar: bar,
        Piyo: piyo,
    }
}

最初からこの NewFoo を使って Fooインスタンス化していた場合のことを想像しましょう。 Piyo フィールドが増えた場合は関数の引数が足りなくてコンパイルエラーになります。つまりGoのプログラムが実行できない明らかな間違い状態にすることができます。残念ながら 0 埋めじゃない値が入っているというロジカル部分の保障はできませんが、それでも単純な間違いを一つ減らすことができたことに価値があると思います。

func main() {
  foo := NewFoo(
    "#main",
    1000,
  )
}

しかし、これだと初期化したくないフィールドをコンストラクタに含めたくない気持ちも、まあわかります。(実はGoでも初期化したくないには出会ったこと無いのですが) そこでコンストラクタの対象から外せる ignore_constructorというフィールドにつけられるタグを用意してあります。下記のように対象から外したいフィールド(Piyo)にタグをつけるだけで NewFoo メソッドの引数から除外されます。

type Foo struct {
  Bar string
  Piyo int `ignore_constructor:true`
}
func NewFoo(bar string) Foo {
    return Foo {
        Bar: bar,
    }
}

補足

constructor を使うで3つの形式のフォーマットのファイルが必要になってきます。

  1. .goファイルです。コンストラクタを作る上で.goで書かれたstructが必要です。
  2. .yamlファイルです。どのファイルから読み込んで、どこに出力するか等の設定情報が必要です。
  3. .tplファイルです。NewFoo関数をどのように構成するかのテンプレートファイルが必要です。詳しくはtemplate

2, 3 のファイルは constructor setup コマンドで設定ファイルの雛形を手に入れたり、テンプレートを入手することができます。

READMEもぜひ見てみてください。

最後に

最後に簡単に自分のGoの経歴を簡単に。1年ほどiOSアプリエンジニアをやりながらGoでAPIサーバーを書いていました。つい最近の3週間ほど前からメインの業務を切り替えてiOSエンジニアからGoでAPIサーバーを書くことになりました。そこでGoになれるようにライブラリ作ってみるか。とかそういうモチベーションもあり今回のライブラリを作りました。もしかしたらまだGoに慣れていないところもあるのでなにか違うことがあればbannzaiあてに連絡でもください。

さて、ところで私はiOSエンジニア界隈ではOSSを作っては発表していたらいつのまにかスター乞食とあだ名がついてしまい親しみのある代名詞があります。今後はGo界隈にもOSS作っては発表しに行くかもしれませんね。

それはさておき、このライブラリが便利!最高!クール!bannzaiいいぞ!bannzai応援しているぞ!Go界隈へようこそ!この乞食!と思ったそこの貴方

スターください

おしまい \(^o^)/