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
tofu_san0000
June 08, 2019
Technology
0
300
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
92
Other Decks in Technology
See All in Technology
AWSサービスアップデート 2024/12 Part3
nrinetcom
PRO
0
140
GoogleのAIエージェント論 Authors: Julia Wiesinger, Patrick Marlow and Vladimir Vuskovic
customercloud
PRO
0
130
#TRG24 / David Cuartielles / Post Open Source
tarugoconf
0
570
[IBM TechXchange Dojo]Watson Discoveryとwatsonx.aiでRAGを実現!座学①
siyuanzh09
0
110
Accessibility Inspectorを活用した アプリのアクセシビリティ向上方法
hinakko
0
180
いま現場PMのあなたが、 経営と向き合うPMになるために 必要なこと、腹をくくること
hiro93n
9
7.4k
Oracle Base Database Service 技術詳細
oracle4engineer
PRO
6
54k
WantedlyでのKotlin Multiplatformの導入と課題 / Kotlin Multiplatform Implementation and Challenges at Wantedly
kubode
0
240
三菱電機で社内コミュニティを立ち上げた話
kurebayashi
1
350
Copilotの力を実感!3ヶ月間の生成AI研修の試行錯誤&成功事例をご紹介。果たして得たものとは・・?
ktc_shiori
0
340
Docker Desktop で Docker を始めよう
zembutsu
PRO
0
150
シフトライトなテスト活動を適切に行うことで、無理な開発をせず、過剰にテストせず、顧客をビックリさせないプロダクトを作り上げているお話 #RSGT2025 / Shift Right
nihonbuson
3
2.1k
Featured
See All Featured
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
226
22k
Stop Working from a Prison Cell
hatefulcrawdad
267
20k
Building Applications with DynamoDB
mza
93
6.2k
The Cult of Friendly URLs
andyhume
78
6.1k
Rails Girls Zürich Keynote
gr2m
94
13k
A designer walks into a library…
pauljervisheath
205
24k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
120k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
19
2.3k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
365
25k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
38
1.9k
The Cost Of JavaScript in 2023
addyosmani
46
7.2k
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 まとめ