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^)/
🍵を入れました
🍵を入れました
表題の通りOchaをTeapotに入れました。
この記事ではTeapotを紹介したいと思います。
Teapotについて
TeapotはSwiftで作ったライブラリです。 macOSのCLIのコンソール上で動くプログラムになります。 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というライブラリを作ってブログにも記事を書きました。今回の Teapot は Ocha を import
して実現しています。Ocha が入っているので Teapot にしました。
作りたかった理由としてはファイル変更時に実行したいことってたまにあるなー。でもOchaは自分でSwiftのコードを改めて書く必要があるなー。もっと気軽に使いたいなー。と思って作った感じです。
細かく制御したい場合は Ocha, 気軽にシェルスクリプトを走らせたい場合は Teapot という使い分けができると思います。
素敵なロゴを描いてもらいました
TeapotのREADMEを見るとわかるのですが素敵なロゴを井上乃彩 / Noa Inoueさんに描いていただきました。実はOchaの時も井上さんにロゴを描いてもらっていました。今回のTeapotにもロゴが欲しかったので引き続きお願いさせてもらいました。Ochaとの親和性も考えられててとても面白くかわいいロゴになっておりとても良いです。今回の自分の作品である Ocha
→ Teapot
の歴史的な流れがあって、それがロゴというわかりやすい形で関連付けされているのが自分にとっても嬉しいです。
噂のロゴです。
おまけ
実はすでにoverlookという似たようなツールが存在しています。が、これを動かしたりはしていないです。これを知ったタイミングが少なくともOchaを完成させたあとだったのでTeapotも思いついちゃったし作りきっちゃおう。と思って結果的に車輪の再発明になりました。overlookのほうはちゃんとウォッチしていないので詳細な機能の比較はできないのですが、自分でコードを知っているかどうかで扱いやすさや安心感は違うので車輪の再発明だとしても無駄ではなかったかなと思っています。
最後に
今回はTeapotというライブラリを紹介しました。これも自作であるOchaというライブラリに強く関連されています。TeapotはSwift
で動くCLIツールとなっており、簡易的なyaml
を用意することで簡単に変更されたファイルに対してシェルスクリプトを実行できるようになっています。コード量自体はそんなに多くないのですらっと読めるかもしれないです。Teapot を使ってバグ等を発見したら日本語でいいのでissueやPRを作ってもらえたら嬉しいです。
あとOSSにロゴ乗っているの最高です。テンション上がります。
このライブラリが便利!最高!クール!ロゴかわいい!と思ったそこの貴方へ
スターください 🍵
おしまい \(^o^)/
GoでOSS書いてみました
GoでOSS書いてみました
表題の通りこの記事では Go
で書いた OSS
である constructor
を紹介します。
constructor
constructor は Go
製のGo
のコードを生成してくれるシンプルなCLIツールになっています。 constructorはいわゆるコンストラクタ関数を生成してくれます。
コンストラクタについてはEffective Goにも書かれているので気になる方はこちらもどうぞ。
例えば struct Foo
が Bar
という 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 はこの問題の早期発見、またはどのフィールドが Required
か Optional
かの判断材料を提供できるライブラリになっています。
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つの形式のフォーマットのファイルが必要になってきます。
.go
ファイルです。コンストラクタを作る上で.go
で書かれたstruct
が必要です。.yaml
ファイルです。どのファイルから読み込んで、どこに出力するか等の設定情報が必要です。.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^)/