Swift, iOSでGraphQLクライアントを導入する

# この記事について

iOSアプリでGraphQLサーバにアクセスするためのクライアントを実装するための導入を書きます。サーバ機能ではなく、クライアント機能の説明です。

- Swift
- iOS15
- Xcode13

なお、サーバ側の実装については[【Go言語】はじめてのGraphQLサーバ実装 | gqlgen](https://qiita.com/hiroyky/items/4d7764172e73ff54f18b)も記述したのでご覧下さい。

# 手順
## Apollo-iosパッケージのインストール

GraphQLのライブラリではおなじみのapolloを利用します。
XcodeのFile -> Add Packages を開き、apollo-iosを検索してインストールします。

## スキーマ定義のダウンロード

### apolloコマンドのインストール
`apollo`コマンドがインストールされていなければインストールします。インストールにはnode.jsやJavaScriptでの開発でパッケージ管理でおなじみの`npm`を使います。(npmコマンドのインストールについては割愛させていただきます。)

```shell
$ npm install -g apollo
```

### スキーマのダウンロード

GraphQLサーバに向けて下記のコマンドを実行します。ここでは`schema.json`ファイルにスキーマ定義が全て記述されます。

```shell
$ apollo schema:download --endpoint=http://localhost:8080/graphql schema.json
```

`schema.json`をXcodeプロジェクト内に設置します。デフォルトではビルドターゲットのアプリの階層直下に保存します。つまり`*.xcodeproj`ファイルがある階層の次のディレクトリです。(Assets.xcassetsディレクトリがある階層に保存)

## ビルド設定の追加

ここが難所です。[Apollo公式ドキュメント](https://www.apollographql.com/docs/ios/installation/#adding-a-code-generation-build-step)の「Adding a code generation build step」と併せてご覧下さい。

1. ビルドターゲット・アプリケーションのBuildPhasesを開きます。
1. BuildPhasesで「+」ボタンをクリックし「New Run Script Phase」を選びます。「Run Script」が追加されます。
1. 追加された「Run Script」を「Generate Apollo GraphQL API」に名称変更します。
    1. タイトル部分をクリックすると変更できます。
    1. あくまでも開発者が見て分かりやすい名前にします。
1. 「Generate Apollo GraphQL API」を既存項目の「Compile Sources」の上に移動します。
1. [Apollo公式ドキュメント](https://www.apollographql.com/docs/ios/installation/#adding-a-code-generation-build-step)の「Swift Package Manager Run Script」を画面のフォームに貼り付けます。
    1. (必要に応じて)最後の行でschema.jsonのパスを編集します。
    1. (必要に応じて)最後の行で生成されるSwiftファイルのパスを編集します。

![screen001_アートボード 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73358/96089bc6-9ddc-70cf-75eb-2b727ecbaaa7.png)

## ビルド

通常通りアプリケーションをビルドします。`API.swift`が生成されます。ただ、プロジェクト内では管理されていないのでfinder等でソースコードを確認したら、Xcodeのプロジェクトに追加します。

#### エラー
ここで下記のようなエラーに遭遇しました。[こちらの投稿](https://community.apollographql.com/t/error-building-swift-project-after-installing-apollo-ios-client/2308/2)によるとapollo-iosの0.9.5バージョンではSwift5にきちんと対応できていないことが原因のようです。「Update Package」をして0.50.0にしたら解消しました。(2022年1月11日現在)

```
Cannot convert parent type 'EnumeratedSequence<[Key]>' to expected type 'EnumeratedSequence<IndexingIterator<Array<Key>>>'
```

## クエリの実行

[https://www.apollographql.com/docs/ios/initialization/#basic-client-creation](Apollo#basic client creation)にて基本的なクライントの実装方法が説明されています。シングルトン設計にしていますね。

```swift:GqlDriver.swift

import Foundation
import Apollo

class GqlDriver {
    static let shared = ApiDriver()
    
    private (set) lazy var apollo = ApolloClient(url: URL(string: "http://localhost:8080/graphql")!)
}
```

上記のGraphQL APIドライバクラスに対して下記のようにクエリを実行します。`fetch`関数でサーバに問い合わせています。引数の`query`には先ほどの自動生成で作成されたGraphQLクエリを指定します。

```swift
func func1() {
    GqlDriver.shared.apollo.fetch(query: UserQuery(id:"VXNlcjox")) { result in
            switch result {
            case .success(let r):
                // r に戻り値が含まれるので処理を書く。
            case .failure(let e):
                print("\(e)")
            }
    }
}
```

# 参考・関連文献
- https://www.apollographql.com/docs/ios/installation/
- https://www.apollographql.com/docs/ios/downloading-schema/
- https://www.apollographql.com/docs/devtools/cli/
- https://community.apollographql.com/t/error-building-swift-project-after-installing-apollo-ios-client/2308/2
- https://www.apollographql.com/docs/ios/initialization/#basic-client-creation
- https://blog.spacemarket.com/code/graphql-on-swift-by-apollo-ios/