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.6k
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
LLM本来の能力を解き放つサンドボックス技術とAI民主化への適用
yukukotani
3
4.6k
LLMによるContent Moderationの本番運用の裏側と品質担保への挑戦
suikabar
3
800
技術的負債解消で開発者の未来を開く- AIの力でコード刷新
kmd2kmd
0
120
エンジニアと一緒にテストコードの設計と実装を改善した話
mototakatsu
0
230
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
200
jQueryをバージョンアップする前に使いたいjQuery Migrate
matsuo_atsushi
0
610
A2UI という光を覗いてみる
satohjohn
1
170
Mujeres en SEO Summit 2026 - Greatest Disaster Hits en Web Performance
guaca
0
210
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
220
ローカルLLMでどこまでコードが書けるか -拡張版 / How much code can be written on a local LLM Extended
kishida
12
4.5k
コンテキストの使い捨てをやめる — ビジネスルール駆動開発と miko —
ioki
0
250
dRuby over BLE
makicamel
2
400
Featured
See All Featured
What's in a price? How to price your products and services
michaelherold
247
13k
AI Search: Where Are We & What Can We Do About It?
aleyda
0
7.6k
For a Future-Friendly Web
brad_frost
183
10k
How GitHub (no longer) Works
holman
316
150k
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
200
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
160
Context Engineering - Making Every Token Count
addyosmani
9
990
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
230
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
0
330
4 Signs Your Business is Dying
shpigford
187
22k
Unsuck your backbone
ammeep
672
58k
How STYLIGHT went responsive
nonsquared
100
6.2k
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を選択した先に見えてきたもの。 ご清聴ありがとうございました