Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。| iOSDC 2021
Search
nkmrh
September 18, 2021
Programming
6.5k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。| iOSDC 2021
nkmrh
September 18, 2021
Other Decks in Programming
See All in Programming
Spec Driven Development | AI Summit Lisbon
danielsogl
PRO
0
170
Signal Forms: Beyond the Basics @ngBaguette 2026 in Paris
manfredsteyer
PRO
0
230
「AIで開発し、AIを届ける」をEvalでつなぐ 〜AIネイティブに始めるプロダクト開発の実践〜 / Connecting "Develop with AI, deliver AI" with Eval
rkaga
4
4.7k
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
150
Semantic Version 単位で戦略を柔軟に変えて、パッケージアップデートを自動化する
daitasu
0
170
代数的データ型って何が嬉しいの? #frontend_phpcon_do
kajitack
8
3.3k
net-httpのHTTP/2対応について
naruse
0
450
Make SRE Operations Easier with Azure SRE Agent
kkamegawa
0
4.6k
Why Laravel apps break—Mastering the fundamentals to keep them maintainable
kentaroutakeda
1
340
不変条件と整合性境界—ビジネスが決める設計判断と実現パターン / Invariants and Consistency Boundaries
nrslib
13
3.5k
Webフレームワークの ベンチマークについて
yusukebe
0
150
GitHub Copilot CLIのいいところ
htkym
2
1.3k
Featured
See All Featured
Deep Space Network (abreviated)
tonyrice
0
170
Context Engineering - Making Every Token Count
addyosmani
9
950
Why Your Marketing Sucks and What You Can Do About It - Sophie Logan
marketingsoph
0
160
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
65
55k
The Spectacular Lies of Maps
axbom
PRO
1
790
New Earth Scene 8
popppiees
3
2.3k
Large-scale JavaScript Application Architecture
addyosmani
515
110k
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
2.8k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.9k
GitHub's CSS Performance
jonrohan
1033
470k
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
65
56k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
250
Transcript
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 「スタディサプリ」がFull SwiftUIを選択した 先に見えてきたもの。 Hajime NAKAMURA @_nkmrh iOSDC
Japan 2021
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 @_nkmrh / 中村 肇 ➔ iOS アプリエンジニア
➔ 2020年4月にQuipperに入社 ◆ 新規プロダクト開発 iOS担当 ◆ 小中高大学受験向けスタディサプリの一部運用を担当
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 「Full SwiftUI」 @main struct full_swiftuiApp: App {
var body: some Scene { WindowGroup { ContentView() } } }
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 Full SwiftUI を選択した背景 02
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 新規開発アプリの要件 講座一覧 授業動画 問題演習
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 新規開発アプリの要件 iOS 14 以降 プッシュ通知 App内課金 オンボーディング
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 技術検証 ➔ 想定するデザインのプロトタイプをSwiftUIで実装 ➔ Full SwiftUIでのアーキテクチャパターンの検証 ➔
UIKitを組み合わせる仕組みの検証
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 👀 見えてきたメリット 👍 02
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 アニメーション
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 要件 ➔ ボタンを押してリストに要素を追加/削除し、任意のアニメーションを適用 ◆ 追加時:左から右へスライドイン ◆ 削除時:拡大・縮小
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 追加 削除
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 Button("+1") { withAnimation { appendItem() } }
Button("-1") { withAnimation { removeItem() } } ボタンの実装 withAnimation(_:_:)メソッドのbody 引 数 の中で配列を操作することでアニメーション を適用します
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 List { ForEach(items, id: \.self) { item
in Row(item: item) .transition( AnyTransition.asymmetric( insertion: AnyTransition.slide.combined(with: AnyTransition.opacity), removal: .identity ) ) } } 追加時アニメーション AnyTransition.asymmetric(insertion:removal:)を使う と、Viewの挿入時と削除時で異なる Transitionを適用で きる
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 struct ScaleTransitionEffect: GeometryEffect { var animationFactor: CGFloat
var animatableData: CGFloat { get { animationFactor } set { animationFactor = newValue } } func effectValue(size: CGSize) -> ProjectionTransform { // 次スライドで実装 } } 削除時アニメーション animatableDataプロパティで 値 の 変 化に基づいたアニメーションを提 供す る
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 func effectValue(size: CGSize) -> ProjectionTransform { let
anchorPoint = CGPoint(x: size.width / 2, y: size.height / 2) // 2次関数を利用 `f(x) = ax^2 + bx + c` let scale = quadraticFunction(x: animationFactor, a: -6.8, b: 5.8, c: 1) return ProjectionTransform( CGAffineTransform.identity .translatedBy(x: anchorPoint.x, y: anchorPoint.y) .scaledBy(x: scale, y: scale) .translatedBy(x: -anchorPoint.x, y: -anchorPoint.y) ) } 削除時アニメーション
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 List { ForEach(items, id: \.self) { item
in Row(item: item) .transition( AnyTransition.asymmetric( insertion: AnyTransition.slide.combined(with: AnyTransition.opacity), removal: AnyTransition.modifier( active: ScaleTransitionEffect(animationFactor: 1), identity: ScaleTransitionEffect(animationFactor: 0) ) ) ) } } 削除時アニメーション
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 アニメーションまとめ ➔ withAnimation(_:_:)で変数の変化に合わせてア ニメーションを簡単適用 ➔ .transitionモディファイアでViewの追加/削除時の トランジションを適用
➔ GeometryEffectプロトコルを実装してViewにア フィン変換を適用
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 👀 見えてきたデメリット 😇 03
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 ライブラリの対応 ライフサイクル系メソッドの不安定な動作 ➔ Firebase の自動スクリーントラッキングが対応していない ◆ (firebase-ios-sdk
v8.2.0) ➔ onAppear()が予期しないタイミングで呼ばれる ◆ TabViewで非表示タブのView.onAppearが呼ばれたりする
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 class ViewModel: ObservableObject { @Published var isPresented
= false { didSet { if isPresented { logger?.screenEvent("second_screen") } else { logger?.screenEvent("first_screen") } } } ... } struct FirstScreen: View { @StateObject var viewModel = ViewModel() var body: some View { NavigationLink( destination: SecondScreen(), isActive: $viewModel.isPresented, label: { EmptyView() } ) ... } } スクリーントラッキングログ実装例
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 ➔ Firebaseの自動スクリーントラッキングログはSwiftUI未対応 ◆ (firebase-ios-sdk v8.2.0) ➔ Viewのライフサイクル系メソッドの挙動が不安定
◆ .onAppear() 見えてきたデメリットまとめ
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 👀 見えてきたtips 04
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 プログラムで画面遷移を制御する
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 APIと通信して条件に適した場合のみ、 一覧画面から詳細画面へプッシュ遷移したい 要件
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 基本のPush遷移実装 *https://developer.apple.com/tutorials/app-dev-training/creating-a-navigation-hierarchy private let items = ["Apple",
"Banana", "Cat", "Dog"] var body: some View { List { ForEach(items, id: \.self) { item in NavigationLink( destination: Text(item) ) { Text(item) } } } NavigationLinkと遷移先のView を設定するだけで完成
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 プログラムで画面遷移を制御したい ➔ チュートリアルのコードでは、ユーザーのタップ入力を受ける前提なので、プ ログラムでの遷移には使えない ➔ 通信が完了したら遷移する処理を実装するには、NavigationLinkの isActive引数を取るInitializer*を使う
*https://developer.apple.com/documentation/swiftui/navigationlink/init(_:isactive:destination:)-6xw7h
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 @State private var isSelected = false var
body: some View { ZStack { Button(action: { // 通信を行う模擬実装 DispatchQueue.main.asyncAfter(deadline: .now() + 1) { isSelected = true } }) { Text("Submit") } プログラムでの画面遷移方法 isActiveフラグのBinding先を定義 非同期処理が終わるとフラグをtrueに変更
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 @State private var isSelected = false var
body: some View { ZStack { NavigationLink( destination: Text(item), isActive: $isSelected) { EmptyView() } Button(action: { … } プログラムでの画面遷移方法 NavigationLinkを 設 置 して、isActiveフラグを isSelectedのStateにBindingさせる
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 struct Selection<T> { var isSelected: Bool var
item: T? { didSet { isSelected = item != nil } } … } 一覧画面から詳細画面への遷移 Selection ➔ 一覧画面で選択したアイテムを保持 ➔ 画面遷移のトリガー isActiveフラグのBinding先として定義 アイテムをセットしてフラグを更新
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 Itemを選択すると遷移する隠しNavigationLinkを作成 Selectionを使った画面遷移 @State private var selection =
Selection<String>(item: nil) @ViewBuilder private var navigationLinkIfPossible: some View { if let selectedItem = selection.item { NavigationLink( destination: Text(selectedItem), isActive: $selection.isSelected) { EmptyView() } } else { EmptyView() ... フラグが必ずtrueになるので自動的に遷移する アイテムがあった場合のみ、NavigationLinkを生成する
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 var body: some View { ZStack {
navigationLinkIfPossible List { ForEach(items, id: \.self) { item in Button(action: { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { selection = .init(item: item) } }) { Text("\(item)") } ... アイテムをセットして遷移をトリガー 隠しNavigationLinkを設置 非同期処理完了後、アイテムをセット
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 APIと通信して条件に適した場合のみ、 一覧画面から詳細画面へプッシュ遷移したい ➔ 隠しNavigationLinkとSelectionを組み合わせて実装 NavigationLinkをプログラムで制御するまとめ
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 Full SwiftUI で開発した所感 05
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 Full SwiftUI で開発した所感 ➔ UIの実装がスピードアップ ◆ レイアウトの記述がシンプル
◆ ライブプレビューで素早く動作確認が可能 ➔ 実装の変更やレビューがしやすい ➔ 開発体験が加速!🚀 ➔ ⚠ 今回紹介した事例の他、ハマりポイントや懸念はあるのでそこ は強いやっていき💪が必要
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 https://quipper.hatenablog.com/archive/category/Engineering-Native-iOS ➔ 「スタディサプリ」が React Native から卒業するまで、ある いは技術的負債への感謝と敬意
➔ iOS アプリ開発とユニットテスト ➔ iOS/Androidチーム合同でユニットテストクロスレビューを 行っている話 ➔ SwiftUIのディープリンク対応:プッシュ通知から画面遷移 する方法 本日お伝えした詳細や他のトピックを投稿しています!
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 選考・採用イベント ご応募方法 リクルートは仲間を積極募集中です!!! このページをご覧の方限定の特設応募サイトよりご登録ください 採用イベント カジュアル面談 まずは気
軽にカジュアルに話を してみたい 選考受験 スピーディーに選考でこれまでの 経験が活かせるか確認したい
#iosdc 「スタディサプリ」がFull SwiftUIを選択した先に見えてきたもの。 ご清聴ありがとうございました