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.7k
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
tsc.rip を支える技術 / Kyoto.なんか #8
susisu
0
170
リアルな遅延を測る仕様
kota_yata
1
130
まずはプロンプトガイドを読もう、話はそれからだ
kiakiraki
1
210
異なる設計思想のフレームワークを経験して得た学び
amekuhideki
1
490
引き算の組織 ― アウトカムとAIに全振りするために辞めたこと ― / Organization by Subtraction
hirokiyamamoto14
PRO
0
300
いまどきの Codex で開発する visionOS アプリの開発スタイルについて
karad
0
160
私のClaude Code活用法 (個人開発編) - PHPerKaigi mini #4(2026/08/24)
panda_program
1
160
Go 1.27からのGODEBUG / Go 1.27 リリースパーティ #go127party
mazrean
0
210
【デモ】Kiroで体験する仕様駆動開発|設計からコーディングまでAIと進める開発フロー
cmkudo
0
390
Dockerfile CMD for Node.js
grazie1999
0
120
言葉の格闘技のススメ~紙とペンと言葉から始める、キャリアの描き方~
progresscicada
2
170
コンパウンドプロダクト開発のためのローカルプロセスマネージャー再発明 #layerxgo
izumin5210
0
340
Featured
See All Featured
Docker and Python
trallard
47
4.1k
How to train your dragon (web standard)
notwaldorf
97
6.8k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
4.5k
Paper Plane (Part 1)
katiecoart
PRO
1
10k
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
210
Abbi's Birthday
coloredviolet
3
9.5k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.1k
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
660
Design in an AI World
tapps
1
290
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
440
ラッコキーワード サービス紹介資料
rakko
1
4.5M
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を選択した先に見えてきたもの。 ご清聴ありがとうございました