文字っぽいの。

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

iPadではpopover、iPhoneではsheetとして表示されるModalをUIKitで実装する。

課題

iPadではpopoverとして表示をしたいが、iPhoneでは画面が狭いのでsheetとして表示したいということがある。 またsheetとして表示するならば、 UISheetPresentationController を利用して、いわゆるハーフモーダルにも対応した実装を行いたい。

実装

final class ViewController: UIViewController {
    @IBAction func settingButtonTouchUpInside(_ sender: UIButton) {
        let viewController = PopoverViewController.viewController()

        viewController.modalPresentationStyle = .popover
        viewController.popoverPresentationController?.delegate = self
        viewController.popoverPresentationController?.sourceView = sender
        viewController.popoverPresentationController?.sourceRect = sender.bounds
        viewController.popoverPresentationController?.permittedArrowDirections = .up

        // NOTE: ここで次の設定をしても sheetPresentationController が nil のため反映されない
        // viewController.sheetPresentationController?.detents = [.medium(), .large()]
        // viewController.sheetPresentationController?.prefersGrabberVisible = true

        present(viewController, animated: true)
    }

}

extension ViewController: UIPopoverPresentationControllerDelegate {
    func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
        return traitCollection.horizontalSizeClass == .compact ? .formSheet : .none
    }

    func presentationController(_ presentationController: UIPresentationController, prepare adaptivePresentationController: UIPresentationController) {
        if let sheet = adaptivePresentationController as? UISheetPresentationController {
            sheet.detents = [.medium(), .large()]
            sheet.prefersGrabberVisible = true
        }
    }
}

結果

SOUNDPEATSのイヤーカフイヤホンが良かった。

前から気になっていたところセールで安かったので買ってみた。

良かった点

  • セール時には6000円くらいで変えて安い
  • 思ったよりちゃんとした音質で聞ける
  • 左右の区別がない(!)
  • メガネやマスクと干渉しない
  • マルチポイント接続できる
  • カナル型と違って落ちない
  • TypeC充電

微妙な点

  • タッチ操作で音量操作や再生停止ができるが、少し難しい。こういうのは物理ボタンのほうがやりやすい。

感想

このイヤホンには左右が存在しない。ケースから取り出す時に右側にしまわれている物は右耳用、左側のものは左耳用になる。しかもケースには逆にしまうことも可能。ケースにしまった後で蓋を締めて10秒経つと右側は右耳用、左側は左耳用に勝手に切り替わるようになっている。しまう時もつける時も簡単に使えるのがとてもいい。

装着感もかなり楽で、長時間つけていても苦にならない。外音も聞こえるし、耳を塞がないので風呂上がりや運動時、熱い夏でも耳の穴が密閉されなくて良い。

メガネやマスクとも干渉しないし、Shokzのように首の後ろにワイヤーがあったりもしないので、ヘッドレストやパーカーと干渉することもない。非常に快適に使えて便利。

使い始め初期は装着に手こずるかもしれないが、すぐに片手だけでつけられるようになる。自分はカナル型がどうにも耳の形に合わず、歩いたり下を向くとすぐに落ちてしまって危ないけれど、これなら落ちないので安心感も高い。

高音質で音楽を楽しむためのイヤホンとしては当然力不足ではあるが、家事をする時や出かける時、作業時のながら聞きなどには非常に良いイヤホンで愛用している。

SwiftのJSONDecoderでISO 8601拡張形式(ミリ秒など)のDateもDecodeできるようにする

はじめに

SwiftでJSONをDecodeしてDecodable protocolを継承した構造体・クラスを生成することはよくある。その場合に、JSONに含まれるDateはISO 8601形式である場合が多い。

ISO 8601基本形式であればJSONDecoderに用意されている設定を利用すれば良い。

let jsonDecoder = JSONDecoder()
jsonDecoder.dateDecodingStrategy = .iso8601

あとはこのJSONDecoderを利用してデコードすれば良い。

課題

上述した書き方ではミリ秒などの拡張形式に対応できない。例えばこのようにミリ秒まで含められる拡張形式。

{
    "createdAt": "2024-10-10T08:00:00.110Z",
    "updatedAt": "2024-10-10T20:00:00.220Z"
}

これはdateDecodingStrategyに .ios8601 を指定したJSONDecoderではデコードに失敗する。

解決策

ありがたいことに、SwiftにはISO8601DateFormatterという、その名の通りなFormatterが存在する。

developer.apple.com

ただしこれはJSON Decoderではないので、 JSONDecoderがこれを利用できるように少しコードを書く必要がある。

struct CodableItem: Codable {
    var createdAt: Date
    var updatedAt: Date
}

let jsonString = #"""
{
    "createdAt": "2024-10-10T08:00:00.110Z",
    "updatedAt": "2024-10-10T20:00:00.220Z"
}
"""#

let jsonDecoder = JSONDecoder()
jsonDecoder.dateDecodingStrategy = .custom { decoder -> Date in
    let container = try decoder.singleValueContainer()
    let dateString = try container.decode(String.self) // 一旦Stringとして取得する

    let dateFormatter = ISO8601DateFormatter()
    dateFormatter.formatOptions = [
        .withInternetDateTime,
        .withFractionalSeconds,
        .withDashSeparatorInDate,
        .withColonSeparatorInTime,
        .withColonSeparatorInTimeZone
    ]

    // StringからDateに変換
    guard let date = dateFormatter.date(from: dateString) else {
        throw DecodingError.dataCorruptedError(in: container, debugDescription: "不正なDate")
    }

    return date
}

let jsonData = jsonString.data(using: .utf8)!
do {
    let decodedData = try jsonDecoder.decode(CodableItem.self, from: jsonData)
    print(decodedData.createdAt)
    print(decodedData.updatedAt)
} catch {
    print(error)
}

このコードで、ミリ秒が含まれたDateでもデコードすることができる。

APIによっては、EndpointやEntityの種類によってミリ秒が含まれたり含まれなかったりすることもある。そんな場合はJSONDecoderを複数作るよりも .custom {} 内部で formatOptions を変えた ISO8601DateFormatter を作って、分岐処理をすることで簡単に対応できる。

2024年の買ってよかったもの。

RICOH GR IIIx

このカメラは本当に買ってよかった。

何を撮っても、すごく気持ちがいい写真が撮れる。本体も小型なので、ちょっとしたお出かけにも持っていきやすい。一眼レフだとどうしてもカバン+カメラという構成になるけど、GR Ⅲxならカバンに入れられるので身軽に出かけて、気軽に写真が撮れる。

AGM PAD P1

防水のAndroidタブレット。これもいい買い物だった。 お風呂に入る時にiPadiPhoneで動画を見たいが、毎回防水ケースに入れたり出したりするのが非常に面倒だった。 「ならもう防水のタブレット買っちまえ」と買ったのがこれ。 普通のAndroidなのでYouTube, プライムビデオ, AbemaTVなど、なんの問題もなくサクサク動いてくれる。

ただし、厚いしゴツいし重い。手で持ったり、外出時に持っていく用途には向かない。

山崎実業 マグネットバスルームタブレットホルダー

AGM PAD P1をお風呂場で固定するために利用している。 立ってシャワーをする時に見やすい位置に固定できるので便利。

MagSafe対応 スマホスタンドアーム

MagSafeでくっつけられるスタンドアーム。ただし充電はできない。 ベッドにつけておくことで、寝ながらiPhoneで動画が見られる。

そして、この商品にはメタルプレートが付属している。

このリングをAGM PAD P1に貼り付けることで、AGM PAD P1もくっつくようになる。

多くのスタンドアームはクリップ式になっているので、タブレットスマホを取り外す時には両手を使ってクリップを広げる必要がある。 だが、このMagSafe方式なら片手でぐっとひねってあげるだけで取り外せるので便利。もちろん外そうとしなければガッチリと固定されている。

これでAGM PAD P1を使って寝室でも風呂場でも快適に動画を楽しむことができるようになった。

山崎実業 隠せる調味料ラック

コンロ横に置ける調味料ラック。多くの調味料ラックは網状になっていてスカスカなので、ラックに置いたすべてのものが油まみれになって地獄になる。一方でこの調味料ラックは、板によって構成されているのでその心配がない。表面も拭き掃除がしやすいし、引き出しの中に入れれば油ハネで汚れることもない。

ただし、結構な大きさで存在感があるので、キッチンによってはすごく狭くなってしまう点に注意。

LED減光シール

最近のガジェットは何でもかんでも青色LEDでビカビカに光りすぎて眩しい。 このシールを貼るとほとんど光らなくなるので便利。

エレコム スマホスタンド ディスプレイ Magsafe

ディスプレイ横にiPhoneをMagSafeで固定できるやつ。

MaciPhoneを利用していると、MacWebカメラとしてiPhoneを利用できる「連携カメラ」という機能がある。

このデバイスiPhoneを固定してあげれば、Meet・Zoom・DiscordなどでiPhoneWebカメラとして利用することができる。もちろん画質も良い。

さらに、使わないときは回転して完全にディスプレイ裏に隠せるので、普段は何もない状態にしつつ、必要な時にiPhoneをセットしてMTGをするということができる。

UGREEN 10Gbps USB-C切替器

『本体だけでなく延長された切り替えスイッチがついているUSB切替器』は長らくエレコムのこの商品くらいしか選択肢がなかった。

USB切替器だけなら様々出てるが「延長された手元用のスイッチ」がついてるものが全然なかったなか、これが新しく発売されていた。

WinとMacで利用しており、USBマイクやStream Deckなどを切り替えて使っている。

伊藤園 健康ミネラルむぎ茶 希釈タイプ

水で薄める麦茶の元。前に書いた記事では缶タイプしか無かったけど、新しくペットボトルタイプが発売された。

fromatom.hatenablog.com

缶と違ってよいのは、コップ一杯だけでも麦茶が作れる点。

グランズレメディ モアビビちゃんの魔法の粉

靴の匂いを消してくれる謎の粉。まじで匂いが消えてすごい。 靴だけでなく足の匂いも消してくれるので最強。

注意点としては粉なので、靴を履いてすぐに脱ぐと床が白くなる。 出かける際に忘れ物があったことを思い出すと終わるので注意が必要。

山善 DCモーター扇風機

DCモーターの扇風機。めっちゃくちゃ弱い風が欲しくて購入。これは夏場に大活躍した。

夏にエアコンをつけて寝る時は、エアコンは弱めにするがそれだとちょっと暑い。 なので扇風機やサーキューレーターを併用することになるが、弱だとしても結構な風があたって気になって眠れなくなってしまう。ので扇風機を消すけどそしたら今度は暑い。

という問題を解決してくれたのがこのDCモーター扇風機。最弱で回すと「これ風出てるのか?」と思う程度に弱い風が出てくれる。活動していると全く感じない風量だが、ベッドで横になって寝ようとすると、確かにふんわりと風を感じる。これが非常に快適で暑い夏を乗り切ることができた。

マーナ Daily ウォーターボトル 500ml

パッキンレスのウォーターボトル。なんとパッキンがない。食洗機にいれる時に蓋を外して、パッキンを取って〜とかせずに、蓋を外せばそのまま洗える。

ロジクール MX ANYWHERE 3S

マウスはMX ANYWHEREが最強。2Sを使っていたけれど、微妙にクリックの反応が悪くなってきたので買い替え。 クリック音がすごい静かになっている。MX ANYWHEREは最強なので、細々した良さは省略。

FUNLOGY Speaker

食卓でiPadで動画を見る時用に購入。セール時には1500円弱で買える激安商品にもかかわらず、そこそこ良いの音が出る。 コンパクトで食卓の邪魔にもならないし、ガジェットガジェットしていないスッキリした見た目も良い。 USB-Aで給電、3.5mm ステレオミニプラグ接続、シンプルでコンパクト、そこそこな音質という「これで良いんだよ」という優等生スピーカー。

当然音にこだわる人には向かないので注意。

SwitchBot CO2センサー

SwitchBotのCO2センサー。前まではキングジムのこれを使っていた。

これも便利ではあったけど、角度調整ができないので数字が見えにくかったり、通知を飛ばしたりできないので気づいたら1200PPMとか、濃厚二酸化炭素空間で生活していたりする。

SwitchBotのCO2センサーであればアプリで通知を飛ばすこともできるし、APIを叩いて自分でSlackに通知を送ることもできるので便利。

Anker Eufy (ユーフィ) Smart Scale P2 Pro

スマート体重計。前まで使っていたWithings Body Cardioはどうにもアプリや連携周りの挙動が難しかった。

自分ひとりなら良いけど、妻も使いたいかつ妻の体重は自分には非公開にするためにアレコレしたが、どうにもこうにもうまく連携できなかった。

Ankerのこれは別々で管理ができるし、なによりサイズがすごい小さくて良い。

キリンガラナ 300mlボトル缶

ガラナはキリンガラナが一番うまい。うまい……が、本州では売ってくれないので北海道から密輸する必要がある。 ペットボトルバージョンもあるけど、この300ml缶ぐらいがちょうど良い。

ニトリルゴム手袋

どこの製品でも良いと思うけど、ニトリルゴム手袋。 いわゆるゴム手袋というのが嫌いで、使ってこなかった。中は蒸れて臭くなるし、物は扱いにくくなるしで避けていた。 が、当然ながら冬になると手が荒れまくってグチャグチャに破滅していた。

そこで購入したのがニトリルゴム手袋(粉なし)。掃除・炊事など、長時間水回りをアレコレする時つけていて、かなり荒れが少なくなった。ハンバーグを作ったり、唐揚げを揉み込んだりと手が油まみれになる時にもこれがあると便利。

地味に便利だったのが段ボール解体時。乾燥した季節に段ボールやボール紙をアレコレしていると、手の油分は吸収されるし、紙で手を切ってしまうこともよくあったが、ニトリルゴム手袋をしていれば安全になった。

使い捨てなので気軽にはめて、ポイポイ捨てられるのも良い。

無印良品 野菜や果物の鮮度を保つポリエチレン袋

www.muji.com

会社の同僚におすすめしてもらった商品。

野菜室で人参がゾンビになったり、白菜・キャベツが死んだりしていたのがかなり改善された。 とにかく野菜はこの袋にぶち込んでから冷蔵庫に入れておくと、かなり日持ちする。

台湾マンゴーアイスティーノンアルコールビール

ビール最強の地、台湾のノンアルコールビール。炭酸マンゴーティージュースではあるけど、麦芽の苦みもあることで甘ったるくならずに非常に美味しい。Amazonで買うと割高なので、運良く輸入品店などで見かけたら買ってみてほしい。

G&G GTP 9マガジンの注入バルブを交換する。

免責

本記事を参考・模倣などして改造したことによる直接的、付随的、結果的、間接的、あるいは懲罰的な損害、経費、損失、または債務について、いかなる責任も負いかねます。

非公式の方法ですので全て自己責任でお願いします。

交換方法

まずは2mmのピンポンチで2本のピンを外します。

外した注入バルブパーツには前後がありますが、このパーツとマガジン本体のG&Gのマークを揃えてあげればよいです。

次に金色の注入バルブパーツを外します。手で外せる場合もありますが、ペンチなどで反時計回りに緩めてあげればよいです。

注入バルブを交換します。今回利用したのはM93R-AG No.107の注入バルブです。自分の場合は加工無しでつけられました。

G&G正規パーツは内側からねじ込む形ですが、非正規パーツは外からねじ込む点に注意してください。

バルブが挿入できたら、もとに戻します。

ウォータープライヤーがあるとピンを押し込みやすいです。

後はガス漏れや破裂したりしないか少しだけガスを入れて試します。

感想

G&G正規の注入バルブでは「1度マガジンを冷やす」という工程を経なければガスが入らなかったのですが、交換することでスムーズにガスが入り吹き戻しもされるようになりました。

GTP 9は見た目がかっこよいのですが、マガジンにガスが入りにくいためにゲームで使うことが少なくなっていました。この改造によってゲームでも使えるようになりました。

PS5とPS4コントローラーのアナログスティックを金属製に交換した。

PS4コントローラー(DUALSHOCK 4)とPS5コントローラー(DualSense)のスティックが加水分解してベトベトになってしまった。

ベットベトでとてもつらい

PS5用プロコンであるDualSense Edgeであれば、スティック自体を交換できるけれど、これらにはそんな機能はついていない。

買い替えも検討したけど、シンプルに高い。DualSenseは1万円以上するし、DUALSHOCK4ですら8000円する。高すぎる。 というわけで、保証が切れたり壊したりすることを承知の上で自分でスティックを交換することにした。

同じようなスティックに交換しても、再度加水分解が発生することが目に見えているので、金属製のスティックを探して購入。

YouTubeで分解の方法を調べてコントローラーを分解して、スティックの交換を行った。爪が何本か折れたけど、まぁ自分用だから良いでしょ。

完成したのはこんな感じ。

このスティックは純正のものより若干高さがあるが、最初は違和感があるけどそこまで致命的ではない。 FPSをコントローラーでやる人ならもうちょっと高くしたいだろうなとは思う。

分解するときには電動の精密ドライバーが役に立った。

利用する電動ドライバーはもっと安いものでも良いけれど、トルク調整ができるものをオススメする。

今のところ快適ではあるけど、今度はメッキが剥げるとかあるのかなぁと考えている。

iOSDC Japan 2024に参加してきました。

今年もiOSDC Japanの季節がきた

参加してきました。

iosdc.jp

今年は気軽な参加

6年連続で登壇し続けていましたが、今年は残念ながら採択されなかったのでのんびり参加になりました。

登壇は楽しいのですが、どうしても自分の発表が終わるまで心が落ち着かず、他の人のセッションを聞いたりブースを回ってるときも、心がふわふわし続けてしまいます。

今年はそれがなかったので、とても気楽にセッション・ブース・コンテンツを楽しむことができて、良かったです。

スポンサーブースビンゴ

今年のiOSDCではスポンサーブースビンゴという企画が新しく始まりました。 スポンサーブースを巡ってスタンプを集めると、1列でたこ焼きチケット、2列でクレープチケットが貰えて、実際に会場に来ているキッチンカーで引き換えられるというものです。 さらに、すべてのスタンプを埋めるとiOSDCオリジナルノベルティまでもらえるという豪華さ!

特にオリジナルノベルティのパーカーはスポーツ用に最適な生地の、ちょっといいやつが用意されていたので「これはゲットせねば」と頑張りました。

たこ焼きとクレープも「食べたいし、ちょっと頑張ろうかな」と思えるちょいど良い景品でしたし、スポンサーブースで声をかけるのが苦手な自分でも「スタンプくださいー」からコミュニケーションを始めることができて、とてもいい企画だと思いました。

惜しむらくはたこ焼きもクレープも爆裂に人気すぎて、手に入れるタイミングが無かったことです。実行委員会さんも「みんな食べてくれるのかな?」と心配していたらしいので、今年の大盛況ぶりを鑑みてきっと来年は増えてるはず!楽しみですー。

Swiftコードバトル

今年の新しい企画としてSwiftコードバトルが企画されました。

blog.iosdc.jp

Swiftはスクリプト言語などと比較して、魔術によってコードゴルフをするのが難しい言語だと思います。 しかし、そんな中でも出場者の方々は様々な手法で短いコードを生成しており、非常にレベルが高い戦いを見ることができました。

コードバトル開催時間中は会場でアルコールも提供開始されていたこともあり、ビールを飲みながら野球を眺める感覚で観戦ができたのもとても楽しかったです。 会場前方でプロSwiftエンジニアからのコメントが入るのも楽しかったですね。

また問題作成者の方からは「ChatGPTなどで一発で最短に思われる解にたどり着かないことを確認した」旨の話があり、しっかりとした作問がされていてすごいなぁと感心していました。考えるの難しそう。

今年のラーメン

なんだか毎年恒例になってきました。会場にはキッチンカーやたこ焼き・クレープも来ていたのですが、せっかくラーメン激戦区である高田馬場まで行くのであれば、いくつか回りたい店舗もあるので、今年もラーメン屋めぐりをしました。

どの店舗も美味しかったので、高田馬場に訪れた際や来年のiOSDCでぜひ。

鶏そば 三歩一

龍の羽 時色

らぁ麺やまぐち

破壊的イノベーション

博多ラーメン でぶちゃん 高田馬場本店

まとめ

今年も楽しいiOSDC Japanを楽しむことができました。毎度のことですが、実行委員会の皆さまには感謝の念しかありませんね。 登壇者の皆さま、コードバトラーの皆さま、スポンサーブースの皆さま、参加者の皆さまもお疲れ様でした!

それでは皆様、また来年お会いしましょう。