Skip to content

Commit

Permalink
Fix tutorials
Browse files Browse the repository at this point in the history
  • Loading branch information
kalupas226 committed Oct 15, 2023
1 parent 43dd4bc commit 7c776cd
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@Intro(title: "TCA における Bindings・Cancellation") {
次はここまでに作成してきたリポジトリ一覧画面に、リポジトリ検索機能を追加していきます。

このチュートリアルでは、検索機能の実装を通してTCA における Bindings と Cancellation について学ぶことができます。
このチュートリアルでは、検索機能の実装を通して TCA における Bindings と Cancellation について学ぶことができます。
}

@Section(title: "BindingReducer") {
Expand All @@ -14,7 +14,7 @@
@Steps {
@Step {
まずは、`RepositoryList` Reducer に検索機能のための状態を追加します。
検索機能を実現するために、後ほど SwiftUI の `searchable` API を利用しますが、その API は `text` という引数に `Binding<String>` な型を要求します
検索機能を実現するために、後ほど SwiftUI の `searchable` API を利用しますが、その API は `text` という引数に `Binding<String>` 型を要求します
TCA には Binding value を表現するための API である `@BindingState` Property Wrapper が用意されているため、これを利用して `searchable` に提供するための状態を追加します。

@Code(name: "RepositoryListView.swift", file: 03-01-code-0001.swift, previousFile: 03-01-code-0001-previous.swift)
Expand All @@ -28,7 +28,8 @@
}
@Step {
最後に TCA における Binding の処理を実行する核である、`BindingReducer` を `body` に組み込む必要があります。
この API は `body` に組み込むだけで、先ほど追加した `@BindingState`, `BindingAction` が機能するように作られています。
この API は `body` に組み込むだけで、先ほど追加した `@BindingState`, `BindingAction` が機能するように作られています。
`body` では、先ほど追加した `binding` Action をハンドリングする必要がありますが、今のところは実行したい処理はないため、一旦 `none` を返すだけにしておきます。

@Code(name: "RepositoryListView.swift", file: 03-01-code-0003.swift)
}
Expand All @@ -41,15 +42,15 @@
}
@Steps {
@Step {
View に検索機能を追加すると説明しましたが、SwiftUI には `searchable` が用意されているため、今回はそれを利用すれば View 側の実装はほとんど終わりです
View に検索機能を追加すると説明しましたが、SwiftUI には `searchable` が用意されているため、今回はそれを利用すれば View 側の実装はほとんど完了します
`searchable` には前述の通り、`Binding<String>` を提供する必要があるため、先ほど用意した `@BindingState` を提供します。
`ViewStore` 経由で `@BindingState` の projectedValue を利用する subscript が用意されており、`viewStore.$value` のようにすると `Binding<Value>` が取得できるようになっているため、それを利用します。
ついでに `navigationTitle` もつけておきましょう。

@Code(name: "RepositoryListView.swift", file: 03-02-code-0001.swift, previousFile: 03-02-code-0001-previous.swift)
}
@Step {
`navigationTitle` と `searchable` は `NavigationStack` 配下で機能する API であるため、`NavigationStack` で囲っておきましょう
`navigationTitle` と `searchable` は `NavigationStack` 配下で機能する API であるため、`NavigationStack` で View 全体を囲っておきましょう

@Code(name: "RepositoryListView.swift", file: 03-02-code-0002.swift)
}
Expand Down Expand Up @@ -79,23 +80,22 @@
> 可読性とのバランスも考慮する必要がありますが、TCA を利用する際はそのことを頭に入れながら開発しましょう。
}
ここまでで `query` を入力する度にリポジトリ検索して、それがリストに反映されるような機能を作ることができました。
しかし、現状の実装のままだと `query` 少しでも入力する度に API リクエストが実行されてしまうようになっており、通信コスト的にもサーバー側的にもあまり良いとは言えない状況になっています。
しかし、現状の実装のままだと `query` を少しでも入力する度に API リクエストが実行されてしまうようになっており、通信コスト的にもサーバー側的にもあまり良いとは言えない状況になっています。
@Step {
`query` 入力の度に API リクエストするのではなく、`query` 入力から 0.3 秒経つごとにリクエストするような実装に書き換えて、問題を解決しましょう。
このような挙動のことを debounce と呼んだりしますが、TCA には `Effect.debounce` が用意されているため、これを `Effect.run` につけてあげると簡単にその挙動を実装できます。
`debounce` には `Hashable` な `id` が必要となるため、そのための enum を用意しています
また、`debounce` の挙動を実現するために、一つ Action を追加しています
`debounce` には `Hashable` な `id` が必要となるため、そのための enum を用意します
また、`debounce` の挙動を実現するために、一つ Action も追加しましょう

@Code(name: "RepositoryListView.swift", file: 03-03-code-0003.swift)

> `Effect.debounce` は内部的には `Effect.cancellable` という API を利用しており、これによって指定の秒数内に複数の処理が実行された場合に、古いものはキャンセルするという実装によって、debounce の挙動を実現しています。
> `Effect.debounce` は内部的には `Effect.cancellable` という API を利用しており、指定の秒数内に複数の処理が実行された場合に、古いものはキャンセルするという処理によって、debounce の挙動を実現しています。
}
@Step {
さて、検索機能と呼べるものが実装できたので、アプリを起動して挙動を確認しておきましょう。
検索機能と呼べるものが実装できたので、アプリを起動して挙動を確認しておきましょう。

@Image(source: "03-03-image-0004", alt: "")
@Image(source: "03-03-image-0004.gif", alt: "アプリを起動して検索機能を確認している図")
}
検索機能が一通り実装し終わったところで、次は開発を効率的に進めるための要である Xcode Previews の改善を行っていきましょう!
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
リポジトリ一覧画面の実装が一通り終わりましたが、普段の開発効率を高めるために、もう少しリポジトリ一覧画面を改善できます。
具体的には、アプリの開発効率を高めてくれる Xcode Previews のための改善を行っていきます。

このチュートリアルでは、リポジトリ一覧画面の Xcode Previews の動作を改善することを通して、依存関係の制御方法および依存関係を制御することによって実現できる Xcode Previews 利用のテクニックを学ぶことができます
このチュートリアルでは、リポジトリ一覧画面の Xcode Previews の動作を改善することを通して、TCA における依存関係の制御方法および依存関係を制御することによって実現できる Xcode Previews 利用時のテクニックを学ぶことができます
}

@Section(title: "swift-dependencies による依存関係の制御") {
Expand All @@ -15,19 +15,19 @@
@Steps {
@Step {
リポジトリ一覧画面における依存関係は、GitHub API の search エンドポイントに対してリクエストしている部分となります。
この依存関係は、現在実際に通信が発生する API リクエストを行っていますが、これを制御可能にすることによって、Previews ではリクエストせずに mock のデータを即時に返すということができるようになります。
この依存関係は、現在実際に通信が発生する API リクエストを行っていますが、これを制御可能にすることによって、Previews では実際のリクエストを行わずに mock のデータを即時に返すということができるようになります。
そのため、まずはこの依存関係を制御するための interface を `GitHubAPIClient` という名前で作成しましょう。
この Client は `String` を受け取って `[Repository]` を返却する非同期な interface を持っているという構造になっています。

@Code(name: "Client.swift", file: 04-01-code-0001.swift)

> interface というと Swift では protocol で定義する方法が広く知られていますが、Point-Free は struct の property で interface を定義することを推奨しています
> interface というと Swift では protocol で定義する方法が広く知られていますが、Point-Free は struct の property で interface を表現することを推奨しています
> struct で interface を定義しておくと、後ほど紹介する swift-dependencies と親和性が高いコードにすることもできます。
>
> この辺りの話についての詳細が気になる方は [swift-dependencies のドキュメント](https://pointfreeco.github.io/swift-dependencies/main/documentation/dependencies/designingdependencies) を参照してください。
}
@Step {
次に、TCA で依存関係を便利に扱うための仕組みである swift-dependencies を利用していきます。
次に、TCA で依存関係を便利に扱うことができる swift-dependencies を利用していきます。
まず、swift-dependencies を利用するために `Dependencies` を import する必要があります。
そして、swift-dependencies の `DependencyKey` という protocol に Client を準拠させるために、`liveValue` を定義します。
`liveValue` の型は `DependencyKey` に準拠させた `GitHubAPIClient` 自身となっており、`liveValue` には実際にアプリで利用される機能を定義することになります。
Expand All @@ -37,7 +37,7 @@

> swift-dependencies は Point-Free 製の依存関係を管理するためのライブラリです。
> TCA では、この swift-dependencies を利用して依存関係を制御することが推奨されます。
> 今回の Workshop では詳しく説明できないため、気になる方は[ドキュメント](https://pointfreeco.github.io/swift-dependencies/main/documentation/dependencies)を参照してください
> 今回の Workshop では詳しく説明できないため、気になる方は[ドキュメント](https://pointfreeco.github.io/swift-dependencies/main/documentation/dependencies)等を参照してください
}
@Step {
swift-dependencies には `DependencyKey` の他に、Xcode Previews とテストのために依存関係を提供する Key として機能する `TestDependencyKey` という protocol があります。
Expand All @@ -58,13 +58,13 @@

@Code(name: "Client.swift", file: 04-01-code-0004.swift)

> xctest-dynamic-overlay の詳細について気になる方は、例の如く[ドキュメント](https://github.com/pointfreeco/xctest-dynamic-overlay)を参照してください

> xctest-dynamic-overlay の詳細について気になる方は、[リポジトリ](https://github.com/pointfreeco/xctest-dynamic-overlay)等を参照してください
>
> ここまででは `DependencyKey` と `TestDependencyKey` に Client を準拠させてきましたが、実は `DependencyKey` は `TestDependencyKey` に準拠しているため、`DependencyKey` にだけ準拠させるようにしても問題ないです。
> ただ、実装部分を明確に区切ることができたり、意識して使えばモジュール分離の観点でメリットがあるため、基本的には今回のように別々に準拠させておくと楽になると思います。
}
@Step {
最後に swift-dependencies を活用するために、`DependencyValues` という struct の extension として、`gitHubAPIClient` property を定義します。
最後に swift-dependencies を活用するために、`DependencyValues` という型の extension として、`gitHubAPIClient` property を定義します。
ここまでの手順を終えることによって、各モジュールの Reducer からは `@Dependency(\.gitHubAPIClient)` という形で依存関係が利用できるようになります。

@Code(name: "Client.swift", file: 04-01-code-0005.swift)
Expand All @@ -83,13 +83,13 @@
それでは先ほど定義した `GitHubAPIClient` を実際に Reducer で使ってみましょう。
先ほど少しだけ説明しましたが、Reducer では `@Dependency` という Property Wrapper を通してこの依存関係を利用できます。
この `@Dependency` を利用するために `Dependencies` を import します。
また、`GitHubAPIClient` の `liveValue` を実装した際に、Reducer の `searchRepositories(by:)` 内にある `TaskResult` の中身はカットしているはずなので、そこで代わりに `GitHubAPIClient` を利用するように書き換えましょう。
また、`GitHubAPIClient` の `liveValue` を実装した際に、Reducer の `searchRepositories(by:)` 内にある `TaskResult` の中身はカットしているはずなので、その部分で代わりに `GitHubAPIClient` を利用するように書き換えましょう。

@Code(name: "RepositoryListView.swift", file: 04-02-code-0001.swift, previousFile: 04-02-code-0001-previous.swift)
}
@Step {
Reducer での実装は終わったので、最後に Xcode Previews のコードに少し手を加えていきましょう。
実は `Store` の initializer には `withDependencies` という、`DependencyValues` を設定するための引数が存在しています
実は `Store` の initializer には `withDependencies` という、`DependencyValues` を上書きするための引数が存在しています
これを利用することで、「Previews では `GitHubAPIClient` を mock 実装で返す」ように `GitHubAPIClient` Dependency の実装を書き換えることができます。
実際に、事前にプロジェクトに用意されている `Repository.mock(id:)` を利用して、`gitHubAPIClient.searchRepositories` で mock を返すようにして、Previews の動作を確認してみましょう。
`ProgressView` の変化も Preview で見れると捗りそうなので、`Task.sleep(for:)` を使って適当な秒数 sleep させます。
Expand All @@ -98,6 +98,8 @@
@Image(source: "04-02-image-0002.gif", alt: "withDependencies を利用した View を Preview している図")
}
}
このように swift-dependencies を利用してアプリの依存関係を制御することで、実際のアプリで動作する依存関係と Xcode Previews で動作する依存関係を簡単に切り替えることができます。
依存関係の制御はテストにおいても重要となってくるため、次はそれについて学んでいきましょう。
}
}
}
Loading

0 comments on commit 7c776cd

Please sign in to comment.