😄

try! Swift Tokyo 2024に参加しました!

2024/03/23に公開
3

try! Swift Tokyo 2024に参加してきました!
実は、まだ学生でSwiftを書き始めたか始めてないかだった2020年に参加しようとしたことはあったんですが、covidで中止になってしまったので4年越しに参加することができました!ヤッター!

色んな人とコミュニケーションが取れて楽しかったです😄
英語話せないのに果敢に話しかけて玉砕したり…前職の同僚とランチしたり…Swift/iOSコミュニティでよくご一緒する方々と話せたり…現職の同僚とコードチャレンジに取り組んだり…🕺

以下、感想(思い出?w)を徒然と書き綴ります。

トーク

day1

全部のトークそれぞれ面白かったですが、全部のセッションは書ききれないので特に好きだったセッションの感想です。

Opening

Swift Punk

Ask the speaker 周辺で見かけた際も輝いていたのが面白かったです。

(追記)
day2でSwiftPunkのステッカーもらいました!
SwiftPunkのステッカー

残念ながらSwiftPunkは解散してしまうそうですが、来年また再結成されることを願っています。
メンバー(MC)のTimさんと話した時に、try! Swiftのちょっと前にやろうとなったけど3台もあるかな〜と思ったらあったと話していて、ロックな感じしましたw

Cultivating a Sense for Designing Great Applications

usagimaruさんのセッションです。

自分は割とプラットフォームが提供するものが正だと思ってきたので、型破りなデザインの話のところは衝撃がありました。
最近、雑談で「ベストプラクティスがあると似たり寄ったりなデザインになってしまうから、アプリにあったデザインが作られるべき」のようなことを聞いて、正直私は(とはいえプラットフォームで操作感が統一されてないとユーザは混乱するんじゃないかなぁ・・・)と思っていたのですが、このモヤモヤはプラットフォームのデザイン思想に沿っているかどうか、がキーだったんだな!と、とてもスッキリしました。
Pull to Refreshがクライアント発だというのも知らなかったので驚きました! これもプラットフォームのデザイン思想と型破りなデザインが噛み合ったから生き残ったものなのか〜と納得です。

御朱印帳モチーフのアプリも興味深かったです。
文化に目を向ける、良い話だな〜と感じました。

AIによる言語学習の変革:DuolingoのAIチューターを深掘る

Xingyu Wangさんのセッションです。

自分でもChatGPTとか使って何か作れないかな?と思うくらい実装方法がイメージしやすくて面白かったです。
非常〜に重いバックエンドとの通信の際のテクニックの話で、基本的な遅延対策以外にも、レスポンスが欲しいタイミングに間に合わなければデフォルト値を返すなどのOS側での工夫が共有され、本当に大変そうだな...と思いましたw
自分自身はそこまで重い通信が必要な場面に遭遇したことはないですが、いつか通信が重くて困ったら見返したいセッションでした。

(追記)
後述のday2のAfter Partyでもっと英語喋れたら良かったのに🥺 と辛酸を舐めたのでDuolingoデビューしました。
Partyの帰り際に一言だけXingyuさんと話せたんですが、セッション良かった!しか言えなかったので、もうちょっとどこがどう良かったのか伝えられたら良かったなぁと思います。

SF Symbolsの芸術的世界:限りない可能性を解き放つ

Lil OssaさんのLTです。
正直、try! Swiftで一番楽しみにしていました。結果、超面白かったです!
英語でしたが、これはノリが大事...!と直感し、早々に翻訳機を取りました。なので、正確に内容が聞けているかどうかは微妙な部分はありますが、それでも面白くてすごかったです。

私自身、Webを中心に開発していた頃にCSSでイラストを描画することに熱を上げていた時期があり、ドロイドくんをCSSで描いて動かすなどしていたので、非常に親近感の湧く内容でした!

というわけで早速私もtry!してみました!!!!
Ryomm cat
割と近い? 気づいたらかなり熱中してましたw

めちゃ長いんですが、コードです↓ playgroundで遊べて楽しい!!

playgroundで動くコード
import SwiftUI
import PlaygroundSupport

struct SFSymbolsArt: View {
    var body: some View {
        ZStack {
            // face base
            Image(systemName: "circle.fill")
                .resizable()
                .foregroundColor(.orange)
                .fontWeight(.bold)
                .frame(width: 250, height: 200)
                .zIndex(1)
            
            // ear
            Image(systemName: "drop.fill")
                .resizable()
                .foregroundColor(.orange)
                .frame(width: 90, height: 100)
                .offset(x: 20,y: -100)
                .rotationEffect(.degrees(30))
                .zIndex(1)
            Image(systemName: "drop.fill")
                .resizable()
                .foregroundColor(.orange)
                .frame(width: 90, height: 100)
                .offset(x: -20,y: -100)
                .rotationEffect(.degrees(-30))
                .zIndex(1)
            
            // hige
            Image(systemName: "horn.blast")
                .resizable()
                .foregroundColor(.orange)
                .fontWeight(.bold)
                .aspectRatio(contentMode: .fit)
                .frame(width: 150, height: 150)
                .rotationEffect(.degrees(25))
                .offset(x: 60, y: 20)
                .zIndex(1)
            Image(systemName: "horn.blast")
                .resizable()
                .foregroundColor(.orange)
                .fontWeight(.bold)
                .aspectRatio(contentMode: .fit)
                .frame(width: 150, height: 150)
                .rotationEffect(.degrees(155))
                .offset(x: -70, y: 20)
                .zIndex(1)
            
            // left eye
            Group {
                // base
                Image(systemName: "circle.fill")
                    .resizable()
                    .foregroundColor(.white)
                    .fontWeight(.bold)
                    .frame(width: 110, height: 110)
                    .zIndex(2)
                // up side
                Image(systemName: "rectangle.fill")
                    .resizable()
                    .foregroundColor(.orange)
                    .fontWeight(.bold)
                    .frame(width: 120, height: 54)
                    .offset(x: 0, y: -28)
                    .zIndex(3)
                // eye circle
                Image(systemName: "circle")
                    .resizable()
                    .foregroundColor(.orange)
                    .fontWeight(.bold)
                    .frame(width: 60, height: 60)
                    .offset(y: 5)
                    .zIndex(4)
            }
            .rotationEffect(.degrees(20))
            .offset(x: -50)
            
            // right eye
            Group {
                // base
                Image(systemName: "circle.fill")
                    .resizable()
                    .foregroundColor(.white)
                    .fontWeight(.bold)
                    .frame(width: 110, height: 110)
                    .zIndex(2)
                Image(systemName: "rectangle.fill")
                    .resizable()
                    .foregroundColor(.orange)
                    .fontWeight(.bold)
                    .frame(width: 120, height: 54)
                    .offset(x: 0, y: -28)
                    .zIndex(3)
                // eye circle
                Image(systemName: "circle")
                    .resizable()
                    .foregroundColor(.orange)
                    .fontWeight(.bold)
                    .frame(width: 60, height: 60)
                    .offset(y: 5)
                    .zIndex(4)
            }
            .rotationEffect(.degrees(-20))
            .offset(x: 50)
            
            // eye brow
            Group {
                Image(systemName: "surfboard.fill")
                    .resizable()
                    .foregroundColor(.white)
                    .frame(width: 30, height: 30)
                Image(systemName: "rectangle.fill")
                    .resizable()
                    .foregroundColor(.white)
                    .frame(width: 36, height: 3)
                    .rotationEffect(.degrees(-45))
            }
            .rotationEffect(.degrees(15))
            .offset(x: 30, y: -30)
            .zIndex(4)
            
            Group {
                Image(systemName: "surfboard.fill")
                    .resizable()
                    .foregroundColor(.white)
                    .frame(width: 30, height: 30)
                Image(systemName: "rectangle.fill")
                    .resizable()
                    .foregroundColor(.white)
                    .frame(width: 36, height: 3)
                    .rotationEffect(.degrees(-45))
            }
            .rotationEffect(.degrees(75))
            .offset(x: -30, y: -30)
            .zIndex(4)
        }
        .frame(width: 300, height: 300)
    }
}

PlaygroundPage.current.setLiveView(SFSymbolsArt())

Ryomm cat with visionPro

Build your next website with Swift

Paul Hudsonさんのセッションです。

コーヒー列に並んでいて序盤を聞き逃したので、なぜSwiftでHTMLを書こうとしているのかというモチベーションが分からず...だったのであまりしっかりとした感想は書けないのですが、いやだったらなぜピックアップしたと思われるかもしれないんですが、本当にトークが良かったんです!
翻訳機をつけなくても聞ける!内容がわかる!トークがうまいって言語の壁を越えるのかと衝撃的でした!

恥ずかしながら英語アレルギーでHacking with Swiftを見たことがなく存じ上げていなかったのですが、すごく分かりやすくて動画も見てみようと思いました。
廊下やAfterPartyで気さくに話かけてくださって、私の拙い英語も根気よく聞いてくれたので、すごく分かりやすくて良かったことを伝えられて良かったです!

Macro testing

Point-Freeのお二人のセッションです。
https://github.com/pointfreeco/swift-macro-testing の話。

まだ自分はマクロを自作したことがないので、そうなんだ〜という受け身な聞き方にはなりましたが、従来の assertMacroExpansion() で比較するやり方では各所で手作業入力・コピペが必要となり面倒なため、ライブラリを作ったよ、というような話でした。
ライブでデモがあったのですが、expandSourceにマクロのコードをもう一度書くのが大変だから空文字を渡しておいてわざとFailureにさせて失敗したコードをコピペする!と言っていて、Point-Freeさんほどの大御所もコピペとかするんだ〜と変なところで感動してましたw

swift-macro-testingのデモを見ていると、結構swift-snapshot-testingと似た使い心地っぽいな〜と思いました。
record: true にするとDiagnostics(Failureの時のエラー文と比較するもの)を自動で作ってくれる、と、いやSnapshotTest的だな〜やっぱ作者同じなんだな〜と思いました

day2

全体的にちょっとアカデミックな?入り込んだ内容が多かった印象です。

TextKitの理解を深めよう

Marcin Krzyzanowskiさんのセッションです。

通訳さんがすごく頑張っていました。あと細かくは分からないけどジョークがすごかったw
私は雰囲気でTextKitをやっている...!だったのでそうなんだ😮となるところが多く、たくさん学びを得ました。
関連するドキュメントをいくつか挙げてくれていたので、見てみようと思います

Accessibility APIを使ってアプリケーションを拡張する

kishikawa katsumiさんのセッションです。

こちらはすごく創作欲?開発欲?みたいなものが刺激されるセッションでした! アクセシビリティAPIに限らず、数々のAPIを組み合わせてmac上で動く便利な機能を開発する例なども出されていて、自分も何か作ってみたくなりました。
こんなにアプリにとらわれずに動作するものを作れると知らなかったので、新たな扉が開かれたような心地がしました。
自分だったらどんなものを作ろうかなぁ...。

Swiftの型推論を学ぼう

omochimetaruさんのセッションです。

非常にためになるセッションでした!
正直タイムテーブルを見た時点では、自分には難しそうなテーマだなぁと思い、話に着いていけるか不安でしたが、ギリギリ理解が追いつくスピードで話が展開されていき、完全に理解した気持ちになりました!
数学の問題を解いてる時のようなドーパミンが出てた気がしますw

このブログを書きながらタイムテーブルも見返しているのですが、

これにより、コードのコンパイルが重すぎてエラーが出てしまう理由がわかるでしょう。

という記述があり、理由わかりました!!と思わず頷いてしまいましたw
オプショナルなどconvが必要となる型推論が多いと、仮説を立てて試行するところで重くなってしまうんだなぁ、と勉強になりました。

コード署名を楽しく乗り切る方法

Josh Holtzさんのセッションです。

すごく分かりやすいセッションでした!
私は雰囲気でコード署名をやっている...!だったので、頭の中の整理整頓がされたみたいで、理解が進んだような気がします。そうだったのかコード署名! コード署名が楽しくなってきたかも!(?)

パズルで例えるなら4つのピースを合わせるだけ!(ただしそれぞれ似たようなピースがたくさんある) という状況にある中で、自分は何をすればいいかがよく分かっていなかったから混乱して苦手意識を持っていたんだ...と、世界の見え方が変わったような気がしました。

スポンサーブース

スポンサーブースのスタンプラリーを制覇したい!と燃え上がっていました🔥
一通り回って、なんだかクイズを出されているブースがとても多かった気がします。
Swiftの地力不足を痛感して悔しかったのでまた再チャレンジしたいです。

LINEヤフーのコードレビューをするコーナーでまだ出ていない指摘をできたのが嬉しかったです!
おそらく昨年のiOSDCでも同じような形式で出されていましたが、その時は私に指摘できることなどない…と早々に諦めてしまった記憶があり、個人的にはリベンジを果たせたようで達成感がありました。

DeNAの脳内previewはめっちゃ難しかったですw
VGrid初見でしたし、ToolbarItemPlacementにあんなに色々種類があることも知らなかったですし、SFSymbolsのうさぎと亀が知らない英単語で指定されるのも知らなくて、もっと勉強して出直します。うさぎと亀って万国共通なんですね...🫣

(追記)
day2は問題が変わっているらしいと聞きつけて果敢に再度チャレンジしましたが惨敗でしたw
近くの人に混じって解説を聞けたのでRyommはまた一つかしこくなりました🤓
(追記ここまで)

ZOZOのやらかしエピソードを書くコーナーでは、割とあるあるなエピソードを書けた気がしますw
トイレットペーパーをもらったので、やらかしと共に水に流していきたいと思います。

RevenueCatのノベルティがとても可愛かったです。売上猫のキーキャップが特にキュートでした!
(追記)
と言っていたらday2でゲットしました🙌 かわいい〜

(追記ここまで)

RIZAP Groupのノベルティもすごかった、ぬくぬくグッズをもらいました。
day1の夜にdrink upも開催されていたのですが、家が遠い問題により断念し、来年は近くに引っ越してるかホテル取るかしておきたいなと思いました。

無事スタンプラリーを制覇し、ピンバッジと普通のりこちゃんキラキラシールをゲットしました✊ (try! Swiftの鳥、名前あったんだな〜)
(追記)
day2でスタンプラリー2周目を敢行し、レアグッズを当てました!やったー:party_parrot:
https://x.com/ioco95/status/1771396969359192530?s=20

その他トピック

day1

Wi-Fiがないのは聞いていたので、オフラインでも使えるNotionのアプリをMacに入れていったのですが、結局ずっとTwitterにメモを垂れ流していました。モババは持っていって良かったです。

結構早めに着いてしまったので、受付が開くまで、すぐ前に立っていたイカしたSwift birdパーカーの方と少し話をしてました。ただ分かれてから気付いたのですが………名前教えてもらったのに自分名乗るの忘れてた😫‼️と………。Twitterやってるか早く聞いておけばよかったと後悔してます😫 明日はやらかさないように気をつけたいです。

バリスタが淹れてくれるコーヒーがめちゃくちゃ美味しかったです。
まさかこういったカンファレンスでフォームミルクでラテアートまでしてくれるコーヒー屋さんがきてくれるとは!!となり、並んででも飲みたい美味しいコーヒーでした!

day2

序盤に名乗ってTwitter聞く一連の流れを割と徹底できたので、昨日の失敗は糧になりました。
意外とTwitterを持っていなくてInstagramなら...みたいな方が割といたので、Instagramのオープンなアカウント作っておけば良かったなと思いました。

名札にアイコンのシール貼ってる人が分かりやすくていいなーと思ったので、自分もシール作ろーと思いました。
あと各所で貰ったステッカーを名札の裏に貼ってるのも良い!と思いました。大事に取っておいて日の目を見ないのもなんだかなぁと思いつつ、貼る場所に悩むところもあったので、帰ってから名札の裏を貰ったステッカーで埋め尽くしました😊スプラのロッカーみたいでかわいい

壊滅的に英語が話せないのが本当に悔やまれたのですが、After Partyや、その前の待機時間に結構話かけてくれて、そこから色々な人と話せて良かったです。本当は話しかけてみたかった人もいたんですが、MP不足でできなかったので、来年は今日より英語マシになっていたいです。とりあえずDuolingoとNHK語学とLangakuを入れてみました💪
正直AfterPartyでこんなに英語話者の人と話せるとは思っていなくて、世界で働く人の話を聞けて楽しかったです。Australianで集まっていたところになぜか自分が混じっていたんですが、短期留学したことのある場所に住んでいるらしく、地元トークみたいなことができたのも面白かったです。裏庭にカンガルーが出た話で、危なくないのか?と聞いたらメスだから大丈夫👍と言っていて雌雄わかるんだwと思ったのが印象に残ってます。
visionProをゲットした方が実際の使用感も教えてくれて、そんな感じなんだ〜と思ったりもしました。(結構会場で試させてくれる人もいましたが、メイクのラメがうつってしまったら申し訳ないと思って私は試してないです)

もちろん日本の開発者ともたくさん話せて楽しかったです!
WWDC現地にいつか行ってみたいけど英語がなぁと思って申込を見送ってしまう、という相談をしたら、現地に行く日本人たちで事前に集まったり、現地で交流したりするよ、という話を聞いて、今年は申し込んでみようかな...!と勇気が出ました!

AfterPartyには1000人ほど参加していたそうで、Swiftに関わる人がこんなにたくさんいるんだなぁと世界の広さを感じました。はじめは不安も大きかったですが多くの刺激を受けた2日間で、本当に参加して良かったと思います。

オーガナイザー、スタッフ、スピーカー、そして一緒に参加したみなさまおつかれさまでした!
https://x.com/ioco95/status/1771445224583127118?s=20

どうしても外せない用事のため、残念ながらday3は参加できなかったのですが、どのワークショップも魅力的で来年こそは参加したいものです🥹

Discussion

uhooiuhooi

脳内プレビューのツールバーとSF Symbolsの問題は、できるわけないと思って作ってるので大丈夫ですよ笑(当然私も解けない)

RyommRyomm

ありがとうございます笑
とはいえ解けないと悔しいので、解説ブログお願いします!笑笑