try! Swift Tokyo 2025 セッションまとめ

try! Swift Tokyo 2025へ参加してきたので気になったセッションをいくつか抜粋して内容や感想などをまとめようと思います。

# iOS 17, 16, 15などの新機能

https://www.youtube.com/watch?v=qY09lmDo7GU&list=PLCl5NM4qD3u_Azg7gKw5CK_DqSLeb4QMY

まずはiOS15〜17で出た新規APIの話です。
新規APIは毎年WWDCで発表されるけど、その当時の最新OSバージョンでしか使えないことが多いからもう一度復習しておきたいよねという話から始まりました。1年以上前のセッションの内容だとほとんど覚えていないのでとても勉強になったセッションでした。
発表では、

- `.formatted()`
- `.privacySensitive()`
- `ContentUnavailableView`
- `.scrollTargetBehavior()`
- `.containerRelativeFrame()`
- `.contentTransition(.numericText())`

などのAPIの紹介がありました。

```swift
import Foundation

let now = Date()

// 02/03/2025, 14:52
now.formatted()

// 02/03/2025
now.formatted(date: .numeric, time: .omitted)

// 0.420
0.42.formatted(
    .number.precision(.significanDigits(3))
)

// Option A, Option B, or Option C
[
    "Option A",
    "Option B",
    "Option C",
].formatted(.list(type: .or))
```

↑で紹介した`.formatted()`のほか、`.scrollTargetBehavior()`、`.containerRelativeFrame()`などはiOS17以降のScrollViewで使えるケースが多そうでした。

# 素早く実現する優れたアプリデザイン

https://www.youtube.com/watch?v=5qURvTGz40Q&list=PLCl5NM4qD3u_Azg7gKw5CK_DqSLeb4QMY

元Appleのデザイナーだった方のセッションです。Halideというカメラアプリを開発していて、Apple Design Awardsでの受賞歴もあるとのことでした。
https://apps.apple.com/jp/app/halide-mark-ii-pro-camera/id885697368

発表内容は技術寄りではなく、良いアプリを作るために意識するべきことについてでした。英語力が足りず会場ではほとんど理解できませんでしたが、アーカイブを見て大まかな内容は理解できました。
本物のカメラにある物理的なボタンやダイヤルなどの操作は、部品がどこにあるか分かっているため写真を撮りながらでも操作しやすい。それを小さな画面が一つしかないiPhoneにいかに適用させるかという話でした。

> Great software doesn't oversimplify.
Great software helps users learn.
Don't think that good design means making something too simple.

というフレーズが心に残りました。
Apple時代のスティーブ・ジョブズとのやりとりについて聞けたのも興味深かったです。
Xや他の参加レポートなどでも好評が多かったと感じたので、興味があればアプリを実際に使ってみても良いかもしれません。

# SwiftUI Textを使った特殊効果

https://www.youtube.com/watch?v=zbnYo_46kS8&list=PLCl5NM4qD3u_Azg7gKw5CK_DqSLeb4QMY

Hacking with Swiftの著者であるPaul Hudsonさんのセッションです。
Word ArtでSwiftのStringを装飾する方法について紹介していました。

![Screenshot 2025-05-21 at 16.44.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281766/d6a266c2-7882-4eb1-b52c-8c9e914a6405.png)

SwiftUIのTextは

- Lines（オレンジ部分）: 行。画面のサイズに応じて横幅が変わったりする
- Runs（緑部分）: 同じ書式を持つ行内にあるテキストのまとまり
- Glyphs（青部分）: スペースを含む個々の文字

という3つの要素で文章がレンダリングされていて、iOS18以降では`TextRenderer`を利用することでこれら3つを好きなようにレンダリングできるようになりました。
紹介されていたサンプルコードと画像でどのような装飾ができるかをまとめます。
`TextRenderer`のカスタム性もさることながら、個人的にはTextでMarkdownがそのまま使えることに衝撃を受けました。

![Screenshot 2025-05-23 at 15.19.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281766/053d70bc-2360-46a3-ace7-755b97164928.png)

```swift
Text("This is an ***important*** string with lots of text inside.")
    .font(.system(size: 96))
    .lineSpacing(50)
    .textRenderer(StripedRenderer())

...

struct StripedRenderer: TextRenderer {
    func draw(layout: Text.Layout, in ctx: inout GraphicsContext) {
        for (i, line) in layout.enumerated() {
            if i.isMultiple(of: 2) {
                ctx.opacity = 0.35
            } else {
                ctx.opacity = 1
            }
            ctx.draw(line)
        }
    }
}
```

![Screenshot 2025-05-23 at 15.19.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281766/d7278e56-247f-4cde-9388-36064e577d0e.png)

```swift
let important = Text("important")
    .customAttribute(Highlight())
Text("This is an \(important) string with lots of text inside.")
    .font(.system(size: 96))
    .lineSpacing(50)
    .textRenderer(HighlightRenderer())

struct Highlight: TextAttribute {}

struct HighlightRenderer: TextRenderer {
    let color: Color = .blue
    
    func draw(layout: Text.Layout, in ctx: inout GraphicsContext) {
        for line in layout {
            for run in line {
                if run[Highlight.self] != nil {
                    ctx.fill(
                        Rectangle()
                            .path(in: run.typographicBounds.rect), 
                        with: .color(color))
                }
                ctx.draw(run)
            }
        }
    }
}
```

![Screenshot 2025-05-23 at 15.18.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281766/2a202049-aac3-4cc9-90d6-17097ea5ea87.png)

```swift
Text("This is an important string with lots of text inside.")
    .font(.system(size: 96))
    .lineSpacing(50)
    .textRenderer(
        WaveRenderer(
            strength: 20,
            frequency: 0.5
        )
    )

struct WaveRenderer: TextRenderer {
    let strength: Double
    let frequency: Double
    
    func draw(layout: Text.Layout, in ctx: inout GraphicsContext) {
        for line in layout {
            for run in line {
                for (i, glyph) in run.enumerated() {
                    let yOffset = strength * sin(Double(i) * frequency)
                    
                    var copy = ctx
                    copy.translateBy(x: 0, y: yOffset)
                    copy.draw(glyph)
                }
            }
        }
    }
}
```

また後半では、さらに複雑なWord ArtをInfernoというOSSで実現する方法について解説がありました。

https://github.com/twostraws/Inferno

# 日本語でのSwiftプログラミング

https://www.youtube.com/watch?v=uVgxJpSvp-I&list=PLCl5NM4qD3u_Azg7gKw5CK_DqSLeb4QMY

Swiftではどこまで日本語だけを使ってプログラミングができるのかというテーマで、自身が開発している将棋アプリを題材にして紹介していました。
登壇者のブログで発表内容に近いものがありました。

https://zenn.dev/huiygfutfgvjknj/articles/9990f9ee42a410
https://dev.to/sfrrvsdbbf/swift-programming-in-japanese-5h98

ただ日本語でプログラミングをしているだけでなく、SwiftUIを使用して全てのAppleプラットフォームでリリースしているらしいです。Apple Watchでもリリースしているのには流石に笑いました。
クラス名やプロパティはもちろん、enumのケース名も日本語なので、将棋の駒名に`rawValue`がそのまま使えるなどのメリットがあるらしいです。
また、一部の例外はあるが、ほとんどの文字が名前空間で使用できると聞き、Appleのサポートの手厚さに感銘を受けました。

ここまで徹底して日本語オンリーのプログラミングに挑戦しているのは初めて見たのでとても興味深かったです。そして何よりソースコードが超面白いのでぜひ見てほしいです。日本語と言いつつコミットメッセージに度々英語が出てくるのがじわじわ来ました。

https://github.com/FlipByBlink/PlainShogi

# ⚡️ 40万以上DLされた個人開発アプリをサービス終了するためにしたこと

https://www.youtube.com/watch?v=gCbpdvQyapM&list=PLCl5NM4qD3u_Azg7gKw5CK_DqSLeb4QMY

WebCollectorというWebサイト全体のスクリーンショットを撮るアプリのサービス終了についての話でした。
こちらも登壇者の記事があったので先に共有しておきます。

https://note.com/fromkk/n/n84e374498386

- Appleが同じ機能を作成した(iOS13から)
- AdMobが広告を非表示にした
- 増え続けるFirebaseの利用料

の3つを理由にアプリのサービスを終了することにしたとのことでした。余談ですがSafariでフルスクリーンショットが撮れるようになったのは知りませんでした。
赤字が増え続け、最終的にVision Proが買えるまで膨れ上がったとのことで、個人アプリの辛い現実を学ぶことができました。

サービス終了にあたり、主に以下の4つの対応を紹介していました。

- 退会機能の追加
- お知らせ表示機能の追加
- PUSH通知を受け取れる機能の追加
- 画像を全てダウンロードする機能の追加

赤字を抱えつつも、サービス終了後のケアもしていて素晴らしいと思いました。自分も今後個人アプリを開発することがあればぜひ参考にしたいです。

# SwiftUI APIデザインの教訓: 手続き型APIと宣言型世界の橋渡し

https://www.youtube.com/watch?v=fSvYYn7jiSQ&list=PLCl5NM4qD3u_Azg7gKw5CK_DqSLeb4QMY

SwiftUIで手続き型APIを使用するための設計案の一つを紹介しているセッションです。カメラアプリを題材にし、手続き型APIである`AVFoundation`をSwiftUIで使用する際の設計案です。
AppleのチュートリアルではAVFoundationをViewModelに切り出して`@StateObject`を使う方法が紹介されています。
https://developer.apple.com/tutorials/sample-apps/capturingphotos-camerapreview

このセッションではよりSwiftUIに近いAPIデザインを目指し、方法を紹介されていました。
実装の詳細は登壇者のサンプルコードを見ていただきたいですが、`UIViewRepresentable`やcallbackで`@ViewBuilder`を活用することでこのようにSwiftUIで扱いやすい構造が実現できていました。

```swift
struct CaptureView: View {
    var body: some View {
        CaptureSession(isEnabled: true) {
            CapturePreviewView(device: .backCamera)

            CaptureOutput(device: .backCamera) ...
        }
    }
}
```

https://github.com/niw/CaptureUI

# Swiftコード生成の可能性を解き放て

https://www.youtube.com/watch?v=Csvhb-XnQUY&list=PLCl5NM4qD3u_Azg7gKw5CK_DqSLeb4QMY

Swift Macros、Swift Package plugins、Swift Syntaxを使ってJavaフレームワークのSwift版を作ったという話です。
Androidには依存注入のコードを自動生成するDaggerというライブラリがあるのですが、それと同じ機能を提供するSwordというライブラリをどのように開発したかという内容でした。

https://github.com/rockname/sword

自分も最近Android開発にチャレンジしており、Daggerの依存注入の強さに惹かれていたため、かなり興味深いセッションでした。

詳細は登壇者の発表ブログがあるのでそれが一番分かりやすいと思います。

https://blog.smartbank.co.jp/entry/2025/04/11/unlock-the-potential-of-swift-code-generation

Swift MacrosやSwift Syntaxの構文解析を使ったコードは普段ほぼ触ることがない分開発やメンテが大変ですが、上手く活用できればコードの見通しが良くなるのでとても便利だと思いました。その一方で、Swift Macrosの成果物を使用したい場合、現在のXcodeでは補完の効かないことがあり、わざわざExpand Macroからコピーして貼り付け、のようなことをするのが多いため、今後のXcodeで改善されるとさらに使いやすくなるかと思います。

# ⚡️ SwiftUIの8番出口

https://www.youtube.com/watch?v=eGNKJaj1rC8&list=PLCl5NM4qD3u_Azg7gKw5CK_DqSLeb4QMY

エンジニア以外にも一度見て欲しい動画です。
一時期ブームになって映画化も予定されている「8番出口」のSwiftバージョン？です。発表を見て欲しいのであえてそれ以上の説明はしませんが、スライドの作り込みが素晴らしすぎて肝心の内容がほとんど頭に入ってこないほどでした。

- `isExclusiveTouch`
- `Layout` protocol
- `.scrollDismissesKeyboard(.immediately)`
- `ShareLink`

発表ではUIKitとSwiftUIのこのようなAPIの紹介があり、アーカイブを見直しましたがとても勉強になりました。