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
RxSwiftでMVVMパターン
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
tofu_san0000
June 08, 2019
Technology
0
380
RxSwiftでMVVMパターン
Web技術勉強会#02のLT資料です。
RxSwift+MVVMでシンプルなログインフォームを例として解説しました。
tofu_san0000
June 08, 2019
Tweet
Share
More Decks by tofu_san0000
See All by tofu_san0000
Rxのストリームを 感じるために大切なこと
tofu_san0000
0
110
Other Decks in Technology
See All in Technology
kintone開発のプラットフォームエンジニアの紹介
cybozuinsideout
PRO
0
860
参考スライド:AI_Driven_Engineering_Redefined
creationline
0
100
トップマネジメントとコンピテンシーから考えるエンジニアリングマネジメント
zigorou
4
830
Claude Codeの進化と各機能の活かし方
oikon48
21
12k
類似画像検索モデルの開発ノウハウ
lycorptech_jp
PRO
5
1.1k
複数クラスタ運用と検索の高度化:ビズリーチにおけるElastic活用事例 / ElasticON Tokyo2026
visional_engineering_and_design
0
130
事例に見るスマートファクトリーへの道筋〜工場データをAI Readyにする実践ステップ〜
hamadakoji
1
280
Oracle Database@AWS:サービス概要のご紹介
oracle4engineer
PRO
3
1.7k
[JAWSDAYS2026]Who is responsible for IAM
mizukibbb
0
370
作りっぱなしで終わらせない! 価値を出し続ける AI エージェントのための「信頼性」設計 / Designing Reliability for AI Agents that Deliver Continuous Value
aoto
PRO
2
270
Evolution of Claude Code & How to use features
oikon48
1
580
8万デプロイ
iwamot
PRO
2
230
Featured
See All Featured
Color Theory Basics | Prateek | Gurzu
gurzu
0
240
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
210
Balancing Empowerment & Direction
lara
5
940
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
480
Skip the Path - Find Your Career Trail
mkilby
1
75
First, design no harm
axbom
PRO
2
1.1k
Evolving SEO for Evolving Search Engines
ryanjones
0
150
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.7k
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
140
Facilitating Awesome Meetings
lara
57
6.8k
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
290
Side Projects
sachag
455
43k
Transcript
RxSwiftでMVVMパターン 1 @Web技術勉強会#02 2019/06/08(土) @tofu_san0000
2 @tofu_san0000 豆腐: 木綿派 エディタ: XCode、VSCode、Vim、PHPStorm 言語: Swift、PHP、Vue.js
• RxSwift概要 • MVVMパターン概要 • ViewModel作成 • ViewController作成 • まとめ
3 アジェンダ
• RxSwift概要 • MVVMパターン概要 • ViewModel作成 • ViewController作成 • まとめ
4 アジェンダ
• 各言語で実装されている「Rx」のSwift向けライブラリ • RxKotlin、RxJava、RxJS、RxPHPなどがある • Rx ◦ Reactive Programming ◦
Extensions 5 RxSwift概要
• Observable • Disposable • Operator • Scheduler • etc
... 6 RxSwift概要>メソッド
• 監視することができる対象 • 監視してデータ(イベント)を受け取ることができる • Observableはストリーム(Stream)とも呼ぶ 7 RxSwift概要>メソッド>Observable Observable.of(“a”, “b”,
“c”) .subscribe( // 処理 )
• next ◦ データが正しく流れてきたというイベント • error ◦ エラー(例外)で異常停止した時に流れるイベント • completed
◦ 完了時に発生するイベント 8 RxSwift概要>メソッド>Observable>イベント next next next next error completed
9 RxSwift概要>メソッド>Observable>イベント Observable.of(“a”, “b”, “c”) .subscribe( x in // “a”,
“b”, “c” onNext: { value in // イベント発生時の処理 }, onError: { error in // エラー発生時の処理 }, onCompleted: { // 完了時の処理 } )
• 直訳すると破棄・処分できるもの • Observableをsubscribe(購読)した戻り値 • Disposableに対してdispose()を呼ぶことで 非同期処理のキャンセル • 監視する側としての処理をキャンセル 10
RxSwift概要>メソッド>DisposeBag
let disposeBag = DisposeBag() Observable.of(“a”, “b”, “c”) .subscribe( x in
// “a”, “b”, “c” onNext: { value in // イベント発生時の処理 }, onError: { error in // エラー発生時の処理 }, onCompleted: { // 完了時の処理 } ).disposed(by: disposeBag) 11 RxSwift概要>メソッド>DisposeBag
• Observable、Subjectで作成したストリームを操作できる • ストリームを変換・合成・生成 12 RxSwift概要>メソッド>Operator
let disposeBag = DisposeBag() Observable.of(1, 2, 3) .filter { x
in > 1 } // 2, 3 .map { x in x * 2 } // 4, 6 .flatMap { x in Observable.of(x, x * 2) } // 4, 8, 6, 12 .subscribe( x in onNext: { value in // イベント発生時の処理 }, onError: { error in // エラー発生時の処理 }, onCompleted: { // 完了時の処理 } ) 13 RxSwift概要>メソッド>Operator
• 処理をどのスレッドで実行するか指定 14 RxSwift概要>メソッド>Scheduler
• RxSwift概要 • MVVMパターン概要 • ViewModel作成 • ViewController作成 • まとめ
15 アジェンダ
16 MVVMパターン概要 View Controller View Model Event Observable
17 MVVMパターン概要>例
LoginButtonの 有効・無効 18 MVVMパターン概要>例>View Model概要 View Model Input Output Email入力
Password入力 LoginButtonタップ Emailバリデーション Password バリデーション ログイン結果
• RxSwift概要 • MVVMパターン概要 • ViewModel作成 • ViewController作成 • まとめ
19 アジェンダ
20 View Model作成 class LoginViewModel {}
class LoginViewModel { // output lazy var validEmail: Observable<Bool> lazy
var validPassword: Observable<Bool> lazy var loginButtonEnable: Observable<Bool> lazy var login: Observable<LoginResponse> } 21 View Model作成>output定義
class LoginViewModel { // output // 省略 // input init(
email: Observable<String>, password: Observable<String>, loginTap: Observable<Void>, loginAPI: LoginAPI.Type, disposeBag: DisposeBag ) {} } 22 View Model作成>input定義
class LoginViewModel { // output // 省略 // input init(
email: Observable<String>, password: Observable<String>, loginTap: Observable<Void>, loginAPI: LoginAPI.Type, disposeBag: DisposeBag ) { validEmail = email.map { !$0.isEmpty } } } 23 View Model作成>validEmail
class LoginViewModel { // output // 省略 // input init(
email: Observable<String>, password: Observable<String>, loginTap: Observable<Void>, loginAPI: LoginAPI.Type, disposeBag: DisposeBag ) { validEmail = email.map { !$0.isEmpty } validPassword = password.map { !$0.isEmpty } } } 24 View Model作成>validPassword
class LoginViewModel { // output // 省略 // input
init( // 省略 ) { // 省略 loginButtonEnable = Observable.combineLatest { validEmail, validPassword } .map { $0 && $1 } } } 25 View Model作成>loginButtonEnable
class LoginViewModel { // output // 省略 // input
init( // 省略 ) { // 省略 let params = Driver.combineLatest(email, password) { (email: $0, password: $1) } login = loginButtonTap. .withLatestFrom(params) .flatMap { loginAPI.login(with: params) } } 26 View Model作成>loginButtonEnable
class LoginViewModel { // output lazy var validEmail: Observable<Bool> lazy
var validPassword: Observable<Bool> lazy var loginButtonEnable: Observable<Bool> lazy var login: Observable<LoginResponse> // input init( email: Observable<String>, password: Observable<String>, loginTap: Observable<Void>, loginAPI: LoginAPI.Type, disposeBag: DisposeBag ) { validEmail = email.map { $0.isEmpty } validPassword = password.map { $0.isEmpty } loginButtonEnable = Observable.combineLatest { validEmail, validPassword } .map { $0 && $1 } let params = Driver.combineLatest(email, password) { (email: $0, password: $1) } login = loginBittonTap .withLatestFrom(params) .flatMap { loginAPI.login(with: params) } 27 View Model作成>完成
• RxSwift概要 • MVVMパターン概要 • ViewModel作成 • ViewController作成 • まとめ
28 アジェンダ
class LoginViewController: UIViewController { @IBOutlet weak var emailTextField: UITextField! @IBOutlet
weak var emailErrorLabel: UILabel! @IBOutlet weak var passwordTextField: UITextField @IBOutlet weak var passwordErrorLabel: UILabel! @IBOutlet weak var loginButton: UIButton! } 29 View Controllerl作成
class LoginViewController: UIViewController { // 省略 private let disposeBag =
DisposeBag() private let vm = LoginViewModel! override func viewDidLoad() { vm = LoginViewModel ( email: emailTextField.rx.text.orEmpty.asObservable, password: passwordTextField.rx.text.orEmpty.asObservable, loginAPI: LoginAPI.self, disposeBag: disposeBag ) } } 30 View Controllerl作成>LoginViewModel作成
class LoginViewController: UIViewController { // 省略 override func viewDidLoad() {
vm = LoginViewModel ( email: emailTextField.rx.text.orEmpty.asObservable, password: passwordTextField.rx.text.orEmpty.asObservable, loginAPI: LoginAPI.self, disposeBag: disposeBag ) vm.validEmail .bind(to: emailErrorLabel.rx.isHidden) .disposed(by: disposeBag) } } 31 View Controller作成>validEmail
class LoginViewController: UIViewController { // 省略 override func viewDidLoad() {
vm = LoginViewModel ( // 省略 ) vm.validEmail .bind(to: emailErrorLabel.rx.isHidden) .disposed(by: disposeBag) vm.validPassword .bind(to: passwordErrorLabel.rx.isHidden) .disposed(by: disposeBag) } 32 View Controllerl作成>validPassword
class LoginViewModel: UIViewController { // 省略 override func viewDidLoad() {
vm = LoginViewModel ( // 省略 ) // 省略 vm.loginButtonEnable .bind(to: loginButton.rx.isEnable) .disposed(by: disposeBag) } } 33 View Controllerl作成>loginButtonEnable
class LoginViewModel: UIViewController { // 省略 override func viewDidLoad() {
// 省略 vm.login .obseveOn(MainScheduler.instance) .subscribe( onNext: {}, onError: {} ).disposed(by: disposeBag) } } 34 View Controllerl作成>login
class LoginViewModel: UIViewController { // 省略 override func viewDidLoad() {
// 省略 vm.login .obseveOn(MainScheduler.instance) .subscribe( onNext: { [weak self] self?.showAlert(for: response) }, onError: { [weak self] error self?.showAlert(for: error) } ).disposed(by: disposeBag) } 35 View Controllerl作成>login
class LoginViewModel: UIViewController { @IBOutlet weak var emailTextField: UITextField! @IBOutlet
weak var emailErrorLabel: UILabel! @IBOutlet weak var passwordTextField: UITextField @IBOutlet weak var passwordErrorLabel: UILabel! @IBOutlet weak var loginButton: UIButton! private let disposeBag = DisposeBag() private let vm = LoginViewModel! override func viewDidLoad() { vm = LoginViewModel ( email: emailTextField.rx.text.orEmpty.asObservable, password: passwordTextField.rx.text.orEmpty.asObservable, loginAPI: LoginAPI.self, disposeBag: disposeBag ) vm.validEmail.bind(to: emailErrorLabel.rx.isHidden).disposed(by: disposeBag) vm.validPassword.bind(to: passwordErrorLabel.rx.isHidden).disposed(by: disposeBag) vm.loginButtonEnable.bind(to: loginButton.rx.isEnable).disposed(by: disposeBag) vm.login .obseveOn(MainScheduler.instance) .subscribe(onNext: { [weak self] self?.showAlert(for: response) }, onError: { [weak self] error self?.showAlert(for: error) }).disposed(by: disposeBag) } } 36 View Controllerl作成>完成
• RxSwift概要 • MVVMパターン概要 • ViewModel作成 • ViewController作成 • まとめ
37 アジェンダ
• ViewControllerが肥大化しない • 処理が明確 ◦ テストがしやすい 38 まとめ