見出し画像

Apollo iOS チュートリアル (1)

以下の記事を参考に書いてます。

Apollo iOS Tutorial

1. はじめに

このチュートリアルでは、アプリに Apollo iOS SDK を追加して、GraphQLサーバ と通信する方法を紹介します。

以下のツールで動作することを確認しています。

・Xcode 11.5
・Swift 5.2
・Apollo iOS SDK 0.28.0

2. 作成するアプリ

このチュートリアルでは、SpaceX のロケットの座席予約するためのアプリを作成します。その過程で、Apollo iOS SDK の以下の機能を利用します。

・スキーマのダウンロード
・コード生成
・クエリとミューテーション
・エラー処理

アプリを動作確認できるように、ビルド済みの GraphQLサーバ を用意しています。

3. Xcodeのプロジェクトの作成

Xcodeのプロジェクトの作成手順は、次のとおりです。

(1) Xcodeを開き、メニュー「File → New → Project」を選択。
(2) iOSテンプレートで「Master-Detail App」を選択し、Nextボタンを押す。
(3) 「Product Name」に「RocketReserver」を指定し、「User Interface」が「Storyboard」であることを確認し、Nextボタンを押す。
(4) Createボタンを押す。

4. プロジェクトへのApollo iOS SDKの追加

Xcodeのプロジェクトに「Apollo iOS SDK」を追加する手順は、次のとおりです。

(1) メニュー「File → Swift Packages → Add Package Dependency」を選択。
(2) パッケージリポジトリとして https://github.com/apollographql/apollo-ios.git を指定し、Nextボタンを押す。

画像1

(3) 「Version」ドロップダウンで Up to Next Minor を選択し、Nextボタンを押す。

画像2

(4) Apollo ApolloWebSocket を選択し、Finishボタンを押す。

画像3

5. GraphiQLでのスキーマの確認

このチュートリアルでは、「Apolloフルスタックチュートリアル」で作成した GraphQLサーバ の修正版を使用します。「https://apollo-fullstack-tutorial.herokuapp.com/」にアクセスすることで、GraphiQL を使って「スキーマ」を確認できます。

画像4

「スキーマ」は、サーバーが実行できる GraphQL 操作を定義します。右側の「SCHEMA」をクリックするとスキーマの定義が、「DOCS」をクリックすると、クエリとMutationの説明の一覧が表示されます。

画像18

6. Xcodeでのスキーマの準備 - schema.json

「Apollo iOS SDK」からコードを生成するには、サーバのスキーマのローカルコピーが必要です。これを実現するために、Apollo CLI には schema:download コマンドが提供されており、GraphQLサーバからスキーマをダウンロードできます。

Xcodeから Apollo CLI を使用するには、スクリプトの Run Build Phases をプロジェクトに追加します。

(1) 「TARGETS」の「RocketReserver」の「Build Phases」を選択。

画像6

(2) 「Build Phases」の「+」ボタンをクリックし、「New Run Script Phase」を選択。
「Build Phases」のリストの一番下に新規「Run Script」が追加されます。

画像7

(3) 新規「Run Script」を「Dependencies」と「Compile Sources」の間にドラッグ。

画像8

(4) 新規「Run Script」をダブルクリックして、「Apollo CLI」に名前変更。

画像9

(6) Apollo CLI Phaseを展開し、「Adding a code generation build step」の「Swift Package Manager Run Script」をペースト。
このスクリプトは、「スキーマ」を使用して「Apollo iOS SDK」がサーバとの対話に使用するコードを生成します。

画像10

(7) 貼り付けたスクリプトの最後の行をコメントアウトし、その下に次のスクリプトを追加。

"${SCRIPT_PATH}"/run-bundled-codegen.sh schema:download --endpoint="https://apollo-fullstack-tutorial.herokuapp.com/"

このスクリプトは、Apollo CLI の schema:download コマンドを実行します。

(8) プロジェクトをビルドして、スクリプトを実行。
AppDelegate.swiftと同じフォルダにスキーマ schema.json がダウンロードされています。

(9) schema.json をプロジェクトにドラッグ&ドロップ。

画像12

アプリケーションバンドルのサイズを減らすため、全てのターゲットのチェックをはずします。スキーマはターゲットに追加されるコードの生成に使用するため、それ自身をターゲットに追加する必要はありません。

画像11

7. GraphiQLでのクエリの確認

最も一般的なGraphQL操作は クエリ です。サーバのスキーマに準拠した形式で、データを要求します。 サーバの GraphiQL に戻ると、前に開いた「DOCS」で使用可能なクエリを確認できます。

詳細については、launches クエリをクリックしてください。

画像13

右側のパネルには、クエリ自体と、クエリが返すものに関する情報が表示されます。 この情報を使用して、最終的にアプリに追加するクエリを作成できます。

左側のテキスト領域に次の行を追加して、使用可能なlaunchesのリストを読み込むクエリを実行できます。

・(GraphiQL)

query LaunchList {
}

Apollo iOS SDK では、すべてのクエリに名前を付ける必要があります(GraphQLの仕様では必須ではありませんが)。今回のクエリ名は「LaunchList」です。

次に、クエリのカッコの間に、「la」と入力します。 オートコンプリートがポップアップします。

画像14

GraphiQL はクエリを作成および検証するための優れたツールであるため、変更を試すためにXcodeでプロジェクトを繰り返しビルドする必要はありません。

スキーマが示すように、launchesクエリはLaunchConnectionを返します。作成するクエリは、次のように、このLaunchConnectionのどのフィールドを返すかを正確に示します。

・(GraphiQL)

query LaunchList {
  launches {
    cursor
    hasMore
  }  
}

GraphiQLの再生ボタンを押してこのクエリを実行すると、ページの右側にJSONオブジェクトとして結果を返します。

画像15

このクエリは正常に実行されますが、Launchに関する情報は含まれていません。 これは、クエリに必要なフィールドが含まれていないためです。

次のように、idとsiteを取得するようにクエリを更新します。

・(GraphiQL)

query LaunchList {
  launches {
    cursor
    hasMore
    launches {
      id
      site
    }
  }  
}

もう一度クエリを実行すると、idとsiteも表示されます。

画像16

8. Xcodeでのクエリの準備 - LaunchList.graphql

クエリが正しいデータを読み込んでいるので、Xcodeに戻ります。

(1) メニュー「File → New → File」を選択し、テンプレート「Other → Empty」を選択し、Nextボタンを押す。

(2) 「次へ」をクリックして、ファイル名 LaunchList.graphql を指定し、「schema.json」と同じフォルダで保存。
以前と同様に、どのターゲットにも追加しないでください。

(3) GraphiQLから最終クエリをコピーし、LaunchList.graphql にペースト。

9. Xcodeでのコード生成 - API.swift

Xcodeでのクエリとスキーマが準備できたので、コード生成の実行を行います。

(1) 「Build Phases」の「Apollo CLI Run Script」に追加したコード(schema:downloadを含む)をコメントアウト。
スキーマはビルド時に変更されません。つまり、スキーマを再取得する必要はありません。

(2) 以前にコメント化したコード(codegen:generateを含む)のコメントアウトを解除。

(3) プロジェクトをビルド。
ビルドが完了すると、API.swift が schema.json と同じフォルダに表示されます。

(4) API.swiftをXcodeのプロジェクトにドラッグ。 

画像17

今回は、RocketReserverのターゲットに追加します。このファイルをアプリのバンドルに含めて、クエリを実行できるようにします。

画像18

10. API.swiftの確認

API.swift の確認を行います。

(1) API.swift を開いて確認。
ルートクラス LaunchListQuery を定義し、その下に多くのネストされた構造体があります。構造体をGraphiQLで返されたJSONデータと比較すると、構造が一致していることがわかります。これらの構造体には、クエリが要求するフィールドのプロパティのみが含まれます。

・API.swift (一部)

  public let operationDefinition: String =
    """
    query LaunchList {
      launches {
        __typename
        cursor
        hasMore
        launches {
          __typename
          id
          site
        }
      }
    }
    """

(2) LaunchList.graphql のidプロパティをコメントアウトして保存し、もう一度ビルド。
ビルドが完了すると、最も内側のLaunchには、組み込みの__typenameと要求されたサイトプロパティのみが含まれます。

・API.swift (一部)

  public let operationDefinition: String =
    """
    query LaunchList {
      launches {
        __typename
        cursor
        hasMore
        launches {
          __typename
          site
        }
      }
    }
    """

(3) idのコメントアウトを解除し、再構築してプロパティを復元。

11. Xcodeでのクエリ実行

API.swiftを使って、Xcodeでのクエリ実行を行うには、ApolloClientのインスタンス を生成します。 このインスタンスは、API.swiftのコードを受け取り、それを使用してサーバへのネットワーク呼び出しを行います。このインスタンスは、コードベースのどこからでもアクセスできるシングルトンにすることをお勧めします。

(1) 新規スクリプト Network.swift を作成し、Basic Client Creationのコードをコピー。
必ず「import Apollo」をファイル先頭に追加してください。

・Network.swift

import Apollo
import Foundation

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

(2) コードの「http://localhost:8080/graphql」を「https://apollo-fullstack-tutorial.herokuapp.com」に更新。

(3) AppDelegate.swiftapplication:didFinishLaunchingWithOptions に以下のコードを追加。

・AppDelegate.swift

Network.shared.apollo.fetch(query: LaunchListQuery()) { result in
    switch result {
    case .success(let graphQLResult):
        print("Success! Result: \(graphQLResult)")
    case .failure(let error):
        print("Failure! Error: \(error)")
    }
}

(4) アプリをビルドして実行。
CodeSandboxはGraphQLサーバを起動するのに数秒かかることがありますが、起動すると次のような応答が表示されます。

Success! Result: GraphQLResult<Data>(data: Optional(RocketReserver.LaunchListQuery.Data(resultMap: ["launches": Optional(["__typename": Optional("LaunchConnection"), "cursor": Optional("1550799900"), "hasMore": Optional(true), "launches": Optional([Optional(["__typename": Optional("Launch"), "id": Optional("94"), ....

この記事が気に入ったらサポートをしてみませんか?