Slide 1

Slide 1 text

ɹɹ ©︎ hey, Inc SwiftUIとGraphQLで新規プロダクトの継続的な破壊に立ち向かう ヘイ株式会社 テクノロジー部門 モバイルアプリケーション本部 STORES レジ iOSエンジニア 横山 拓也(@_chocoyama) iOSDC2021

Slide 2

Slide 2 text

ɹ ɹ 自己紹介 横山 拓也 Yokoyama Takuya @_chocoyama(Twitter) 2013.04 ヤフー株式会社入社 2019.12 ヘイ株式会社入社

Slide 3

Slide 3 text

ɹ ɹ CD

Slide 4

Slide 4 text

ɹ ɹ CD Continuous Delivery? Compact Disk? Creative Director? Career Development? Cash Dispenser?

Slide 5

Slide 5 text

ɹ ɹ Continuous Destruction(継続的な破壊) ※ Ωϟονʔͩͱࢥͬͯ࢖͚ͬͨͩͰɺͲ͜ʹ΋ఆண͍ͯ͠ͳ͍ݴ༿Ͱ͢

Slide 6

Slide 6 text

ɹ ɹ アプリケーションの継続的な破壊 改善・変更 (破壊) フィードバック Ұ౓࡞ͬͯऴΘΓͰ͸ͳ͘ɺ ৗʹ࡞ͬͨ΋ͷΛյ͠ͳ͕ΒɺΑΓྑ͍΋ͷΛ໨ࢦ͢ ● UIの変更 ● APIの変更

Slide 7

Slide 7 text

ɹ ɹ STORES レジ ● 実店舗で使うPOSシステムを提供するiPadアプリ ● 初期実装から変更がなかった画面はほぼ0 ● 「実装→フィードバック→修正」のサイクルを 
 何度も高速に実行 ● リリースまでに少なくとも3回ほど 
 フルリニューアルしている

Slide 8

Slide 8 text

ɹ ɹ STORES レジの概要

Slide 9

Slide 9 text

heyが提供するSTORESプラットフォームの中の新しいプロダクト ネットショップと1つになった新しいPOSレジアプリ

Slide 10

Slide 10 text

ɹ ɹ 主な機能 ● お会計 ● 決済手段管理 ● 精算 ● 商品・在庫管理 ● ネットショップ連携 ϨδΞϓϦ ϨγʔτϓϦϯλʔ ΩϟογϡυϩΞʔ STORES ܾࡁ୺຤ όʔίʔυϦʔμʔ

Slide 11

Slide 11 text

ɹ ɹ 技術スタック ● フレームワーク:SwiftUI, Combine ● APIクライアント:Apollo-iOS(GraphQL) ● SDK:STORES 決済SDK

Slide 12

Slide 12 text

ɹ ɹ 破壊(変更)への対応

Slide 13

Slide 13 text

ɹ ɹ 変更に対応していく必要性 変更 Ձ஋ ࣮૷ ֬ೝ ϦϦʔε ユーザーに価値を与え続けるには、変更は避けられない ࢼߦࡨޡ ܧଓతͳվળ ϑΟʔυόοΫ ϑΟʔυόοΫ

Slide 14

Slide 14 text

ɹ ɹ 変更に対応しやすい → ユーザーに価値を届けやすい 変更に対応していく必要性

Slide 15

Slide 15 text

ɹ ɹ SwiftUI

Slide 16

Slide 16 text

ɹ ɹ SwiftUIの強み ● 宣言的シンタックスで 
 シンプルな記述ができる ● Previewを活用して 
 高速にUIの確認ができる

Slide 17

Slide 17 text

ɹ ɹ UIKitとの比較 1.レイアウト変更時の修正 2.表示の確認

Slide 18

Slide 18 text

ɹ ɹ 1. レイアウト変更時の修正 ᶃ DescriptionϥϕϧΛ࡟আ ᶄ Ձ֨දࣔͷฒͼͱҐஔมߋ

Slide 19

Slide 19 text

ɹ ɹ UIKit + AutoLayout 1.不要なViewを削除 2.Viewが消えたことによる不整合を修正 3.位置変更するViewのAutoLayout貼り直し 4.Outlet接続関連のコードを削除 ࡟আ Ҡಈ

Slide 20

Slide 20 text

ɹ ɹ SwiftUI ① 削除 ② VStackの 外に移動 ③ HStackを削除、 Textの順番を逆に Before After

Slide 21

Slide 21 text

ɹ ɹ レイアウト変更のしやすさ UIKit+AutoLayout SwiftUI 依存ファイル 多い 少ない 修正ステップ 複雑 単純 安全性 クラッシュの恐れ コンパイラで保証 特有のスキルが必要 素早く安全に対応可能

Slide 22

Slide 22 text

ɹ ɹ 表示の確認 •「UIの変更→表示確認」のサイクルの速さは、開発効率に直結する •複数の表示パターンがあるUIの場合、特に確認コストがかかる

Slide 23

Slide 23 text

ɹ ɹ なんか違う… UIKit + AutoLayout •表示が実行時と異なる場合がある •確認はアプリを起動して対象画面に遷移する •1度に1パターンしか確認できない •修正と起動のサイクルを繰り返す可能性がある このパターン はOK 全部確認 できた! मਖ਼ɾىಈ मਖ਼ɾىಈ मਖ਼ɾىಈ

Slide 24

Slide 24 text

ɹ ɹ SwiftUI + Xcode Preview •アプリの起動が必要ない •修正内容が即時で確認できる •任意の状態を再現したPreviewの実装を残せる •実装とPreviewを1対多の状態にできる •(コードだけでも表示イメージがしやすい) 一気に 全部確認 できた! मਖ਼ɾىಈ

Slide 25

Slide 25 text

ɹ ɹ SwiftUI + Xcode Preview

Slide 26

Slide 26 text

ɹ ɹ 表示確認のしやすさ UIKit+AutoLayout SwiftUI アプリの起動 必要あり 必要なし パターンごとの確認 毎回起動 1度のPreview実行 継続的な利用 できない できる 高コスト 低コスト

Slide 27

Slide 27 text

ɹ ɹ デザインシステムに則った 共通UIライブラリを作成 STORES レジでの変更への取り組み •デザインチームが独自のデザインシステムを構築 •アプリデザインはこれをベースに画面設計 変更に耐え得る 仕組みとして

Slide 28

Slide 28 text

ɹ ɹ デザインシステム = デザイン原則をまとめたスタイルガイドやその実装などの仕組み 以下のような価値を生む ● 一貫性のあるIFでユーザー体験を向上 ● デザイナーとエンジニア間の共通言語を確立 ● 局所最適されたデザインを抑制し、デザイン・開発コストを削減 デザインシステムとは

Slide 29

Slide 29 text

ɹ ɹ 共通UIコンポーネントの例 ※ Preview༻ͷViewΛ࣮ߦ࣌ʹݺͼग़ͯ͠ɺUIΧλϩάͷΑ͏ͳػೳΛ࡞͍ͬͯ·͢

Slide 30

Slide 30 text

ɹ ɹ •汎用的に使えるUIを切り出し •→ 切り出し単位はデザインシステムに合わせるだけ •デザイナーとエンジニアの画面構築工程が揃う •→ パーツを組み合わせるだけで画面ができる •アプリ内 SwiftPackageManager で対象コンポーネントを管理 •→ 依存の切り出し •→ ビルドやPreview実行の高速化 共通UIライブラリの作成 σβΠϯγεςϜʢ໊শඇެ։ʣ σβΠϯγεςϜʢ໊শඇެ։ʣ

Slide 31

Slide 31 text

ɹ ɹ 実装 σβΠϯ γεςϜ໊ දࣔύλʔϯΛ PreviewͰ໢ཏ σβΠϯ γεςϜ໊ σβΠϯ γεςϜ໊ σβΠϯ γεςϜ໊ σβΠϯ γεςϜ໊ σβΠϯ γεςϜ໊ ※ σβΠϯγεςϜ໊͸ඇެ։

Slide 32

Slide 32 text

ɹ ɹ 利用 σβΠϯ γεςϜ σβΠϯ γεςϜ σβΠϯ γεςϜ → UI作成のコストが激減 σβΠϯ γεςϜ σβΠϯ γεςϜ σβΠϯ γεςϜ σβΠϯ γεςϜ

Slide 33

Slide 33 text

ɹ ɹ •レイアウトの変更が容易 
 → 壊しては作るといった繰り返しを気軽にできる •UIの表示確認を効率的にできる 
 → 実装スピードが上がるだけでなく、継続的なメンテナンスにも活用できる SwiftUIの変更しやすさ UI関連の変更にコストがかからない

Slide 34

Slide 34 text

ɹ ɹ GraphQL

Slide 35

Slide 35 text

ɹ ɹ GraphQLとは •API用のクエリ言語 •スキーマによってIFの型が厳密に決まっている

Slide 36

Slide 36 text

ɹ ɹ REST APIとの比較 GraphQL REST 型付け 強い 弱い エンドポイント 1つ 複数 取得リクエスト Query GET 更新リクエスト Mutation POST/PUT/ PATCH/DELETE

Slide 37

Slide 37 text

ɹ ɹ GraphQLの強み 1.取得するデータをクライアントが決められる 2.少ないリクエストで複数のリソースにアクセスできる

Slide 38

Slide 38 text

ɹ ɹ 1. 取得するデータをクライアントが決められる ϦΫΤετ Ϩεϙϯε Ұ୴શσʔλΛऔಘͯ͠ɺඞཁͳ΋ͷ͚ͩ࢖͏ → 必要なデータだけ取得する

Slide 39

Slide 39 text

ɹ ɹ 2. 少ないリクエストで複数のリソースにアクセスできる Φʔμʔৄࡉը໘ アイテム情報 を追加したい

Slide 40

Slide 40 text

ɹ ɹ REST API の場合 Orders Items APIリクエストが増える Orders itemIds

Slide 41

Slide 41 text

ɹ ɹ REST API の場合 Orders Items APIリクエストが増える Orders itemIds 追加の通信処理が必要になり、実装が複雑化する OrdersAPIͷ݁ՌΛݩʹɺ ItemsAPIΛୟ͖௚͢ OrdersAPIΛୟ͚ͩ͘

Slide 42

Slide 42 text

ɹ ɹ GraphQL の場合 Orders Items APIリクエストが増えない Orders itemIds

Slide 43

Slide 43 text

ɹ ɹ GraphQL の場合 Orders Items APIリクエストが増えない Orders itemIds 取得したいデータを追加で指定するだけ

Slide 44

Slide 44 text

ɹ ɹ GraphQL の場合 Orders Items APIリクエストが増えない Orders itemIds 取得できるデータが増えるだけ

Slide 45

Slide 45 text

ɹ ɹ GraphQL の場合 Orders Items APIリクエストが増えない Orders itemIds 同時に複数のQueryを投げられる

Slide 46

Slide 46 text

ɹ ɹ STORES レジでの変更への取り組み 1.APIに変更が入ったことの自動検知(独自実装) 2.APIのIFとなるSwiftコードの自動生成(Apollo-iOSの機能を活用)

Slide 47

Slide 47 text

ɹ ɹ 1. APIに変更が入ったことの自動検知(独自実装) Appエンジニア 想定したデータが取得できません BEエンジニア ドキュメントの更新漏れてました BEエンジニア API完成しました Appエンジニア APIがエラーになります BEエンジニア 変更の共有忘れてました 人力での変更管理には限界がある ケース1 ケース2

Slide 48

Slide 48 text

ɹ ɹ 1. APIに変更が入ったことの自動検知(独自実装) 旧スキーマ ファイル 新スキーマ ファイル App BE 旧スキーマ ファイル 新スキーマ ファイル ࠩ෼νΣοΫ 自動でSlackに変更通知 →共有漏れや不正な定義の参照を防ぐ GitHub Bitrise

Slide 49

Slide 49 text

ɹ ɹ 2. APIのIFとなるSwiftコードの自動生成(Apollo-iOSの機能を利用) スキーマ ファイル BE クエリ ファイル スキーマ ファイル ੩తղੳ Swiftコードを自動生成 →BEとAppでAPI定義が同期される Local Apollo GitHub

Slide 50

Slide 50 text

ɹ ɹ •取得したいデータが増えても「追加リクエスト不要」で「IFの型が自動生成される」 
 → 意図した変更に対応しやすい •API側の変更をシステム的に追従可能なので、人力のコミュニケーションに頼る必要がない 
 →意図していない変更にも対応しやすい データ関連の変更にコストがかからない GraphQLの変更しやすさ

Slide 51

Slide 51 text

ɹ ɹ •SwiftUIを利用 → UI関連の変更にコストがかからなくなる •GraphQLを利用 → データ関連の変更にコストがかからなくなる SwiftUIとGraphQLのまとめ ユーザーに価値を届けやすくなる 変更に対応しやすくなる

Slide 52

Slide 52 text

ɹ ɹ SwiftUI + GraphQL

Slide 53

Slide 53 text

ɹ ɹ •Viewとセットで、必要なデータをQuery(Fragment)で用意する •UIレイアウトと参照データの定義を1対1で宣言しておく(データ取得も宣言的に行う) •SwiftUIのリアクティブなレンダリング機構と、 
 Apollo-iOSのコード生成+キャッシュ機構を組み合わせて実現 •レイアウト定義とGraphQL定義で実装がほぼ完結する Fragment Colocation(的なこと) ※ STORES ϨδͰ͸ಋೖ͍ͯ͠·ͤΜ

Slide 54

Slide 54 text

ɹ ɹ サンプル画面

Slide 55

Slide 55 text

ɹ ɹ 必要な実装コードのすべて SwiftUI GraphQL 1ର1

Slide 56

Slide 56 text

ɹ ɹ 必要な実装コードのすべて データの 参照 Query Mutation SwiftUI GraphQL 1ର1

Slide 57

Slide 57 text

ɹ ɹ 実装サンプル(取得データの反映) 1. Queryを発行 →内部キャッシュが変更されると自動呼び出し 2. 再レンダリング 3. 状態に応じた表示

Slide 58

Slide 58 text

ɹ ɹ 実装サンプル(データの更新) 2. キャッシュの更新を検知 (Queryでオーダーノートを参照→それが更新されると呼び出される) 1. Mutationを発行 (オーダーノートを更新) 3. 再レンダリング 4. オーダーノートの更新

Slide 59

Slide 59 text

ɹ ɹ 実装サンプル データとUIが同期された状態に

Slide 60

Slide 60 text

ɹ ɹ PreviewでのAPIのモック Previewでのみ、 カスタムのEnvironmentValueをセット

Slide 61

Slide 61 text

ɹ ɹ Continuous Destruction(継続的な破壊)

Slide 62

Slide 62 text

ɹ ɹ •SwiftUI •度重なる変更に対して、UIKitでは実現できないスピードで対応を進められた •OSバージョンの変更に対してはまだ弱い •デザインシステムの実装で開発が効率化したが、デザインシステム自体への変更対応は現状最適化できていない •GraphQL •要件や仕様の変更によって、API実装部分が壊れることがほとんどなかった •自動化の仕組みにより、変更に安全に追従していくことができた •開発フローも含めた最適化なども検討していきたい(スキーマファースト開発など) 振り返り と 今後の展望

Slide 63

Slide 63 text

ɹ ɹ ありがとうございました! SwiftUIと GraphQL最高!