WWDC 2022 アプリエンジニア感想まとめ

WWDC 2022

2022年6月6日から6月10日にかけて行われた「WWDC 2022」では、さまざまなセッション動画が公開されています。 本記事では、有志のGoodpatchアプリエンジニアが興味を持ったセッションをピックアップし、セッションの概要や感想をご紹介します!

※本稿はAppleにより一般公開された内容にのみ基づいて記載しました。

ピックアップセッション💙

What’s new in iPad app design

このセッションでは、iPad OS 16 から新しく実装されたデザインについて紹介されています。

  • ツールバーのカスタマイズ
  • ドキュメントメニュー
  • 編集メニュー
  • 検索(Find)と置換
  • ナビゲーション
  • 検索(Search)
  • テーブル
  • セレクション

大きく意訳しているため、気になった方は元動画を見ることをおすすめします。

ツールバーのカスタマイズ

ツールバーのカスタマイズ
ナビゲーションタイトルが左揃えになり、中央にカスタマイズ可能なツールバーが提供されます。 ユーザーがウィンドウサイズを変更すると、中央のツールバーアイテムが自動的にオーバーフローメニューに隠され、更に小さくすると、先頭と末尾のセクションのみが残ります。

SwiftUI on iPad: Add toolbars, titles, and more のセッションでは詳しい実装方法が説明されており、SecondaryAction に設定したものが中央に、PrimaryAction に設定したものが末尾に表示されるようです。 Macアプリのように、複数のツールバーアイテムを一つのセクションにまとめることも可能です。

ドキュメントメニュー

ドキュメントメニュー
ドキュメント全体に影響を与えるアクションです。 複製、名前の変更、移動、エクスポート、印刷などが該当します。 こちらも、SwiftUI on iPad: Add toolbars, titles, and more のセッションで詳しい実装方法について解説しています。

編集メニュー

編集メニュー(ポインタ操作)
選択したテキストの上に表示され、コピーやペーストなどが含まれるメニューです。 タッチ操作の場合は今まで通り水平に表示され、ポインタ操作の場合は垂直レイアウトのオプションが提供されます。 垂直レイアウトでは複数のメニューをセクションにまとめることも可能です。 テキスト以外にも使うことが可能です。

検索(Find)と置換

検索(Find)と置換
高度な検索と置換ができるようになりました。

ナビゲーション

ナビゲーション
iPad OS 16 では、「ブラウザスタイルナビゲーション」という新しいスタイルのナビゲーションが提供されます。 ファイルアプリやWebブラウザなど、複雑な階層があり、レベル間をジャンプすることが多いナビゲーションでうまく機能します。 逆に写真アプリなど、階層が浅かったりフラットな場合はこのナビゲーションを使う必要ありません。

検索(Search)

検索(Search)
ブラウザスタイルナビゲーションの場合、右上に検索バーを配置してフィルタリングを提供することを推奨します。

  • 履歴
  • レコメンド
  • フィルター

などの機能を提供できます。

注意として、右上の検索バーは下に表示されたコンテンツを検索するためのものです。 アプリ全体を検索したい場合は、検索タブを実装しましょう。

テーブル

テーブル
今までもUITableViewはありましたが、これが本当の意味でのテーブルです。 iPadのディスプレイは小さいため必要な情報をすべて表示することはできませんが、表示内容をユーザーが変更することができます。

データではなく、コンテンツを表示します。

幅が狭くなると、単一列のリストになります。 詳細はセカンダリエリアに移動して引き続き利用できるようにすることを推奨します。 並べ替え機能はツールバーボタンに入ります。

実装については SwiftUI on iPad: Organize your interface で紹介されています。

セレクションとメニュー

セレクションとメニュー
iPad OS 15 ではポインターを使って複数コンテンツを選択する際、選択モードになっていました。 iPad OS 16 では編集モードにならずに選択・選択解除できるようになりました。 また、空の場所を右クリックすることでコンテキストメニューを表示できます。

まとめと感想

iPadの新しいデザインについてのセッションを紹介しました。 iPadのデザインについてですが、iPhoneに影響のある内容も多かったですね。 SwiftUIが主流になりつつあり、デザイナーとの連携がより重要になっているのを感じました。

Create parametric 3D room scans with RoomPlan

このセッションではLiDARを搭載したiPhoneやiPadを使って部屋をスキャンし3Dモデル化するRoomPlanというフレームワークについて解説しています。

セッション中ではソースコードを交えた実装方法も説明していますが、ここではRoomPlanの概要にフォーカスして紹介します。

RoomPlanのAPIは、おおきくふたつに分けられます。スキャン体験API(Scanning experience API)と、データAPIです。

スキャン体験API(Scanning experience API)

スキャン体験は、RoomCaptureView によって提供されます。RoomCaptureView は以下を提供します。

  • 検出過程のフィードバック。カメラ映像に重ね合わせ、壁、窓、開口部、ドアや部屋の中の物体に対して輪郭を表示する
  • ビュー下部にスキャンの進捗状況を3Dモデルとしてリアルタイムに表示する
  • 部屋の状態やユーザーの動きに応じて、適切にスキャンを行えるようコーチングとユーザーガイダンスを表示する

RoomCaptureViewの提供する体験

データAPI

データAPIは、スキャン中にデータ構造へのアクセスを提供します。これを用いて、ビジュアライゼーションを自前で実装することができます。

ワークフローは、3つの部分で構成されています。

データAPIの基本的なワークフロー

スキャン(Scan)

RoomCaptureSession からリアルタイムに、3Dモデルやコーチングを含むスキャンの更新情報を取得します。

コーチングは、オブジェクトまでの距離、スキャン速度、部屋の照明の調整、テクスチャの多い場所へのフォーカスなど、ユーザーへのさまざまな指示を表します。

処理(Process)

スキャンしたデータを処理し、最終的な3Dモデルを生成します。

CapturedRoom オブジェクトはスキャンした部屋のデータ構造を保持します。 Surface には、半径などのカーブを表す固有の属性、開始角度と終了角度、サーフェスの4つの異なるエッジ、壁、開口部、窓、ドアなどの建築のカテゴリが含まれています。 Object は、テーブル、ベッド、ソファなどの家具カテゴリを示します。 それぞれが、寸法やスキャンされた表面情報、3段階で表される信頼度などの属性を持ちます。

CapturedRoomのデータ構造

エクスポート(Export)

USDまたはUSDZデータにエクスポートすることができ、既存のワークフローに使用することができます。

既存ワークフローでの活用例

スキャンに適した条件や準備

RoomPlanによるスキャンには適した部屋の条件があります。

適した条件

  • 最大約9メートル×9メートルの大きさの部屋のシングルルーム
  • 最低50ルクス(夜間のリビングルームに相当)以上の明るさ

適さない条件

  • フルハイトの鏡や窓
  • 高い天井
  • とても暗い表面

また高い精度を得るためには事前の準備が重要です。

  • 日中はカーテンを開け、十分な自然光を取り入れる
  • ドアを閉めて部屋の外の余計な場所がスキャンされるのを防ぐ

感想

部屋をスキャンし3Dモデル化する技術は、建築分野での活用にとどまらず、ARKitなどと組み合わせることで、部屋と仮想空間のコンテンツとをより精密に連携させた新たなARのユースケースを生み出せそうだと妄想しています。

Use Xcode for server-side development

このセッションでは、サーバーサイドコードを含めたプロジェクトをXcodeで管理する方法がサンプルコードとともに紹介されています。

Swiftでのサーバーサイド開発は2016年ごろから登場しました。その頃はSwift3.0が出た時期で、そろそろSwiftも安定期に入るかというタイミング、全てSwiftで書けるようになるかもという期待も大きかったですがなかなか2022年現在もそこまで広まりきってないというのが実感値としてあります。

そんな中今回のセッションを見ると、SPMやXcode、Swiftの最新機能を活用することでかなりフルスタックSwiftアプリケーションを快適に開発できるようになったことがうかがえます。ここではざっくりまとめとしてその要点をご紹介します。 なお、使われているのはXcode14及びVaporとなります。

Package.swiftの活用

まず最初のポイントはPackage.swiftの活用です。Swift Package Managerでは実行ファイルも扱うことができるので、以下のように.executable()というものを使うことでサーバーサイドのプロジェクトを実行できる形式として扱うことができます。

// swift-tools-version: 5.7

import PackageDescription

let package = Package(
    name: "MyServer",
    platforms: [.macOS("12.0")],
    products: [
        .executable(
            name: "MyServer",
            targets: ["MyServer"]),
    ],
    dependencies: [
        .package(url: "https://github.com/vapor/vapor.git", .upToNextMajor(from: "4.0.0")),
    ],
    targets: [
        .executableTarget(
            name: "MyServer",
            dependencies: [
                .product(name: "Vapor", package: "vapor")
            ]),
        .testTarget(
            name: "MyServerTests",
            dependencies: ["MyServer"]),
    ]
)

また、Package.swiftを介しているため、このプロジェクトはXcodeのプロジェクトとしても扱うことができます。 セッションの中ではこれをクライアントアプリのXcodeプロジェクトと一緒にワークスペースに取り入れて、同じXcodeのウィンドウでクライアントアプリとサーバーサイド両方を開発できるようにしていました。 現在のXcodeでは複数ターゲットを同時に実行することができるため、実行に関してもクライアントとサーバー双方を実行して試すことができます。

ただしサーバーはローカルサーバーとして立ち上がるため、シミュレータでのみ通信ができるようになっています。

actor の活用

データの読み込みなどを行う部分にはSwift5.5から登場したactorを使うことを推奨していました。特にサーバーは同時に複数のリクエストをさばくことになる可能性があるため、データ競合を防げるactorは非常に簡単に安全な処理を実装することができます。 セッションではJSONファイルからデータを読み込むStorageというものをactorを使って以下のように実装する例が紹介されていました。

actor Storage {
    let jsonDecoder: JSONDecoder
    var donuts = [Model.Donut]()

    init() {
        self.jsonDecoder = JSONDecoder()
        self.jsonDecoder.dateDecodingStrategy = .iso8601
    }

    func load() throws {
        guard let path = Bundle.module.path(forResource: "menu", ofType: "json") else {
            throw Errors.menuFileNotFound
        }
        guard let data = FileManager.default.contents(atPath: path) else {
            throw Errors.failedLoadingMenu
        }

        self.donuts = try self.jsonDecoder.decode([Model.Donut].self, from: data)
    }

    func listDonuts() -> [Model.Donut] {
        return self.donuts
    }

    enum Errors: Error {
        case menuFileNotFound
        case failedLoadingMenu
    }
}

これはもちろんサーバーサイドだけでなく、クライアントアプリで同じようなことをする際にも同じ恩恵を受けることができます。

オールSwiftだからこそのコード共有

最後に紹介するポイントはコード共有です。全てを同じXcodeのワークスペースで管理しており、全てがSwiftで書かれているため、実はこの構成だとデータモデルを同じコードを使い回すことができるようになります。 セッションではSharedという名前のパッケージを作り、そこにデータモデルのコードを書いた上でサーバーサイドとクライアントアプリ双方に読み込んで使うという方法が紹介されていました。

このようなSwift Package Managerを活用したモジュール分割に関しては、昨年のiOSDCごろから日本でも話題となっています。モジュール分割に関しては以下の記事が参考になったのでよければ読んでみてください。

tech.timee.co.jp

まとめ

すべてSwiftで作るフルスタックアプリの開発について、セッションを通じて見てきました。 冒頭にもお話しした通り、出始めの時期以降あまりサーバーサイドSwiftに触れる機会がなかったのですが、今回のセッションを見ることでかなりその開発環境が快適になってきていることを実感することができました。

SwiftやXcodeは開発者体験を重視してデザイン、開発されているので今後の発展も楽しみです。 とりあえず筆者は時間を見つけて実際にフルスタックSwiftアプリを作ってみようと思います。

The craft of SwiftUI API design: Progressive disclosure

このセッションでは、SwiftUIのAPI設計の基本原則のひとつとして、Progressive Disclosure(段階的開示)のコンセプトが紹介されています。

APIを設計する際は、開発者がコードを書く場所(declaration site)の視点だけでなく、そのコードが実際に使われる場所(call site)の視点も必要であり、Progressive Disclosureとは、ユースケースの複雑さに応じてcall siteの複雑さも増していくようにAPIを設計すること、と述べられています。

Progressive Disclosureは、開発者にとって次のようなメリットがあります。

  • 最初のビルドと実行にかかる時間を最小限に抑え、APIを素早く利用できるようになる
  • コードの学習曲線も短くなり、すべてのユースケースに関連しない概念によって、APIが埋もれてしまうのを防げる

Progressive Disclosureの原則をもとにAPIの設計する方法として、次の4つが紹介されています。

  • Consider common use cases:一般的なユースケースを考慮することから始める
  • Provide intelligent defaults:理にかなったデフォルトを提供するように努め、一般的なケースで詳細について考える必要がないようにする
  • Optimize the call site:call siteの最適化を図る
  • Compose, don’t enumerate:可能性を列挙するのではなく、ピースを組み立てられるようにし、あらゆるユースケースに対応できる柔軟性のあるAPIを構築する

「Consider common use cases」は、共通のユースケースを考慮することで、APIを簡潔にするということです。SwiftUIの実例として「Label Pattern」が紹介されています。

Label Patternの例
Label Patternの例

次のようにButtonのラベルをカスタマイズできますが、多くの場合、上記のLabel PatternのAPIで十分です。

Button {
    currentPage += 1
} label: {
    HStack {
        Text ("Next Page")
        NextPagePreview()
    }
}

Progressive Disclosureのコンセプトは、APIの設計に限定されません。このセッションでは、macOSの保存ダイアログのUIを例に取り上げています。このダイアログでは「入力済みのデフォルトの保存場所」「ドロップダウンから頻繁に選択する場所に簡単にたどり着ける」「ダイアログを展開すると複雑なUIが表示され、ファイルシステムをブラウズできるようになる」のように、段階的に情報が開示されています。

WWDC17の「Essential Design Principles」では、UIデザインのデザイン原則の文脈で「Progressive Disclosure」が紹介されています。

Progressive disclosure is a necessary and helpful technique for managing complexity and simplifying decision making. But the same technique can bury information or functionality. So what do you do? Well, discussions about how to properly use progressive disclosure often come down to the 80/20 rule.

段階的開示は、複雑性を管理し、意思決定を単純化するために必要かつ有用な手法である。しかし、同じ手法で情報や機能を埋もれさせてしまうこともある。では、どうすればよいのか。段階的開示の適切な使用方法についての議論は、しばしば80/20の法則に帰結する(意訳)

ソフトウェアデザインにおけるデザイン原則は、「Interface」のデザイン(設計)に活用でき、APIやUIに区別がないことが窺えます。

最後に、このセッションで印象に残った言及を紹介して、このパートを締めたいと思います。

The code equivalent to providing a nice UI experience is making your APIs feel great to use.

優れたUI体験を提供するのと同等のコードは、APIの使用を素晴らしい気分にさせる(意訳)

WWDC17「Essential Design Principles 」
WWDC17「Essential Design Principles 」

関連リンク

One more thing...

集中してコーディングしたい時は、このプレイリストを聴いています。

セッションを紹介したエンジニア💁‍♂️