Slide 1

Slide 1 text

Swiftでもapollo-iosで快適にGraphQL 導入と実装ノウハウ 株式会社スペースマーケット ymanya

Slide 2

Slide 2 text

2 自己紹介 Yuki Manya( @ymanya ) スペースマーケットでフロントエンドチームリーダー兼 Webフロントエンド・iOSアプリエンジニアをしています。 前職はSIerでQAとか新規事業の開発とかやってました。

Slide 3

Slide 3 text

3 今日話すこと/話さないこと ● 話すこと ○ iOS開発でGraphQLを扱うために入れた apollo-iosがよかったこと ○ 実装する前に知っておきたかった実装ノウハウ ● 話さないこと ○ GraphQL自体について

Slide 4

Slide 4 text

4 スペースマーケットって?

Slide 5

Slide 5 text

5 スペースマーケットとは 時間貸し・宿泊スペースを検索・予約できるサービスです

Slide 6

Slide 6 text

6 ざっくりとしたサービスの構成図 クライアントサイド サーバサイド スマートフォン アプリ API(REST/GraphQL) 移行中 Web フロントエンド (各サービスごと)

Slide 7

Slide 7 text

7 現在GraphQLに絶賛移行中で、 昨年末に高速化の一環として検索APIをGraphQL化!

Slide 8

Slide 8 text

8 というわけで、iOSアプリでもその恩恵を受けるため、GraphQLを 引けるようにすべく、導入&実装を行いました

Slide 9

Slide 9 text

9 ライブラリの導入

Slide 10

Slide 10 text

10 apollo-iosの導入 ● GraphQLを扱うライブラリとして apollo-iosを導入しました ○ 弊社ではすでにWebフロントエンド/バックエンドで導入実績があったこと ○ 導入するにあたってのツール類が揃っており、便利そうだったこと ● 大抵のライブラリと同様に、 CarthageとCocoaPodsで導入できます ○ https://www.apollographql.com/docs/ios/installation

Slide 11

Slide 11 text

11 schema.json取得 ● Apolloのツールを使うことで GraphQLスキーマの取得や、クエリから Swiftファイルを生成するのが コマンドで簡単にできるようになります ● ツールはnpmパッケージとして提供されているので node.jsなどが必要となります npm init # 以下対話的に聞かれるのでプロジェクトに合わせて入力ください ... # Apolloのツールをインストール npm install --save-dev [email protected] # ENDPOINTで指定したGraphQLサーバーからスキーマファイルをダウンロードし、 schema.jsonとして保存します # このスキーマファイルがクエリやミューテーションでどういう値を受け付けるかを持っている ので、コード生成のときに使用されます # なお、最新版だと公式 Docsと少しコマンドが違うので注意です npx apollo service:download --endpoint=$ENDPOINT schema.json

Slide 12

Slide 12 text

12 schema.jsonとGraphQLクエリからSwiftファイルを生成 ● 例えば以下のようなスペースを検索するクエリを .graphqlファイルで保存します query searchRooms( $page: Int, ) { searchRooms( page: $page, ) { results { id name } } }

Slide 13

Slide 13 text

13 schema.jsonとGraphQLクエリからSwiftファイルを生成 ● このクエリを実行できる Swiftコードをapolloのツールからコマンド一つで生成します # クエリやミューテーションが書かれた graphqlファイルとスキーマファイルから、 Swiftのコードを生成しま す # namespace指定することで生成されるコードの enumの名前を任意にすることができます。 npx apollo client:codegen ./GraphQLAPI.swift --target=swift --queries=./*.graphql --localSchemaFile=./schema.json --namespace=GraphQLAPI

Slide 14

Slide 14 text

14 schema.jsonとGraphQLクエリからSwiftファイルを生成 ● GraphQLスキーマの情報から、 Swiftコード上で も型定義されており、個人的には JavaScriptより もプログラミングしやすかったです ● もちろん、GraphQLのEnumeration typesも Swiftのenumに変換されます ● すごく簡略化していますが、上のクエリはこんな 感じで変換されます public enum GraphQLAPI { ... public final class SearchRoomsQuery: GraphQLQuery { ... public var page: Int? ... public struct Data: GraphQLSelectionSet { ... public struct SearchRoom: GraphQLSelectionSet { ... public var results: [Result?]? { ... } public struct Result: GraphQLSelectionSet { ... public var id: Int? { get { return resultMap["id"] as? Int } set { resultMap.updateValue(newValue, forKey: "id") } } ... } ... } ... } ... } ... }

Slide 15

Slide 15 text

15 schema.jsonとGraphQLクエリからSwiftファイルを生成 ● GraphQLAPI.swiftをプロジェクトに読み込めば、 Swiftコードからは以下のような形で呼び出せる 状態になります // namespace内にquery searchRoomsがSearchRoomsQueryとして定義されているので、これを生成 します let searchRoomsQuery = GraphQLAPI.SearchRoomsQuery() // クエリに渡すvariablesもclass変数として定義されているので、以下のようにセットできます searchRoomsQuery.page = 2

Slide 16

Slide 16 text

16 ApolloClientを生成し、あとはクエリを実行する ● 以下のようなコードで ApolloClientを生成 し、あとはクエリを実行します ● 実際のコードではシングルトンで Apolloク ライアントを保持するようにしました // 追加したいHTTPヘッダがあれば var apiHeaders: [AnyHashable : Any] = [ "Accept-Language": NSLocale.current.identifier, "Accept-Timezone": NSTimeZone.default.identifier ] let configuration: URLSessionConfiguration = .default configuration.httpAdditionalHeaders = apiHeaders // GRAPHQL_ENDPOINTに接続するApolloクライントを生成 let graphqlClient = ApolloClient(networkTransport: HTTPNetworkTransport(url: URL(string: GRAPHQL_ENDPOINT)!, configuration: configuration)) // 先程のクエリとクライアントでこのようにデータ取得ができます graphqlClient.fetch(query: searchRoomsQuery) { (result, error) in guard let rooms = result?.data?.searchRooms?.results, error == nil else { // エラー時の処理 print(error) return } // 正常に取得できた時の処理 print(rooms) }

Slide 17

Slide 17 text

17 実装ノウハウ

Slide 18

Slide 18 text

18 関数で引き渡す可能性がある部分はfragment化する

Slide 19

Slide 19 text

19 fragmentとは ● GraphQL自体が機能として持つ fragmentは、クエリ の一部を共通化できる仕組みです ● スペース情報をフラグメントとして持てば、検索やお 気に入りでの同じスペース情報を取得する場合でも 再利用できる、といった形です query searchRooms( $page: Int, ) { searchRooms( page: $page, ) { results { ...roomFragment } } } fragment roomFragment on Room { id name }

Slide 20

Slide 20 text

20 fragment有り無しで書いてみた例 // fragmentを使わなかった場合 func configure(rooms: [GraphQLAPI.SearchRoomsQuery.Data.SearchRoom.Result]) { ... } // fragmentを使った場合 func configure(rooms: [GraphQLAPI.RoomFragment]) { ... }

Slide 21

Slide 21 text

21 fragment化のメリット ● UIの構築メソッドなどに fragment単位で渡すことができ、別のクエリで参照している場合でも使い 回せる ○ 公式ドキュメントにも記載がありますが、 fragment化しておくことで、それは別で structとして 定義されるようになる ● そして何よりコードが読みやすい!

Slide 22

Slide 22 text

22 fragment化のデメリット ● fragment化した箇所で例えば on Roomで指定したRoom部分の名称についてサーバー側の変更 があると、データ取得に失敗してしまうこともあるので、注意が必要

Slide 23

Slide 23 text

23 開発に便利なツール

Slide 24

Slide 24 text

24 開発に便利なツール ● XcodeでGraphQLファイルのシンタックスハイライトしてくれる Apollo製のプラグイン ○ https://github.com/apollographql/xcode-graphql ● GraphiQL / Insomnia ○ スキーマを確認しながら、クエリを書き、その場で叩くこともできるツール ○ GraphiQLはブラウザの拡張機能のほか スタンドアロンアプリ もあります ○ REST APIも叩けるInsomniaも弊社では人気です ● ビルド時に自動的にクエリファイルから Swiftコードを生成する設定もあります ○ https://www.apollographql.com/docs/ios/installation.html#adding-build-step ○

Slide 25

Slide 25 text

25 まとめ

Slide 26

Slide 26 text

26 まとめ ● まだ移行段階ではありますが、GraphQL側に寄せたいと思えるほどには快適! ○ GraphQLのメリットである、クライアント側が必要な情報だけをまとめて取得できる 点はもちろんメリット ○ 加えて、型のあるSwiftではGraphQL側で定義されていることにより、 Optional含め て型定義されたコードが生成される点も魅力的! ● GraphQL導入される場合には、クライアントライブラリとして apollo-iosはおすすめで す!

Slide 27

Slide 27 text

27 We're Hiring! ● スペースマーケットでは全方位で絶賛採用活動中です! ○ 特にAndroidアプリエンジニア・Webフロントエンドエンジニア・バックエンドエンジニ アなど ○ 興味のある方は「Wantedly スペースマーケット」で検索orQRコード読取!

Slide 28

Slide 28 text

No content