5.1さらうどん

@giginetの技術ブログ。ゲーム開発、iOS開発、その他いろいろ

WWDC24に参加してきた

WWDC24に行った

今年は運良く当選したので、WWDC24に参加してきた。17, 19以来5年ぶり3回目の出場(23も会場には入れなかったが、現地にはいた)

著者近影

23年は仕事で現地まで行ったが、会場には入れず、参加者の様子を見聞きしただけで厳密な比較はできないけど、今年は昨年に比べて参加者がかなり増え、日本からの渡航者も増えていた印象だった。

COVID以降初の参加だったが、今年は大分COVID前のコンベンションセンター時代の盛り上がりが戻っていたように感じた。

0日目:registration

初日はInfinite Loop(旧Apple本社)にてregistration。バッヂを受け取って終わりかと思ったら、そのままビールが提供されたり、参加者と懇親できるようになっていた。COVID前のWWDCを想起させるノリだった。

ビールを片手にご機嫌の著者

公式のコンテンツはないが、知り合いや初対面の方含め、いろんな人と雑談する。

夜はパーティーで紹介してもらった韓国から来たDeveloper集団の宿にお邪魔して交流したりとわいわいしていた。

1日目:Keynote + Special Event

現地時間の月曜はKeynote、朝6時半前には現着してApple Park前で待機。かなり早く行ったけど、8時前ぐらいまで入場はできずに、列の形成もファジーな感じだった。ここまで張りきらなくても結果的には前の方に座れたので、7時半ぐらいに行くので十分かもしれない。 (とはいえコンベンションセンター時代には徹夜組もいたので、本当に毎年いつから並べば良いか読めない)

8時過ぎたらPark内に入場できて、朝食などが振る舞われた。 期間中は事あるごとに食事が提供されたが、どれも美味しくて驚く。以前のWWDCは雑なランチボックスが5日間提供され続けていて、かなり食傷していたけど、とても食事の質が上がっていた。しかも参加費が無料だからビックリ。

Keynote前には朝食も用意されてメッチャ美味かった

ようやっとKeynote。発表内容は他の記事に譲るけど、やはり毎年とてもプラットフォームの進化にワクワクする。茶番のクオリティも年々上がっていて楽しませてくれる。

Tim CookとFederighiが出てきてテンション上がる

その後、後述のLabに行ったり、Xcode 16を試したりしていた。

同僚の@___freddi___ と仕事中

今回、Swiftのメジャーバージョンがあることは自明であり、開発中のアプリのビルドがちゃんと通るかを心配していたが、事前の予想に反してSwift 5モードの互換がかなりしっかりしており、少しの変更で開発環境を作ることができた。終わった後のビールが美味い。

電源、ネットワークのブースが屋外で、炎天下の中作業していたけど、熱中症のリスクがあるので気をつけた方が良い。

In Person Lab

今年の特徴として、わずか3時間ほどではあったが、COVID前のIn Person Labが部分的に復活していたのは良かった。ビルド+Xcode Labがあったので、予め用意していたld(linker)周りの質問を持って行った。

In-Person Labでビルドについてディスカッションした

In Person Lab、やはりOnline Labと比べて体験が段違い。対面であることのコミュニケーションのしやすさもそうだが、その場で操作を変わってくれたり、ホワイトボードでディスカッションできたりと、Online Labで同等の体験は難しい。

また、Online Labではミーティングの参加者しか対応してくれないが、In Person Labでは、わからないところは周りのエンジニアを呼んできてくれて教えてくれるのも大きい。

最終的に屈強なビルドエンジニアが4人も集まってきて見てくれた

ここでわいわいすることこそがWWDCの真骨頂だと思うので、来年はぜひ日数を伸ばしてほしい。

2日目:Special Session

2日目火曜日はわずかに1時間ほどのセッションがあるのみ。昨年は「Apple Vision Proのアプリ作ってね〜」という基調講演的な内容だったと聞いていて、今年はApple Intelligence(以下AI)関連かな、と思っていたが、案の定そんな感じだった。

セッション自体は撮影や録画禁止だったので、中身の記録はないが、会場は小規模なホールで、登壇者が生で話してくれるのは体験が良かった。

Special Session、小規模なホールで開催された

撮影禁止とは言え、コンフィデンシャルな話がされていたわけではなく、AIに対応していくための既存のセッションの内容の再紹介といった感じで、気になる方はIntent周りのセッションを見ると、大体内容が把握できると思う。終盤はApple Vision ProでのARや、AIを使った体験設計の話で、セッションにはない内容だった。

技術的な話は、要約すると「App Intent作ってほしい〜」と言った話だった。App Intent自体はSiriやSpotlight、Widgetsなどを連携するために以前からiOSに存在するが、AIの登場により「通貨」のような側面が強くなってきたなと感じた。今後数年のAIの普及や、将来的なApp Intentの用途の拡大を見越すと、徐々にIntentを充実させる投資を行っていかないと、将来的なアプリの進化が行き詰まってしまうかもしれない。

他にもAssistant Schemaの解説があった。

Integrating your app with Siri and Apple Intelligence | Apple Developer Documentation

Assistant Schemaはアプリ上の手続きをSiriやAIが解釈可能な形で提供するためのブリッジの役割を果たす仕組みだ。 Intelligenceというと、なにか抽象的なインプットで、魔法のように全て良い感じにしてくれるものを想像してしまうが、実際は開発者が泥臭く手続きを記述するものなんだなという印象だった。開発者が泣くことでエンドユーザーが魔法のような体験を得ることができている。

開催期間中

開催期間中は、日中はAirbnbで仕事をしつつも、今年は日本人も多く、日々様々なミートアップが開かれていたので、毎日パーティーピープルをしていた。

到着日に開かれた日本人nomikaiや、世界中の参加者とのMeetup、Swiftコミュニティによる10周年イベントと暇をする時間がなかった。

Swift 10周年おめでとう〜

昨年は週末までいたが、後半時間を持て余したので、今年は木曜昼には現地を離れた。そのため、Online Labやセッション動画の鑑賞、新技術の検証がほとんどできなかったのが心残り。 例年に比べて全体的に薄味な印象だったのでなんとかなった。

今年の総評

正直、昨年の新プラットフォーム(Apple Vision Pro)の発表が大きすぎて、今年はそれに比べると大きな発表はなかったと思う。

しかし、例年の傾向から大きな発表は数年に一度なので「今年は繋ぎの年っぽいなあ」という察しは事前にあって、良くも悪くも予想の範疇ではあった。

Swift AssistやApple Intelligenceも現段階では触れるところが限られているし、OS機能の強化も順当なものであった。 開発環境もSwift 6が出ることや、言語の新機能は、ほとんどオープンソースコミュニティで発表や議論が尽くされていたし、Xcodeの変化も目新しいものはなかった。

WWDCの前から、生成AIの普及とデバイスの高性能化に伴って、クラウドからローカルAIへの再評価が始まりつつあるな、と感じていたが、Apple Intelligenceの発表によりそのトレンドが強固なものになったのを感じた。 この領域は、ネイティブエンジニアに利があると思うし、ネイティブに投資していくのはまだまだ将来性が高いのではないかと考えている。

Apple Intelligenceは現時点では、A17 Proを始めとする高性能端末でしか動作せず、アーリーアダプター向けの機能ではあるけど、今後2〜3年ほどで普及機にも浸透し、生活に欠かせないものになっていく未来が見える。 その時に備えて今からどんどん投資していきたい。

Apple Vision Pro

今回、ついに日本発売が決まったので、帰りの飛行機で速攻注文した。(¥784,200 💸)

レンズの購入などが煩わしく、現地では敢えて購入しなかったので楽しみに発売を待とうと思う。

高い買い物なので、せっかくだから何か開発しようかなあ。

Swift MacroをSwift PackageなしでXcodeで扱う

Swift MacroってXcodeから使えないの?

Swift 5.9からSwift Macroが実用段階になったが、WWDCの動画でも、公式ドキュメントでもSwift Packageから作成することが前提となっている。

targets: [
    // Macro implementation that performs the source transformations.
    .macro(
        name: "MyProjectMacros",
        dependencies: [
            .product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
            .product(name: "SwiftCompilerPlugin", package: "swift-syntax")
        ]
    ),


    // Library that exposes a macro as part of its API.
    .target(name: "MyProject", dependencies: ["MyProjectMacros"]),
]

Xcodeで作成したtargetに組み込むときもパッケージの依存を組み込み、マクロフレームワークをimportするだけで利用可能になるようだ。

しかしXcodeGUIではMacroを含むモジュールに依存関係を設定する方法はない。通常の依存関係と異なり、Macroはビルドdestinationが特殊なので、これを内部的にどうやって設定しているのか気になった。 (例えばiOSアプリにMacroを組み込むとき、アプリケーション自体はiOS向けにビルドするが、Macroは常にmacOSで実行可能な形でビルドされなくてはならない)

XcodeやxcodebuildのレイヤーではMacroを依存として指定する方法がないのだが、swiftcのレイヤーでは何らかの方法でmacroへの依存関係を指定しているはずだ。

-load-plugin-executable

調べてみたところ、どうやら -load-plugin-executable というオプションがswiftcに生えていた。

この辺の記事にほとんど書いていた。

$ swiftc --help
  -load-plugin-executable <path>#<module-names>
                          Path to an executable compiler plugins and providing module names such as macros

この -load-plugin-executable でmacroターゲットのバイナリを指定すると良いらしい。Xcodeでは OTHER_SWIFT_FLAGS にこれを指定する。

実際に、適当なMacroを含むパッケージをXcodeプロジェクトに追加し、利用してみたときのログを見ていると、同じフラグを渡していることが確認できる。

Special Thanks : mtj0928/userinfo-representable

Swift MacroをSwift PackageなしでXcodeだけで作る

そして、調べてみると、SwiftPMにおける.macro ターゲットは、実は単なるmacOS向けの executableTarget とほぼ同等なものであることがわかった。

-load-plugin-executable で要求されるバイナリは、実は単に CompilerPlugin に適合した型をエントリーポイントとしたexecutableを要求しているだけのようだ。 あとでswiftc側でどういうバイナリを想定しているかの内部実装も読んでみたい。

ここまでわかると、Swift Packageを使わずとも、Xcodeだけでマクロを捏造できるのでは?と思って試してみた。

Macroターゲット側 (MyMacro)

  1. XcodemacOSのCommand Line Toolターゲットを作る
  2. swift-syntaxをSwift Packageとして追加して依存関係にする
  3. 普通にmacroを書く

ここで実装するマクロはなんでもよいので、公式ドキュメントのサンプルにある fourCharacterCode の実装を使うことにした。

重要なのは、executableのエントリーポイントとして、CompilerPluginを指定することだ。

import SwiftCompilerPlugin

@main
struct MyMacro: CompilerPlugin {
    var providingMacros: [Macro.Type] = [FourCharacterCode.self]
}

アプリターゲット側 (MyApp)

  1. Build Phaseのdependenciesに MyMacro を追加する
    • 単にビルド前にexecutableを作っておきたいだけ
  2. OTHER_SWIFT_FLAGS-load-plugin-executable $BUILT_PRODUCTS_DIR/../$CONFIGURATION/MyMacro#MyMacroを指定
    • これは BUILT_PRODUCTS_DIR に入っている MyMacro の成果物を指定している
    • 単に $BUILT_PRODUCTS_DIR だとアプリ側のdestinationになってしまうので ../$CONFIGURATIONmacOS側のdestinationにする
    • この指定は特殊で、 swiftc --helpによると、<executable_file_path>#<target_name> らしい
  3. シンボルの解決のために#externalMacroを仮宣言する
    • これをすることで、依存解決時はMacroが存在する体で扱い、実際にビルドするときに2で指定したバイナリを実行する

@freestanding(expression)
macro fourCharacterCode<T>(for value: T) -> UInt32? = #externalMacro(module: "MyMacro", type: "FourCharacterCode")

こうすることで、自作のMacroをXcodeターゲットだけで実装することができた 🎉

Swift Macroのビルドキャッシュ

では、このような複雑な構造を取って一体どんなメリットがあるかというと、ビルドキャッシュの利用という面でXcodeターゲットで管理していた方が扱いやすい面があるのではないかなと思う。

現状、Swift Macroはswift-syntaxへの依存が必須で、どの方法で作ってもswift-syntaxを依存として持つ必要がある。swift-syntaxはデカいので、単純なマクロを1つ作っただけで、ビルド時間が激増してしまうという問題があった。

僕の開発しているScipioなどで、swift-syntaxをprebuiltなXCFrameworkとして維持しておくことができるので、Xcodeで作成したMacroターゲットがprebuilt版のswift-syntaxに依存するようにしておけば、ビルド時間を大きく改善できそう。

また、Xcodeを使わないにしても、swift buildで作成したexecutableをキャッシュしておいて、-load-plugin-executableで利用することで、毎回swift-syntaxやmacroをビルドする必要がなくなる(アーキテクチャの問題はあるが)

ScipioはSwift PackageからXCFrameworkを生成するためのビルドツールだが、現在、macroを使ったターゲットからフレームワークを作ることができないという問題がある。理論的には今回紹介した方法で対応できそう。(先にmacroターゲットをexecutableとしてビルドして、framework側からビルドフラグを立てる)

いかがでしたか

業務上で調査して面白かったのでちょっと記事を書いてみた。ここ数年全く技術記事を書いていないという危機感があり今年はこれぐらいの技術記事を定期的に書いていきたいと思っていたのでちょうど良い。 気合入れると続かないので30分以内でガッと書けるやつを目指していきたい。

純粋な技術記事としては8年ぶりの投稿かも・・・・・・

ポケモンガチ対戦用パーティー管理アプリ『PokeBox』をリリースしました

まもなく『ポケットモンスター スカーレット・バイオレット』が発売しますが、先日、iPhone/iPad/Mac向けのポケモンパーティー構築管理アプリ、『PokeBox』をリリースしました。

PokeBox - 構築管理ソード・シールド

PokeBox - 構築管理ソード・シールド

  • gigi-net.net
  • ユーティリティ
  • 無料

ポケモン剣盾』以来、ランクマッチなど、いわゆるポケモンガチ対戦にハマり、自分の需要から開発したアプリです。

『剣盾』環境では、100体近いポケモンを育成し、努力値振りや構築意図などを、秘伝のスプレッドシートで管理していましたが、あまりにも煩雑なので、ちゃんと管理できるアプリを作ろうと思いつつ、この度ようやっと開発できました。

ひでんのExcel

今のところ『剣盾』での利用を想定しています。今更『剣盾』を遊んでいる人は面構えが違う廃人ばかりだと思いますが『SV』のリリース後も末永く使っていただけると幸いです。(SVでシステムが大きく変わらないことを願うばかり!)

ご意見、ご感想、機能要望はTwitterにてお待ちしてます。

特に耐久調整などあまり考えて構築できてないので、本当のガチ勢の方のユースケースなどぜひ聞きたいです。

主な機能

ポケモンエディタ

  • ゲームの表示に近いレーダーチャート
  • わざ、とくせいを管理、フレーバーテキストの閲覧
  • きそポイント(努力値)振り、せいかく補正エディタ
  • ASぶっぱ、CSぶっぱなどよく使う調整の入力支援
  • メモ

パーティーエディタ

  • 作成したポケモンをパーティーに登録
  • もちもの管理
  • 1匹ごとに個別メモが可能

OS標準の機能

  • iPad対応(要iPadOS 16以上)
  • Mac対応(要macOS Ventura以上とM1 Mac)
  • 日本語、英語サポート
  • ダークモードサポート
  • Dynamic Typeサポート
  • iCloudサポート
    • お手持ちの全てのiPhone, iPad, Macでデータを同期できます

今後の機能追加予定

技術的な話

このプロジェクト自体、技術的なキャッチアップの目的も大きかったため、語りたいこともたくさんあるのですが、この記事はリリース告知に留め、技術的な注目トピックをざっくりと紹介。

詳しい話はまた他所の勉強会やカンファレンスでできればなと思っています。

ポケモンデータの構造化

この手のアプリで一番頭を悩ますのが、ゲーム内のデータをどのように構造化するかという点です。

既存の構造化されたポケモンデータとして、PokéAPIというデータソースがあります。これは、ポケモンの様々な情報を取得できるWebAPIです。GraphQLなどにも対応しています。

このPokéAPIオープンソースで開発されていて、実はデータソースもGitHubにあります。

このリポジトリには、大量のCSVと、それらをSQLiteにdumpするPythonスクリプトがあるのですが、長らくメンテされていなかったため、いろいろPRを投げて修正しました。 SQLAlchemyのバージョンを上げたりDocker上でのdumpをGitHub Action対応したりなどです。

ここまで来ると、あとは全データをRDBとして扱えます。

PokeBoxでは、ここから生成したSQLiteをアプリ内に保持し、SQLを投げています。アプリ容量が大きめなのはそのためです。

クソデカSQL。開発にはJetBrains DataGripが役立ちました。

『SV』発売後に、この情報ソースが迅速に新仕様に対応できるかは懸念があります。この辺を自給自足できれば良いのですが、現状難しいため、発売後は別のソースも検討しないとならないかもしれません。

また、わざの習得テーブルなど、一部怪しいデータも散見されました。どうぐの細かなパラメータなども、6世代付近の情報で止まっていそうです。

SwiftUI on iOS 16

今回の裏テーマとして「SwiftUIを使って、Appleの推奨するモダンアプリケーションを正しく作る」というところを目指しました。 全てがSwiftUIでできていますし、UIは標準のメールやメモアプリを参考にしています。

業務ではあまりアプリケーションレイヤーを触らないため、長年、SwiftUIについてしっかりキャッチアップできていないという課題感を感じていました。(エアプ)

今回、1本ちゃんと作って、さまざまなハマりを経験したことで、かなり知識のアップデートができたと考えています。 この手のアプリの中では、かなり使い勝手の良いものが作れたのではと。

PokeBoxは、7月頃に開発を始めたため、WWDC 2022で登場した、iOS 16/iPad OS 16の新機能をふんだんに使っています。

What's new in SwiftUI - WWDC22 - Videos - Apple Developer

Layout

エディタ画面のレーダーチャートの実現のため、新しいCompositional Layoutの仕組みを使っています。

Compose custom layouts with SwiftUI - WWDC22 - Videos - Apple Developer

Pathレンダリングしたグラフに、ZStack で文字を重ね、Layout でグラフ上に配置しています。 おかげで任意のサイズで描画できるレーダーチャートが実装できました。

当初はSwift Chartsの採用も考えましたが、試してみたところ、時系列グラフを前提として設計されていて、レーダーチャートの描画には向かなかったので早々に諦めました。

ViewThatFits

ViewThatFitsiOS 16から登場した新しいViewで、複数の候補のうち、収まりが良いものを自動的に採用してくれる仕組みです。PokeBoxでは、わざの表示に採用しています。

文字数が少ない場合はグリッド表示、多い場合はリスト表示に自動的に切り替わります。これによりiPadで表示しても収まりが良いものとなりました。

Grid/GridRow

Grid は、表組みのようなものを作る際に、最長の要素の幅に合わせて、自動的にレイアウトしてくれる仕組みです。

PokeBoxでは努力値エディタなどに利用しています。各パラメータについて、ラベルやスライダーの開始位置が揃っていることがわかるかと思います。

NavigationSplitView は新しいNavigationStackの一種で、これ1つで全プラットフォームに適したナビゲーションを構築できます。

iPadではSplitViewとなり、iPhoneやwatchOSでは、自動的に NavigationStack のような挙動をします。

これのおかげでワンコードでサイドバー付きのiPad対応が実現しています。

素晴らしい仕組みのように見えて、iPhoneで正しく動かすには、バグが多く悩まされました・・・・・・。β版どころか、16.0になってもバグってるので注意です。

navigationDestination modifierを使うことで、NavigationLink の遷移先を従来より管理しやすくなりました。

PokeBoxではほとんどの画面遷移に用いています。

The SwiftUI cookbook for navigation - WWDC22 - Videos - Apple Developer

Variants Preview

Xcode 14から、Xcode PreviewをダークモードやDynamic Typeを適応した状態でまとめて閲覧できるようになりました。(なぜ今までなかった)

PokeBoxでは、随所でPreviewを活用してUIを作成していました。またタイプ表示といった、小さなコンポーネントもSwiftUI化できました。

こういったものを作るには非常に向いていますね。

最新の開発機能の活用

iCloudサポート

複数のiOS端末を日常から使っているため、個人的な要求として、デバイス間のデータの同期は外せない仕様でした。

今回はシンプルにデータストレージにはCoreDataを採用。CoreDataは NSPersistentCloudKitContainer を使うだけで、簡単にマルチデバイス対応することができました。

Using Core Data With CloudKit - WWDC19 - Videos - Apple Developer

CoreDataやNSManagedObjectAPIは、今の時代には辛かったので、なんとかして欲しいところ・・・・・・。

ローカライズ

i18nも対応しています。実は開発言語は英語で、あとから日本語ローカライズを追加しています。

先ほど紹介したデータベースでは、ポケモンの名前やフレーバーテキストは言語毎に格納されているため、発行するSQLで言語IDを変更することで、簡単にローカライズが実現できました。

英語設定だとこの通り。

StoreKit 2

有料機能のアンロックにはStoreKit 2を利用しています。StoreKit 1は業務で利用していたのですが、2は今回初。あまりの簡単さに涙が止まらなかった・・・・・・。

Meet StoreKit 2 - WWDC21 - Videos - Apple Developer

StoreKit ConfigurationやStoreKit Testingは今回利用していませんが、そのうち試してみたい。

Xcode Cloud

先日からXcode Cloudが利用可能になったので、ユニットテストの実行や、TestFlightの配信に取り込んでみました。

今回のような、プライベートリポジトリでの小規模個人開発にはまさにこれしかないソリューションに感じました。この規模のアプリであれば、pushごとに毎回ユニットテストを回しても無料枠で使えています。

ダンプしたSQLiteの置き場所にGit LFSが必要になり、GitHubへの課金が発生したのはまた別の話。

サブミット自動化

一方で、ストア提出に関してはまだまだfastlaneなど、旧来の手法に分があるように感じました。

metadataの提出が現状、Xcode Cloudだけではできないので、この辺を解決する需要はありそう。

今回はお家芸のdeliverを用いたサブミット自動化*1のほか、snapshotを使ったスクリーンショット撮影自動化にも挑戦しています。

iPadOS/iOS両対応、日英二カ国語対応程度の規模でも、40枚(4枚 * 5機種 * 2カ国語)の撮影が要求されるので、自動化しないとやってられません。

この辺の基盤作りをしっかりしたので、今後の柔軟なバージョンアップにも耐えうると思います。

ちなみに、このアプリ開発のために、iPhone 14 Proサポートを取り込むコミッター業もしました。個人アプリ開発のためにOSSの開発も進むのは良い相乗効果だなと思います。

ぜひダウンロードしてね

手短に書こうと思いつつ、長くなりましたが、良い感じのアプリが作れて大変勉強になりました。 普段UI層をあまり書かないアプリエンジニアなので、「UI作れる人すげーな」という気分。

ポケモンプレイヤーはもちろん、そうじゃない人が触っても楽しいアプリになったと思います。

どうぞご利用ください。

PokeBox - 構築管理ソード・シールド

PokeBox - 構築管理ソード・シールド

  • gigi-net.net
  • ユーティリティ
  • 無料

*1:筆者はfastlaneコミッターです