文字っぽいの。

文字を書いています。写真も混ざります。

山崎実業のお風呂の壁につけられる収納でお風呂場を整理した。

お風呂の壁、多くの場合はマグネットがくっつくようになっていて、最近では「磁石でくっつく洗面器!」とかも売り出されている。 「磁石くっつくのかー。」と思って自分の家でも適当な磁石で試してみたら無事にくっついたので、山崎実業のお風呂収納をいくつか購入した。

山崎実業(Yamazaki) 浴室用ラック ホワイト 約W28XD9.5XH8cm MIST 4237

山崎実業(Yamazaki) 浴室用ラック ホワイト 約W28XD9.5XH8cm MIST 4237

  • 発売日: 2019/03/27
  • メディア: ホーム&キッチン

これには妻が利用するシャンプー・コンディショナー・ボディーソープのボトルや、メイク落としとか諸々のチューブ類が乗っけてある。

男の自分は少ないもので、これで事足りる。立ってシャワーをすることが多いので、高めの位置にくっつけられるのが良い。

ひげそりを置くやつも買った。上の多機能ラックに全部載せられるので、要らなかったかもしれない。

シェーバー類がうまく収納できなくて困っていたが、これを使うといい感じに収納できてよかった。


特に買ってよかったのがこれ。

おもちゃラックだけど、お掃除用具を入れるのに使っている。

f:id:FromAtom:20210116223413j:plain
ごちゃごちゃお掃除セット

お掃除用具はどうしても濡れてしまうのでお風呂外での収納が難しく、かといってお風呂の中では置いておく場所がない。前までは雑なプラカゴに入れていたけど、置いてある場所が汚れやすいし見た目もダサかった。 これなら風呂場の角にくっつけておけるし、浮いてるので水はけも良い。もし邪魔なら外して別の角に貼り付ければ良くて最高。

これらの製品を導入によって、床に物がなくなってのがとても良い。風呂の床にラックなどを置いてあるとどうしても底に水が溜まって汚れるのが早いし、こまめに掃除するのも億劫になる。 また、スクレイパーで水を払うのも物があるとやりにくい。とにかくすべてを空中に固定することで、風呂床の掃除が行いやすくなって便利。

ちなみに「磁石だとシャンプーとかのポンプ押したら落ちないの?」と思っていたけど、よほど力強く押す必要があるほどポンプが固くなければ大丈夫だろう。普段遣いでは全く問題ない。

洗濯機を買い替えた。

大学生の頃からずっと使い続けていた洗濯機をようやく買い替えた。この洗濯機は父親から譲り受けたもので、2008か2009年製の無印良品のものだった。かれこれ12年以上使われているのに、なぜか壊れないので買い換えるタイミングを失っていた。

f:id:FromAtom:20210116150341j:plain
なぜか壊れなかった洗濯機

しかし、さすがに容量も単身者用で小さいし、汚れも気になるようになったので買い替えた。

"時代はドラム式洗濯機"ということで、採寸してみたところ最小サイズでもサイズがピッタリ過ぎたので諦めた。ピッタリなら良いと思うけど、我が家にはこういったランドリーシェルフがあるので、左右に隙間が無いと困ってしまう。

どうしてもドラム式洗濯機が欲しいというわけではなく、容量が増えてあわよくば洗剤の自動投入あると嬉しいという温度感だったので、その条件で洗濯機を探していた。どうやらパナソニック製では縦型でも乾燥機能がついている洗濯機でないと自動投入はないらしい。

というわけで、パナソニックのNA-FW100K8を購入した。

f:id:FromAtom:20210116172413j:plain

panasonic.jp

何度か使ってみた感想を書く。

自動投入はやっぱり便利で、今まで人間がお猪口でオットットしていたのがアホらしくなる。アタックゼロにしたことでかなり楽になったが、それでも作業が有るのと無いのでは大違い。漂白剤だけ手動だけど、これぐらいならギリギリ許容できる。こう考えると食洗機も自動投入になってほしい。

乾燥機能はちゃんと乾くし、タオルもフカフカになる。ただ、冬場はエアコンの前に干しておいたほうが加湿になるしなぁと使うことは少なそう。梅雨シーズンには助かりそう。あとはシーツや布団カバーなど、干すのが面倒かつ乾くまで時間がかかる大物を洗濯乾燥すると。晴れた日に洗濯をガッと回したいのに、こういう大物は物干し竿を専有するので、どうしても優先度が落ちがちなので助かる。

駆動音については、10年以上前の洗濯機と比べるまでもないが、非常に静かだ。さすがに乾燥時にはうるさいが、洗濯時は脱水でも非常に静か。最初は「これ洗ってるのかな?スタートボタン押し忘れたのか?」と思うほどだった。これなら在宅勤務中に洗濯機を回しても大丈夫そう。

デメリットとしては運転中には蓋がロックされるタイプなので、洗っている様子を眺めることができない。脱水時に爆音がしないので「もうすぐ終わるな」と把握することができない。値段が高い。

kakaku.com

自分は家電量販店で値引き交渉してWeb最安値から2万ほど引いてもらって、古い洗濯機の回収もつけてもらえたけど、Webでポチッと買いたい場合には「ドラム式洗濯機でよくね?」という値段だと思う。縦型なので壊れにくく、その点で費用が回収できるといいなと思っている。

Swiftで大量のメンバ変数があるclassやstructのinitを自動生成する。

環境

課題

開発をしていると、こんな感じでメンバ変数がたっぷりあるclassができます。この例ではclassですが、structも同様ですね。

class SampleClass: Codable {
    public let id: Int
    public let title: String
    public let body: String
    public let thumbnailUrl: URL
    public let tags: [String]
    public let categories: [String]
    public let createdAt: Date
    public let updatedAt: Date
    public let comment: [String]
    public let isFavorited: Bool
    public let isBookmarked: Bool
    public let url: URL
}

Swiftのstructではinitが自動生成されますが、Embedded Frameworkを活用してModelをアプリ外に出しているとその恩恵を受けることができません。Modelを外に出している場合は、Codableに準拠しておくだけで済むことが多いのですが、たまにinitが必要になります。

さて、このメンバ変数に対応したinitを書くと、

init(id: Int, title: String, body: String, thumbnailUrl: URL, tags: [String], categories: [String], createdAt: Date, updatedAt: Date, comment: [String], isFavorited: Bool, isBookmarked: Bool, url: URL) {
    self.id = id
    self.title = title
    self.body = body
    self.thumbnailUrl = thumbnailUrl
    self.tags = tags
    self.categories = categories
    self.createdAt = createdAt
    self.updatedAt = updatedAt
    self.comment = comment
    self.isFavorited = isFavorited
    self.isBookmarked = isBookmarked
    self.url = url
}

と、結構な量を書く必要があり面倒です。実際のプロダクトコードでは、もっと多くのメンバ変数を持つ場合もあります。

解決法

XcodeのRefactor機能を使って自動生成します。

  1. class名 or struct名を右クリックする
  2. "Refactor"メニューを選ぶ
  3. "Generate Memberwise Initializer"をクリック

f:id:FromAtom:20210106000215g:plain

これで簡単にinitが生成できます。めでたしめでたし。

Xcode12時代のライブラリ管理ツール選定

背景

iOSDC 2020でMint及びiOSアプリ開発時のライブラリ管理について執筆し、同様の内容をZennでも本として販売した。

zenn.dev

しかし、Xcode12の正式版がリリース後に、執筆時からライブラリ管理を取り巻く状況が大きく変わってきたため、ここに筆を執った次第である。

はじめに

この記事はXcode12を利用してiOSアプリ開発を行う前提で執筆されている。また、情報は2020年12月25日現在のものとなる。

iOSアプリ開発を行う場合、ライブラリ管理には

  • CocoaPods
  • Carthage
  • Swift Package Manager(以下 SwiftPM)
  • Manual

が選択肢として挙げられる。Manualは管理といえるのか怪しいため今回は省略する。なお筆者は、

  • 本業:Carthage, CocoaPods, Mint 1
  • 副業:SwiftPM, Mint
  • 個人開発1:SwiftPM, Mint
  • 個人開発2:CocoaPods, cocoapods-binary

という構成で開発を行っている。

以下、CocoaPods、Carthage、SwiftPMのそれぞれでライブラリ管理をする場合のメリットとデメリットを述べる。

CocoaPods

■メリット

CocoaPodsは2011年に作られたツールで、すでに9年以上の歴史がある。Objective-CからSwiftへの移行も乗り越えた歴戦の戦士であり、十分に長い年月利用されているため、とてもよく枯れている。

また、CocoaPodsにはPluginの仕組みがあり、便利なサブコマンドを追加することができる。有名なところではcocoapods-keyscocoapods-binaryがある。これらPluginもGemとして提供されているので、Gemfile に追記して bundle install して Podfile に追記するだけで利用することができる。

■デメリット

Ruby Gemなので、Rubyが必要になる。Rubyは別に嫌いではないし、個人OSSでも利用している。ただ、こちらとしてはiOSアプリ開発をしたいのであって、Rubyでコードを書くわけではない。SwiftでiOSアプリを書きたいだけなのに、どうしてrbenvでRubyを入れ、Bundlerを入れ、Gemfileを書いてCocoaPodsを入れてバージョン管理をしなければならないのだろうか。Fastlaneも同様にGemなので許容範囲とも考えられるが、正直管理するものが多くて面倒である。

[追記 2020/12/25 15:45]

今ではSystemのRubyを利用することも可能なようです。

[追記ここまで]

よく知られたデメリットとして、Clean Buildするとライブラリも再ビルドが必要になるためビルド時間が長くなる点がある。これはCocoaPodsの仕組み上なくなることは無いが、最近ではそこまで気にしなくても良いだろう。Swift2や3系の頃のSwiftコンパイラはかなり安全側に倒してあり、差分ビルドよりフルビルドが選ばれがちであった。そのため、1行変更しただけでフルビルドが走り、その度にCocosPodsで導入したライブラリ群もビルドされるため、長時間のビルドを待つ事態が頻発していた。Swift5系の今では、こういった挙動は皆無ではないにせよかなり減った。

SwiftUIで開発していると稀に「Derived Dataを消してXcodeを再起動すると直るエラー」に出会う。これが何を起因として発生しているのか不明なのでなんとも言えず、Xcodeのアップデートによって発生しなくなるかもしれないが、もし頻発するようであればフルビルドによって開発速度が低下することは記しておく。

Carthage

■メリット

導入には.pkg, Homebrew, Mintなどが使える。別のプログラミング言語を準備すること無く導入が可能である。

Carthageで導入されたライブラリは事前にビルドされており、Xcode上からはビルド済みのライブラリを利用するため、Clean Buildをしてもライブラリ群の再ビルドが走らない。これによって、CocoaPodsに比べてビルド時間を短縮することができる。ちなみに、cocoapods-binaryでも同様の状況にはできる。

■デメリット

Mintを利用せず .pkg やHomebrewで導入した場合、Carthage自体のバージョン管理を行うことができない。そのため、複数人で開発をしている場合に、チームメンバーそれぞれの手元で異なるバージョンのCarthageが動くことになる。これはMintを導入することで解決可能なため、些末な問題だとも言える。

ライブラリ群が事前にビルドされることはデメリットにもなり得る。開発環境を整える際に大量のライブラリ群のビルドを待たなければならないし、 git switchした際にライブラリのバージョンが異なっている場合にも、ビルドし直さなければならない。これは、Carthage/Build ディレクトリをGitの管理下に含めることで対処可能ではある。しかし、Carthage/Build のサイズが大きいためGitリポジトリの肥大化が加速してしまい、別の問題に直面することになる。

さて、ここまで色々と書いてきたが最も大きなデメリットは、Xcode12からCarthageがまともに使えなくなってしまったことだ。この問題は公式から案内されているWorkaroundを利用することで対応が可能ではある。

しかし、2020年9月16日にXcode12 GMが提供されてから3ヶ月が経った今でもこの問題は解決されていないため、対応を待ち続けるのは良策ではないだろう。

このWorkaround問題に関係しているが、未だにCarthageはXCFrameworksに対応できていない点もデメリットの一つである。XCFrameworksはWWDC2019で発表された仕組みであり、当時は「これをCarthageがサポートしたら copy-frameworksが不要になる!」と言われていた。優秀なOSSコミッター各位が揃っている中で未だに対応していないということは、非常に大きく困難な壁があることは想像に難くない。なお、CocoaPodsとSwiftPMはXCFrameworksに対応している。

SwiftPM

■メリット

Xcodeをインストールすればすぐに使える。サードパーティツールを導入する必要が無く、Xcodeを起動して待っているだけでコードを書き始められる点が最大のメリットだろう。CLIツールを利用しなければ、セットアップスクリプトも不要になる。

iOSアプリを開発をしたいのにRubyやHomebrewを利用してサードパーティツールを入れ、そのツールをセットアップするために、setup.shMakefileに細々と設定を書く必要がなくなるのは非常に心地が良い。README.md# SETUP 項には

$ xed .

とだけ書けば良い。

■デメリット

SwiftPMではXcodeでProjectが開かれると、管理しているライブラリ群の更新を確認する。これはAndroidのGradleに似た挙動ではあるが、地味に時間がかかるため若干不便である。もちろん利用するライブラリが増えると、この待ち時間は長くなっていく。

もっとも不便さを感じるのは、SwiftPMではライブラリをStatic Frameworkとして扱う点である。このため、例えばライブラリAがアプリ本体とWidgetKitの両方で使われていると、エラーになってビルドができない。この問題を解決するためには、1度自作のFrameworkに包んでDynamic Frameworkの様に振る舞わせる必要がある。

この問題は、Embedded Frameworkでも発生する。なお、筆者は試していないがEmbedded Frameworkをprojectとして管理し、それぞれのproject毎にSwiftPMの設定をすることで回避することが可能かもしれない。

Firebase iOS SDKやRxSwiftを利用した際にも問題がある。Firebase iOS SDKのSwiftPM対応はまだbetaであるため、Workaroundを利用しないとAppStore Connectへのバイナリアップロードができない。

If you're using FirebaseAnalytics, Xcode 12.0, and have an issue with device installation or archive uploading, see the workaround at https://github.com/firebase/firebase-ios-sdk/issues/6472#issuecomment-694449182.

RxSwiftの場合はRxTestを利用している場合にリンクエラーになるバグがある。

[SR-12303] Swift Package Manager package stopped working after Xcode 11.3, Xcode 12, etc. - Swift

これからアプリを書き始めるならCombineを使う手もあるが、すでにRxSwiftとRxTestが導入されているプロジェクトで、Combine移行するコストは無視できない大きさである。

結論

2020年12月25日現在では、CocoaPodsを選択するのがベターだと言える。

しかし、今後CarthageのXCFrameworksサポートや、Firebase iOS SDKのSwiftPM/Carthageサポート完了によって最適解は都度変化するだろう。もし記事公開から数カ月後にこの記事を読んでいる読者がいたら、最新の状況を調べてから判断して欲しい。

CLIツールの管理

話が脇道にそれるが、Swift製のCLIツールのバージョン管理についても書く。CLIツールとしてよく使われるものには、

  • realm/SwiftLint
  • nicklockwood/SwiftFormat
  • mono0926/LicensePlist
  • mac-cain13/R.swift
  • SwiftGen/SwiftGen
  • yonaskolb/XcodeGen

などがある。

まず、Homebrewによるバージョン管理は困難2なため除外する。そのため選択肢としては、SwiftPM, Mint, CocoaPods,cocoapods-binaryがある。

SwiftPMの場合、すでにアプリ側でSwiftPMを利用している場合にダミーファイルを用意する必要があるため若干煩雑である。よって現時点では、Mint, CocoaPods, cocoapods-binaryのいずれかを用いるのがよいだろう。もし、アプリ側のライブラリ管理をCocoaPodsに一元化するのであれば、cocoapods-binaryを利用するのがベターだと考える。

実際にこれらツールを用いてCLIツールを管理する方法については、上述したZennの本でも記載している。ただし、わざわざ購入しなくても検索したほうが無料で最新の情報が得られるだろう。

zenn.dev

感想

私個人としてはCarthage+Mintによって「最初にビルドをしておけば、後は高速にコードを書き続けられる」という状況が得られると考えていたが、昨今の状況を鑑みると難しそうである。


  1. CocoaPodsを引き剥がそうと思いながら作業をしていたら、Xcode12リリース後に状況が一変してしまって厳しい。

  2. HomebrewでもFormula提供者が特別な対応をすることでバージョンを指定した(かのような)インストールが可能だが、全ての過去バージョンがインストールできるかはFormula提供者が対応するかどうかに依存する。

Xcodeのデバッグコンソール画面が勝手に閉じて不便なやつ対策

環境

起こること

ビルドしてシミュレータが起動するたびに、デバッグコンソール画面が閉じてしまう。

f:id:FromAtom:20201217192005p:plain
デバッグコンソールはここのこと

ログを見たいのに、ここが閉じてしまうと見れなくてとても不便だった。

対策

XcodeのPreferencesのBehaviorsタブを開き、Running -> Completesから下記の項目を変更する。

f:id:FromAtom:20201217192207p:plain

変更内容はチェックを外すでも良いし、下記画像のように変更しても良い。

f:id:FromAtom:20201217192900p:plain

もしくはこうすると、毎回勝手にデバッグコンソールが開いてくれる。

f:id:FromAtom:20201222132238p:plain

謝辞

この対応方法は@k_katsumiさんに教えていただきました。ありがとうございました。

2020年に新しく始めたこと、諦めたこと。

新しく始めたこと

今年、30歳になりまして、区切りが良いので色々と新しいことを始めることにしました。正直、始めすぎました。

テックリード

昨年末ぐらいからテックリードというのをやっていて、新しい肩書が増え、仕事が増えました。まぁ本業のことはどうでもいいでしょう。

副業

「新しいことにチャレンジしたい!」となると、転職をして職を変える手もあるんですが、個人的には「なんで新しいチャレンジしたいだけなのに、転職なんてクソ面倒な事せなあかんねん。」と思っていました。あと、基本的に自分は飽き性なので、どうせ転職しても次の仕事も2年ぐらいで飽きてしまうはずです。

だったらもう個人事業主として開業して、面白そうなサービスやアプリを作っている会社さんを手伝ったほうが良いじゃんと思って副業をはじめました。 ありがたいことに、手伝いたいと思っていた会社さんと業務委託契約を結ぶことができ、楽しく働くことができています。

本業とは異なったことができるので楽しいです。また「自分、食いっぱぐれることは無さそうだな」という感覚を得られたのが特に良かったです。会社で働いていると「社内では役に立つけど、実は世間では無能なのでは……?」という不安に襲われることがあるのですが、それが解消されて心穏やかに暮らせています。

あと、単純に労働時間が増えるので収入が増えて嬉しい(税金から目をそらす)。

Podcast

よく弟と通話しながらゲームをしているんですが、「この会話を録音したら面白いのでは?」と思い、Podcastをはじめました。

弟とひたすら「これがいい」だの「あれがうまい」だのという話をしています。結構楽しくて地味に続いていますが、最近はGhost of Tsushimaの冥人奇譚で忙しいので、収録できてません。

個人アプリ開発

自分の中で「iOSアプリエンジニアを名乗っているのに、個人でiOSアプリをリリースしていないのはいかがなものか?」みたいなコンプレックスがずっとありました。もちろん他の人にそんなことは思って無くて、自分自身に対してだけです。

エンジニアだからといって、全員がOSS活動をして、本を書き、勉強会に積極的に参加し、アプリやツールをリリースしているわけでないですからね。インターネットを眺めていると、様々な人間の成果が融合されて架空の超人エンジニアが脳内に生まれてしまいがちです。

それはさておき、弊社には個人アプリ開発勢が結構居て、「いいなー、自分も個人アプリ開発者になりたい!」という気持ちが強くなってきたので、アプリを作ってリリースしました。

Memoliaという箇条書きでメモが書きやすいアプリです。実装からデザインまで自分一人でやりました。アプリの開発よりも、Appleへの審査提出周りの方が面倒でした。

使わない人は全然使わないし、使う人はめっちゃ使うアプリかと思いますが、無料なのでぜひ一度お試し下さい。現状、広告もなくて自分は1円も儲からないアプリとなっております。

あきらめたこと

色々始めるということは、なにかを諦めないといけません。1日は24時間ですからね。

丁寧な夕飯

COVID-19の影響でリモートワークになり、毎食自炊ができるようになりました。しかし、夕飯を作るというのは結構時間がかかるものです、買い出しに行って料理をして食べて片付ける。それだけで2時間ぐらいは吹き飛んでしまいます。

なかなかに時間がなくなってしまって困ったので、スーパーのサラダ、サラダチキン、お刺身を日によって違う味のものを買ってきて済ませるようにしました。

料理はもともと好きなので、丁寧な料理は土日に楽しむこととしました。

まとめ

2020年、全世界的に激動な1年となりましたが、個人的にも新しいことを色々と始めて激動な1年となりました。

めちゃくちゃ当然ですが、新しいことを複数一気に始めないほうが良いですね。やりたいことがたくさんあるのに、時間が全く足りなくなって、もやもやした日々を過ごすことになってしまいます。

今年は発散したので、来年は収束させる年にできると良いなぁと思っています。ずっと忙しすぎて記憶が何もねぇ。

SwiftUIで表示したModalをコードからdismissしたい

やりたいこと

f:id:FromAtom:20201107232000g:plain

いわゆる dismiss(animated:completion:) をしたい。

コード

struct ContentView: View {
    @State private var showModal = false

    var body: some View {
        Button("モーダルを開く") {
            showModal.toggle()
        }.sheet(isPresented: $showModal) {
            ModalView()
        }
    }
}

struct ModalView: View {
    @Environment(\.presentationMode) private var presentationMode

    var body: some View {
        Button("閉じる") {
            presentationMode.wrappedValue.dismiss()
        }
    }
}