Slide 1

Slide 1 text

Yappliにおける 
 パーミッション要求の 課題と改善

Slide 2

Slide 2 text

Speaker プロダクト開発本部 開発1部 iOSグループ iOSエンジニア 菅 直之 2022年9⽉からヤプリでiOSエンジニアとして 
 働いています 
 趣味は登⼭と将棋で、最近は電⼦ピアノにハマって います @Nao_RandD

Slide 3

Slide 3 text

01 02 03 04 05 iOSにおけるパーミッション要求 Yappliにおけるパーミッション要求 基盤改善における課題と今後実現したいこと 改善までの道のり まとめ

Slide 4

Slide 4 text

01 iOSにおける パーミッション要求

Slide 5

Slide 5 text

0 1 iOSにおけるパーミッション要求 iOSにおけるパーミッション要求 パーミッション要求とは? • アプリがデバイスの特定機能やデータに 
 アクセスするために必要となる 
 例:位置情報、カメラ、プッシュ通知など 
 • ユーザーがアプリにどのような特定機能‧ 
 アクセスを許可するかを明⽰的にコントロー ルできる

Slide 6

Slide 6 text

0 1 iOSにおけるパーミッション要求 iOSにおけるパーミッション要求 開発者に期待されること • 必要最低限のパーミッションのみを 
 要求すること 
 • プライバシーの側⾯でデータや 
 リソース利⽤の透明性を担保する https://developer.apple.com/design/human-interface-guidelines/privacy

Slide 7

Slide 7 text

0 1 iOSにおけるパーミッション要求 iOSにおけるパーミッション要求 パーミッション要求のダイアログ • ダイアログ表⽰処理はアプリで呼び出し 
 システムによって実⾏される 
 • 同時に複数呼び出すとダイアログが重複する 
 動作となりユーザー体験を損なう 
 • ホーム画⾯など要求処理が集中する場所では 
 呼び出しに配慮が必要になる

Slide 8

Slide 8 text

02 Yappliにおける パーミッション要求

Slide 9

Slide 9 text

0 2 Yappliにおけるパーミッション要求 Yappliにおけるパーミッション要求 Yappliはノーコードのアプリプラットフォーム • 650社以上で導⼊されており、約800アプリを提供している • アプリ要件に合わせて40種以上の機能がある

Slide 10

Slide 10 text

アプリごとのパーミッション • 同じプラットフォーム上でアプリごとに必要な 
 パーミッションが異なる 0 2 Yappliにおけるパーミッション要求 Yappli Yappliにおけるパーミッション要求

Slide 11

Slide 11 text

03 基盤改善における 課題と今後実現したいこと

Slide 12

Slide 12 text

1 2 基盤改善前の課題 今後実現したいこと • 事前説明画⾯の機能追加 03 基盤改善における課題と今後実現したいこと • 可読性‧保守性の低さ • 要求処理のボイラープレート化 • 同時に複数の要求を扱うのが難しい

Slide 13

Slide 13 text

パーミッション要求はシステム実⾏であり、 
 順次実⾏のためにコールバックで実装している コールバックの実⾏は開発者の責務となり、 
 呼ばないことによるコンパイルエラーは出ない 1 基盤改善前の課題 03 基盤改善における課題と今後実現したいこと 可読性‧保守性の低さ func hoge() { requestPermissionA { // ॏཁͳޙଓॲཧ } } func requestPermissionA( completion: @escaping () -> Void) { // PermissionAͷύʔϛογϣϯཁٻॲཧ // ... if isAuthorize { // ޙଓͷॲཧ͕࣮ߦ͞ΕΔ completion() } else { // Կ΋͠ͳ͔ͬͨ } } パーミッションに関わる処理はストア審査リジェクト、 機能提供できないなど⼤きな問題になる可能性が⾼い

Slide 14

Slide 14 text

パーミッションの種類によって 
 要求処理に必要な実装は異なる 機能単位で要求処理を別々に実装しており 処理として共通化されていない 1 基盤改善前の課題 03 基盤改善における課題と今後実現したいこと 要求処理のボイラープレート化 struct ModuleA { typealias Callback = (Bool) -> Void func requestLocationPermission( _ completion: @escaping Callback) { // Ґஔ৘ใͷύʔϛογϣϯཁٻॲཧ } func requestATTPermission(_ completion: @escaping Callback) { // ATT(AppTrackingTransparancy)ͷύʔϛογϣϯཁٻॲཧ } } struct ModuleB { typealias Callback = (Bool) -> Void func requestLocationPermission( _ completion: @escaping Callback) { // Ґஔ৘ใͷύʔϛογϣϯཁٻॲཧ } } 同じパーミッションの実装が 必要な機能ごとに増えていく

Slide 15

Slide 15 text

ホーム画⾯など同時に複数のパーミッション を組み合わせて要求したい場⾯がある 1 基盤改善前の課題 03 基盤改善における課題と今後実現したいこと 同時に複数の要求を扱うのが難しい func requestMultiPermission( _ completion: @escaping () -> Void) { requestLocationPermission { _ in requestATTPermission { _ in requestBluetoothPermission { _ in // ޙଓॲཧ } } } } 要求処理が増えるたびに ネストが深くなってしまう

Slide 16

Slide 16 text

それぞれのパーミッション要求前に事前説明が欲しい要望があった 2 今後実現したいこと 03 基盤改善における課題と今後実現したいこと 事前説明画⾯の機能追加 https://developer.apple.com/jp/design/human-interface-guidelines/privacy#Pre-alert-screens-windows-or-views パーミッション要求前にユーザーに対して、 
 アプリで⽤いる⽤途や提供する機能を説明する 画⾯のこと 許諾率の向上となりAppleも推奨している 事前説明画⾯とは?

Slide 17

Slide 17 text

2 今後実現したいこと 03 基盤改善における課題と今後実現したいこと 事前説明画⾯の機能追加 パーミッション要求 事前説明画⾯表⽰ ユーザーが選択後に 後続の処理

Slide 18

Slide 18 text

2 今後実現したいこと 03 基盤改善における課題と今後実現したいこと 事前説明画⾯の機能追加 パーミッション要求 事前説明画⾯表⽰ ユーザーが選択後に 後続の処理 パーミッション要求前後の処理の差し込みにも柔軟に対応できる実装を⽬指す

Slide 19

Slide 19 text

3つの課題 今後実現したいこと ⬜︎ 可読性‧保守性の低さ ⬜︎ 要求処理のボイラープレート化 ⬜︎ 同時に複数の要求を扱うのが難しい ⬜︎ 事前説明画⾯の機能追加 03 基盤改善における課題と今後実現したいこと ✅ 改善におけるチェックリスト

Slide 20

Slide 20 text

04 改善までの道のり

Slide 21

Slide 21 text

STEP 2 STEP 1 STEP 3 Swift Concurrency導⼊ インターフェースを共通化 04 改善までの道のり 要求処理のワークフロー化

Slide 22

Slide 22 text

STEP 2 STEP 1 STEP 3 Swift Concurrency導⼊ インターフェースを共通化 要求処理のワークフロー化 04 改善までの道のり

Slide 23

Slide 23 text

1 2 Swift Concurrencyのメリット 導⼊における考慮 • Swift Concurrencyについて • async/awaitによる可読性向上 • パーミッションごとの要求処理の差分 • Delegateパターンの吸収 04 改善までの道のり- STEP 1 Swift Concurrency導⼊ 課題 今後実現したいこと ⬜︎ 可読性‧保守性の低さ ⬜︎ 要求処理のボイラープレート化 ⬜︎ 同時に複数の要求を扱うのが難しい ⬜︎ 事前説明画⾯の機能追加 ✅ 改善におけるチェックリスト

Slide 24

Slide 24 text

Swift 5 . 5 から⾔語機能として登場した ⾮同期処理‧並列処理のコードを簡潔かつ安 全に記述できる async/awaitを使⽤することができる 1 Swift Concurrencyのメリット 04 改善までの道のり- STEP 1 Swift Concurrency導⼊ Swift Concurrencyについて https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/

Slide 25

Slide 25 text

async/awaitを⽤いることで⾮同期処理を 
 同期処理と同じような書き⽅で実装できる コールバックのネストが深い処理などを 
 可読性⾼く実装する助けになる 1 Swift Concurrencyのメリット 04 改善までの道のり- STEP 1 Swift Concurrency導⼊ async/awaitによる可読性向上 // ίʔϧόοΫͰͷ࣮ߦ foo { x in bar(x) { y in baz(y) { z in // ޙଓͷॲཧ } } } // async/awaitͰͷ࣮ߦ let x = await foo() let y = await bar(x) let z = await baz(y) パーミッション要求処理を async/awaitに落とし込んでいく

Slide 26

Slide 26 text

async/awaitが対応したものがAPIが 
 提供されていれば素直に実装する プッシュ通知のUNUserNoti fi cationなどは async/awaitで呼び出せるAPIがある requestAuthorization(for: ) 2 導⼊における考慮 04 改善までの道のり- STEP 1 Swift Concurrency導⼊ async/await の I/F が⽤意されている ⭕ func request() async -> Bool { do { let nc = UNUserNotificationCenter.current() return try await nc.requestAuthorization( options: [.badge, .sound, .alert]) } catch { // ΤϥʔϋϯυϦϯά } } https://developer.apple.com/documentation/usernoti fi cations/unusernoti fi cationcenter/1649527-requestauthorization

Slide 27

Slide 27 text

代表的なケース: Delegateでパーミッションのステータス変化を 
 検知する必要がある 例:Bluetoothや位置情報 そのままasync/awaitで置き換えられない 2 導⼊における考慮 04 改善までの道のり- STEP 1 Swift Concurrency導⼊ async/await の I/F が⽤意されていない ❌ class BluetoothPermission: NSObject, CBCentralManagerDelegate { private var centralManager: CBCentralManager? typealias Status = CBManagerAuthorization func request() { // ύʔϛογϣϯεςʔλεʹԠͯ͡ૣظϦλʔϯ // ... // CBCentralManagerΛΠϯελϯεԽ͢ΔͱμΠΞϩάදࣔ centralManager = CBCentralManager(delegate: self, queue: nil) } /// εςʔλε͕มߋ͞ΕΔͱݺ͹ΕΔDelegateϝιου func centralManagerDidUpdateState( _ central: CBCentralManager) { let status = CBManager.authorization // ύʔϛογϣϯεςʔλε͝ͱͷॲཧ } }

Slide 28

Slide 28 text

代表的なケース: Delegateでパーミッションのステータス変化を 
 検知する必要がある 例:Bluetoothや位置情報 そのままasync/awaitで置き換えられない 2 導⼊における考慮 04 改善までの道のり- STEP 1 Swift Concurrency導⼊ async/await の I/F が⽤意されていない ❌ https://developer.apple.com/documentation/swift/withcheckedcontinuation(function:_:) func request() async -> Status { // εςʔλεʹԠͯ͡ૣظϦλʔϯ // ... // CBCentralManagerΛΠϯελϯεԽ͢ΔͱμΠΞϩάදࣔ centralManager = CBCentralManager(delegate: self, queue: nil) return await withCheckedContinuation { [weak self] (continuation: Continuation) in guard let self = self else { return } self.continuation = continuation } } func centralManagerDidUpdateState( _ central: CBCentralManager) { let status = CBManager.authorization if status == .notDetermined { return } // resumeʹεςʔλεΛ౉͢ continuation?.resume(returning: status) continuation = nil } withCheckedContinuation を利⽤する

Slide 29

Slide 29 text

1 2 Swift Concurrencyのメリット 導⼊における考慮 • Swift Concurrencyについて • async/awaitによる可読性向上 • パーミッションごとの要求処理の差分 • Delegateパターンの吸収 04 改善までの道のり- STEP 1 Swift Concurrency導⼊ 課題 今後実現したいこと ✅ 可読性‧保守性の低さ ⬜︎ 要求処理のボイラープレート化 ⬜︎ 同時に複数の要求を扱うのが難しい ⬜︎ 事前説明画⾯の機能追加 ✅ 改善におけるチェックリスト

Slide 30

Slide 30 text

STEP 1 STEP 3 STEP 2 Swift Concurrency導⼊ インターフェースを共通化 要求処理のワークフロー化 04 改善までの道のり

Slide 31

Slide 31 text

1 2 共通化において実現したいこと 実装 • 異なるパーミッション要求処理の共通化 • Managerクラスを定義してI/Fも共通化 • 全体図 • コードに落とし込む 04 改善までの道のり- STEP 2 インターフェースを共通化 課題 今後実現したいこと ✅ 可読性‧保守性の低さ ⬜︎ 要求処理のボイラープレート化 ⬜︎ 同時に複数の要求を扱うのが難しい ⬜︎ 事前説明画⾯の機能追加 ✅ 改善におけるチェックリスト

Slide 32

Slide 32 text

種別によって別々に実装されている処理を 「パーミッション要求」として共通化したい 要求処理を含むプロトコルとして定義する 
 パーミッション種別ごとの処理に 呼び元が依存しないようにする 1 共通化において実現したいこと 04 改善までの道のり- STEP 2 インターフェースを共通化 異なるパーミッション要求を共通化する

Slide 33

Slide 33 text

パーミッション要求を共通化するのに合わせて 
 処理実⾏も1つに集約する 異なる機能から同時に複数のパーミッション要求 がされた場合にも順次実⾏する (前述のダイアログ重複を防⽌するため) 1 共通化において実現したいこと 04 改善までの道のり- STEP 2 インターフェースを共通化 Managerクラスを定義してI/Fも共通化 Manager I/F Managerにパーミッションを 渡した順序で順次実⾏する

Slide 34

Slide 34 text

1 共通化において実現したいこと 04 改善までの道のり- STEP 2 インターフェースを共通化 Managerクラスを定義してI/Fも共通化 Manager I/F Managerにパーミッションを 渡した順序で順次実⾏する 要件 同時に複数のパーミッション要求がされた場合 にも順次実⾏

Slide 35

Slide 35 text

1 共通化において実現したいこと 04 改善までの道のり- STEP 2 インターフェースを共通化 Managerクラスを定義してI/Fも共通化 機能A パーミッションA パーミッションB 機能B パーミッションC Manager I/F Managerにパーミッションを 渡した順序で順次実⾏する 要件 同時に複数のパーミッション要求がされた場合 にも順次実⾏

Slide 36

Slide 36 text

1 共通化において実現したいこと 04 改善までの道のり- STEP 2 インターフェースを共通化 Managerクラスを定義してI/Fも共通化 機能A パーミッションA パーミッションB 機能B パーミッションC Manager I/F Managerにパーミッションを 渡した順序で順次実⾏する 要件 同時に複数のパーミッション要求がされた場合 にも順次実⾏

Slide 37

Slide 37 text

キューでパーミッション要求処理をまとめて 管理して追加された順に実⾏する 1 共通化において実現したいこと 04 改善までの道のり- STEP 2 インターフェースを共通化 Managerクラスを定義してI/Fも共通化 機能A パーミッションB 機能B パーミッションC Manager I/F キューに追加 キュー 実⾏タスク パーミッションA

Slide 38

Slide 38 text

1 共通化において実現したいこと 04 改善までの道のり- STEP 2 インターフェースを共通化 Managerクラスを定義してI/Fも共通化 機能A パーミッションA 機能B パーミッションC Manager I/F キューに追加 キュー 実⾏タスク パーミッションB キューでパーミッション要求処理をまとめて 管理して追加された順に実⾏する

Slide 39

Slide 39 text

1 共通化において実現したいこと 04 改善までの道のり- STEP 2 インターフェースを共通化 Managerクラスを定義してI/Fも共通化 機能A 機能B パーミッションC Manager I/F キューに追加 キュー 実⾏タスク パーミッションB パーミッションA パーミッションAを実⾏中 (ダイアログ表⽰状態) キューでパーミッション要求処理をまとめて 管理して追加された順に実⾏する

Slide 40

Slide 40 text

1 共通化において実現したいこと 04 改善までの道のり- STEP 2 インターフェースを共通化 Managerクラスを定義してI/Fも共通化 機能A 機能B パーミッションC Manager I/F キューに追加 キュー 実⾏タスク パーミッションB パーミッションA パーミッションAを実⾏中 (ダイアログ表⽰状態) Managerが実⾏中に パーミッション要求処理 
 → キューへの追加 キューでパーミッション要求処理をまとめて 管理して追加された順に実⾏する

Slide 41

Slide 41 text

1 共通化において実現したいこと 04 改善までの道のり- STEP 2 インターフェースを共通化 Managerクラスを定義してI/Fも共通化 機能A 機能B Manager I/F キューに追加 キュー 実⾏タスク パーミッションB パーミッションA キューに追加され 順番に実⾏される パーミッションC キューでパーミッション要求処理をまとめて 管理して追加された順に実⾏する パーミッションAを実⾏中 (ダイアログ表⽰状態)

Slide 42

Slide 42 text

キューでパーミッション要求処理をまとめて 管理して追加された順に実⾏する 1 共通化において実現したいこと 04 改善までの道のり- STEP 2 インターフェースを共通化 Managerクラスを定義してI/Fも共通化 Manager I/F キューに追加 キュー 実⾏タスク パーミッションB パーミッションA キューに追加され 順番に実⾏される パーミッションC パーミッションAを実⾏中 (ダイアログ表⽰状態) この動作を満たすように 実装していく

Slide 43

Slide 43 text

パーミッション要求を共通化した PermissionRequestをプロトコルで定義する PermissionManagerにaddされたものを 順次実⾏していくようにする 2 実装 04 改善までの道のり- STEP 2 インターフェースを共通化 全体図

Slide 44

Slide 44 text

パーミッション要求をPermissionRequest としてプロトコルに定義 パーミッションステータスも PermissionStatusとしてプロトコルに定義 2 実装 04 改善までの道のり- STEP 2 インターフェースを共通化 コードに落とし込む(パーミッション要求)

Slide 45

Slide 45 text

2 実装 04 改善までの道のり- STEP 2 インターフェースを共通化 // MARK: PermissionRequest protocol PermissionRequest { func request() async -> PermissionStatus } // MARK: PermissionStatus protocol PermissionStatus {} パーミッション要求をPermissionRequest としてプロトコルに定義 パーミッションステータスも PermissionStatusとしてプロトコルに定義 コードに落とし込む(パーミッション要求)

Slide 46

Slide 46 text

2 実装 04 改善までの道のり- STEP 2 インターフェースを共通化 コードに落とし込む(Managerクラス) パーミッション要求(PermissionRequest) を実⾏するPermissionMangerを実装する

Slide 47

Slide 47 text

PermissionRequestをキューで管理する 外部からキューに追加するメソッドを公開する 実⾏中を⽰すステータスとタスクを 実⾏するメソッドを⽤意する 2 実装 04 改善までの道のり- STEP 2 インターフェースを共通化 コードに落とし込む(Managerクラス)

Slide 48

Slide 48 text

必要な要素 • PermissionRequestを⼊れるキュー • 実⾏中を管理できるOperationStatus • I/Fとして外部からキューに追加するadd() • キューにあるPermissionRequestを 
 順次実⾏するexecuteRequest() 2 実装 04 改善までの道のり- STEP 2 インターフェースを共通化 @MainActor final class PermissionManager { static let shared = PermissionManager() private init() {} private enum OperationStatus { case running case waiting } private var queue: [PermissionRequest] = [] private var operationStatus: OperationStatus = .waiting func add(request: PermissionRequest) { // ... // ࣮ߦதͰͳ͚Ε͹ executeRequest() } private func executeRequest() async { // ... } } コードに落とし込む(Managerクラス)

Slide 49

Slide 49 text

@MainActor final class PermissionManager { static let shared = PermissionManager() private init() {} private enum OperationStatus { case running case waiting } private var queue: [PermissionRequest] = [] private var operationStatus: OperationStatus = .waiting func add(request: PermissionRequest) { // ... // ࣮ߦதͰͳ͚Ε͹ executeRequest() } private func executeRequest() async { // ... } } 必要な要素 • PermissionRequestを⼊れるキュー • 実⾏中を管理できるOperationStatus • I/Fとして外部からキューに追加するadd() • キューにあるPermissionRequestを 
 順次実⾏するexecuteRequest() 2 実装 04 改善までの道のり- STEP 2 インターフェースを共通化 排他的なキューへのアクセスとなるように PermissionManagerを@MainActorにする コードに落とし込む(Managerクラス)

Slide 50

Slide 50 text

実装後 • PermissionManagerにパーミッション要求を 
 追加するだけで実⾏できる • 同時に複数の要求処理が追加されても順次実⾏で 
 パーミッション要求を⾏える 2 実装 04 改善までの道のり- STEP 2 インターフェースを共通化 // MARK: AsIs func requestMultiPermission() { requestLocationPermission { _ in requestATTPermission { _ in } } } // MARK: ToBe PermissionManager.shared.add( request: LocationPermission()) PermissionManager.shared.add( request: ATTPermission()) コードに落とし込む(Managerクラス)

Slide 51

Slide 51 text

実装後 • PermissionManagerにパーミッション要求を 
 追加するだけで実⾏できる • 同時に複数の要求処理が追加されても順次実⾏で 
 パーミッション要求を⾏える 2 実装 04 改善までの道のり- STEP 2 インターフェースを共通化 // MARK: AsIs func requestMultiPermission() { requestLocationPermission { _ in requestATTPermission { _ in } } } // MARK: ToBe PermissionManager.shared.add( request: LocationPermission()) PermissionManager.shared.add( request: ATTPermission()) 同時に呼び出す要求処理が 増えてもネストが深くならない コードに落とし込む(Managerクラス)

Slide 52

Slide 52 text

1 2 共通化において実現したいこと 実装 • 異なるパーミッション要求処理の共通化 • 組み合わせても使⽤できる仕組み • コードに落とし込む 04 改善までの道のり- STEP 2 インターフェースを共通化 課題 今後実現したいこと ✅ 可読性‧保守性の低さ ✅ 要求処理のボイラープレート化 ✅ 同時に複数の要求を扱うのが難しい ⬜︎ 事前説明画⾯の機能追加 ✅ 改善におけるチェックリスト

Slide 53

Slide 53 text

STEP 2 STEP 1 STEP 3 Swift Concurrency導⼊ インターフェースを共通化 要求処理のワークフロー化 04 改善までの道のり

Slide 54

Slide 54 text

1 2 要求処理を組み合わせるワークフロー 実装 • 要求処理に紐づいた処理も共通化したい • 全体図 • コードに落とし込む 04 改善までの道のり- STEP 3 要求処理のワークフロー化 課題 今後実現したいこと ✅ 可読性‧保守性の低さ ✅ 要求処理のボイラープレート化 ✅ 同時に複数の要求を扱うのが難しい ⬜︎ 事前説明画⾯の機能追加 ✅ 改善におけるチェックリスト

Slide 55

Slide 55 text

パーミッション要求と紐づいた処理も 
 PermissionMangerのI/Fで使えるようにしたい 1 要求処理を組み合わせるワークフロー 要求処理に紐づいた処理も共通化したい 04 改善までの道のり- STEP 3 要求処理のワークフロー化

Slide 56

Slide 56 text

パーミッション要求と紐づいた処理も 
 PermissionMangerのI/Fで使えるようにしたい ワークフローを利⽤できるようにする 1 要求処理を組み合わせるワークフロー 要求処理に紐づいた処理も共通化したい 04 改善までの道のり- STEP 3 要求処理のワークフロー化

Slide 57

Slide 57 text

パーミッション要求と紐づいた処理も 
 PermissionMangerのI/Fで使えるようにしたい ワークフローを利⽤できるようにする ワークフローとパーミッション要求を共通化した PermissionActionを新たに定義する 1 要求処理を組み合わせるワークフロー 要求処理に紐づいた処理も共通化したい 04 改善までの道のり- STEP 3 要求処理のワークフロー化

Slide 58

Slide 58 text

ワークフローとパーミッション要求を 共通化したPermissionActionを定義する 2 実装 全体図 04 改善までの道のり- STEP 3 要求処理のワークフロー化

Slide 59

Slide 59 text

ワークフローとパーミッション要求を 
 共通化したPermissionActionを定義する PermissionRequestはPermissionAction 
 に準拠する 2 実装 全体図 04 改善までの道のり- STEP 3 要求処理のワークフロー化

Slide 60

Slide 60 text

ワークフローとパーミッション要求を 
 共通化したPermissionActionを定義する PermissionRequestはPermissionAction 
 に準拠する PermissionManagerはPermissionAction 
 を順次実⾏する 2 実装 全体図 04 改善までの道のり- STEP 3 要求処理のワークフロー化

Slide 61

Slide 61 text

2 実装 // MARK: PermissionAction protocol PermissionAction { func execute() async } // MARK: PermissionRequest protocol PermissionRequest: PermissionAction { func request() async -> PermissionStatus } // Protocol ExtensionͰrequestΛݺͿ͜ͱͰ 
 // PermissionRequestଆ͸࣮૷ʹ͓͍ͯ 
 // PermissionActionͷ࣮૷Λҙࣝ͠ͳͯ͘ྑ͘͢Δ extension PermissionRequest { func execute() async { _ = await request() } } protocol PermissionStatus {} コードに落とし込む (PermissionRequestプロトコル) 04 改善までの道のり- STEP 3 要求処理のワークフロー化

Slide 62

Slide 62 text

2 実装 // PermissionRequest͸͜Ε·Ͱ௨Γͷ࣮૷Ͱྑ͍ʢલड़ʣ struct PermissionA: PermissionRequest { func request() async -> PermissionStatus { // PermissionAͷύʔϛογϣϯཁٻॲཧ } } struct PermissionB: PermissionRequest { func request() async -> PermissionStatus { // PermissionBͷύʔϛογϣϯཁٻॲཧ } } // ෳ਺ͷύʔϛογϣϯΛऔಘ͢Δύλʔϯ΋ϫʔΫϑϩʔʹͰ͖Δ struct MultiPermissionWorkflow: PermissionAction { func execute() async { let statusA = await PermissionA().request() let statusB = await PermissionB().request() } } コードに落とし込む 
 (ワークフローとパーミッション要求) 04 改善までの道のり- STEP 3 要求処理のワークフロー化

Slide 63

Slide 63 text

実装後 • 各機能からPermissionManagerに 
 パーミッション要求とワークフローを 
 キューに追加するのみで実⾏できる 2 実装 struct LocationPermission: PermissionRequest { func request() async -> PermissionStatus { // Ґஔ৘ใͷύʔϛογϣϯཁٻॲཧ } } // ෳ਺ͷύʔϛογϣϯΛཁٻ͢ΔϫʔΫϑϩʔ struct MultiPermissionWorkflow: PermissionAction { func execute() async { await BluetoothPermission().request() // Bluetoothͷཁٻॲཧޙʹඞཁͳॲཧ await ATTPermission().request() } } // ϫʔΫϑϩʔ΋୯ମͷύʔϛογϣϯ΋ಉ͡Α͏ʹݺͼग़ͤΔ PermissionManager.shared.add( action: MultiPermissionWorkflow()) PermissionManager.shared.add( action: LocationPermission()) 04 改善までの道のり- STEP 3 要求処理のワークフロー化 コードに落とし込む 
 (ワークフローとパーミッション要求)

Slide 64

Slide 64 text

// MARK: ࣄલઆ໌ը໘ΛؚΜͩύʔϛογϣϯཁٻ struct PreAlertScreenWorkflow: PermissionAction { func execute() async { // ͜͜ʹࣄલઆ໌ը໘ͷॲཧʢҐஔ৘ใʣ await LocationPermission().request() // ͜͜ʹࣄલઆ໌ը໘ͷॲཧʢATTʣ await ATTPermission().request() // ޙଓॲཧ } } // ݺͼݩ PermissionManager.shared.add( action: PreAlertScreenWorkflow()) 実装後 • 各機能からPermissionManagerに 
 パーミッション要求とワークフローを 
 キューに追加するのみで実⾏できる 2 実装 04 改善までの道のり- STEP 3 要求処理のワークフロー化 コードに落とし込む 
 (ワークフローとパーミッション要求) 事前説明画⾯処理もワークフローとして PermissionManagerで扱える

Slide 65

Slide 65 text

1 2 要求処理を組み合わせるワークフロー 実装 • 要求処理に紐づいた処理も共通化したい • 全体図 • コードに落とし込む 04 改善までの道のり- STEP 3 要求処理のワークフロー化 課題 今後実現したいこと ✅ 可読性‧保守性の低さ ✅ 要求処理のボイラープレート化 ✅ 同時に複数の要求を扱うのが難しい ✅ 事前説明画⾯の機能追加 ✅ 改善におけるチェックリスト

Slide 66

Slide 66 text

05 まとめ

Slide 67

Slide 67 text

取り組んだこと Yappliにおけるパーミッション要求処理の課題と改善 Swift Concurrency導⼊ パーミッション要求処理の共通化 要求処理のワークフロー化 現状の課題改善‧事前説明画⾯の下準備に! 05 まとめ

Slide 68

Slide 68 text

ご清聴ありがとうございました Yappliにおけるパーミッション要求の課題と改善