Slide 1

Slide 1 text

iOSΞϓϦ։ൃʹ MVVM+Reduxύλʔϯ Λ࢖͍࢝Ίͨ࿩ SwiftѪ޷ձ vol36 2018/11/09 @takehilo_kaneko

Slide 2

Slide 2 text

ࣗݾ঺հ ۚࢠ ༤େ @takehilo_kaneko 1೥͘Β͍ https://qiita.com/takehilo

Slide 3

Slide 3 text

͜ͷτʔΫͷ໨త • ࠷ۙݸਓ։ൃͷΞϓϦͰMVVM+ReduxύλʔϯΛ࢖ͬͯ։ൃ Λ࢝Ίͨ • ·ͩReduxΛษڧ࢝͠Ίͯ2िؒ͘Β͍͚ͩͲɺࠓͷஈ֊Ͱྑ ͍ͱײ͍ͯ͡Δ͜ͱΛ࿩͢

Slide 4

Slide 4 text

ର৅ऀ • MVVM͓ΑͼRxSwiftΛ࢖ͬͨ͜ͱ͕͋Δਓ • Redux͓ΑͼReSwiftʹ͍ͭͯ͸͋·Γ஌Βͳ͍ਓ

Slide 5

Slide 5 text

࢓ࣄͰMVVMͰ։ൃΛ͍ͯͯ͠ɺ ͣͬͱReduxΛೖΕͯΈ͔ͨͬͨʂ

Slide 6

Slide 6 text

MVVMͰ։ൃ͍ͯͯ͠ײ͍ͯ͡Δ՝୊

Slide 7

Slide 7 text

ෳ਺ը໘ؒͰͷ ঢ়ଶͷڞ༗ํ๏ ঢ়ଶมԽͷ௨஌ํ๏ ʹ͍ͭͯͷϧʔϧ͕ͳ͍

Slide 8

Slide 8 text

MVVMͷ՝୊ • ҰཡλϒͰ͓ؾʹೖΓ௥Ճˠ͓ؾʹೖΓҰཡλϒʹଈ൓ө • ֤ViewController͸ผʑͷViewModelʹґଘ͍ͯ͠Δ • ViewModelؒͰঢ়ଶมԽΛͲ͏௨஌͢Δʁ • Ұཡը໘͔Βৄࡉը໘ʹભҠ࣌ʹϞσϧ΍ঢ়ଶΛ౉͢ • Ϟσϧ΍ঢ়ଶΛViewControllerʹ౉͢ʁViewModelʹ౉͢ʁ • ౉͢ύϥϝʔλ͕ଟ͍ͱɺίʔυมߋ࣌ʹ౉͠๨Ε͕ग़͖ͯ ͦ͏

Slide 9

Slide 9 text

MVVMͷ՝୊ • Ұཡը໘͔Βݕࡧ৚݅બ୒ը໘ʹભҠ͠ɺݕࡧ৚݅Λબ୒ͨ͠ ΒҰཡը໘ʹ໭ͬͯݕࡧΛ࣮ߦ • Ͳ͏΍ͬͯલͷը໘ʹঢ়ଶΛ౉͢ʁ • DelegateύλʔϯΛ࢖͏ɺҰཡը໘༻ViewModelΛݕࡧ৚݅ બ୒ը໘Ͱ΋ڞ༗͢ΔɺͳͲͷ΍Γํ͕ߟ͑ΒΕΔ

Slide 10

Slide 10 text

͍ͣΕͷ՝୊΋ղܾํ๏͸͋Δ

Slide 11

Slide 11 text

͔͠͠ɺ MVVMʹ׳Εͨϝϯόʔ͕͓Βͣɺ ઌߦͯ͠ϧʔϧΛܾΊΒΕͳ͔ͬͨͨΊɺ ը໘͝ͱʹ΍Γํ͕ҟͳͬͯ͠·ͬͨ

Slide 12

Slide 12 text

ঢ়ଶͷѻ͍ํʹؔ͢Δڞ௨ͷϧʔϧ͕΄͍͠

Slide 13

Slide 13 text

Redux

Slide 14

Slide 14 text

ࠓ೔͸Reduxͱ͸Կ͔ͱ͍͏࿩͸͠ͳ͍ ʢͱ͍͏͔Ͱ͖Δ΄Ͳͷ஌ࣝ͸·ͩͳ͍ʣ

Slide 15

Slide 15 text

͜Ε͔Β঺հ͢Δ࣮૷Πϝʔδ͔Β งғؾΛ௫ΜͰΈ͍ͯͩ͘͞

Slide 16

Slide 16 text

ReduxͱReSwiftʹ͍ͭͯ͸ͪ͜ΒΛࢀর ͜ΕಡΜ͚ͩͩͰ։ൃ࢝ΊΒΕ·ͨ͠ʂ https://qiita.com/susieyy/items/23d44f28c6a6915c58e2

Slide 17

Slide 17 text

MVVM+ReduxύλʔϯΛద༻ͨ͠ ΞϓϦ࣮૷ྫΛ঺հ͢Δ

Slide 18

Slide 18 text

ΞϓϦ֓ཁ • ಡॻϝϞΛऔΔΞϓϦ • RxSwiftͱReSwiftΛ࢖ͬͯMVVM+ReduxύλʔϯΛ࣮૷ • ʢ͜͜Ͱ͸͋Μ·Γؔ܎ͳ͍͕ʣόοΫΤϯυʹFirestoreΛ࠾ ༻͍ͯ͠Δ

Slide 19

Slide 19 text

ReSwift

Slide 20

Slide 20 text

ReSwift ެࣜReferenceΑΓ • ReduxϥΠΫͳ୯ํ޲σʔλϑϩʔΞʔΩςΫνϟͷ࣮૷ • ΞϓϦΛ3ͭͷؔ৺ࣄʹ෼཭͢Δ • State: ΞϓϦશମͷঢ়ଶΛҰͭͷσʔλߏ଄Ͱදݱ • Views: ݱࡏͷΞϓϦͷঢ়ଶΛࢹ֮Խ͢Δ • State Changes: ঢ়ଶมߋͷํ๏Λݫ੍͘͠ݶ http://reswift.github.io/ReSwift/master/index.html

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

ಡॻϝϞҰཡදࣔը໘Λྫʹ࣮૷Λݟ͍ͯ͘

Slide 23

Slide 23 text

Stateͷ࣮૷

Slide 24

Slide 24 text

struct AppState: StateType { var postListState = PostListState() } • ΞϓϦͷঢ়ଶΛstructͰදݱ͢Δ • ը໘୯ҐͳͲͰঢ়ଶΛ֊૚Խ͍ͯ͘͠

Slide 25

Slide 25 text

let appStore = Store( reducer: appReducer, state: AppState(), middleware: []) @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { • ReSwiftͷ࢓૊ΈΛಈ͔ͨ͢Ίɺ࠷ॳʹStoreΛηοτΞοϓ͢Δ • ৔ॴ͸Ͳ͜Ͱ΋ྑ͍͕ɺੜ੒ͨ͠StoreΠϯελϯεΛอ࣋ͯ͠ ͓͘

Slide 26

Slide 26 text

View + ViewModelͷ࣮૷

Slide 27

Slide 27 text

class PostListViewModel: StoreSubscriber { init(store: Store) { self.store = store viewWillAppearStream .subscribe(onNext: { [unowned self] in self.store.dispatch(PostListState.listenPostsActionCreator) self.store.subscribe(self) { subcription in subcription.select { state in state.postListState } } }) .disposed(by: disposeBag) } func newState(state: PostListState) { postsStream.accept(state.posts) } } ಡॻϝϞσʔλͷऔಘΞ ΫγϣϯΛσΟεύον • ViewModelͰঢ়ଶมߋ௨஌ͷड৴͓ΑͼActionͷσΟεύον Λߦ͏Α͏ʹ͍ͯ͠Δ ঢ়ଶมߋ௨஌Λड͚औΓɺ σʔλόΠϯσΟϯάΛ௨ͯ͡ 7JFX$POUSPMMFSʹ௨஌

Slide 28

Slide 28 text

class PostListViewController: UIViewController { func bind() { rx.sentMessage(#selector(viewWillAppear(_:))) .map { _ in } .bind(to: viewModel.viewWillAppear) .disposed(by: disposeBag) viewModel.posts .drive(tableView.rx.items( cellIdentifier: R.nib.postListCell.name, cellType: PostListCell.self) ) { (_, element, cell) in cell.configure(post: element) } .disposed(by: disposeBag) } } • ViewController͸ViewModelͱͷσʔλόΠϯσΟϯάͱɺ σʔλදࣔ΍ը໘ભҠ౳ͷViewػೳʹప͢Δ • ঢ়ଶʹґଘͨ͠ϩδοΫ͸ۃྗ࣋ͨͤͳ͍

Slide 29

Slide 29 text

Action + Reducerͷ࣮૷

Slide 30

Slide 30 text

class PostListViewModel: StoreSubscriber { init(store: Store) { self.store = store viewWillAppearStream .subscribe(onNext: { [unowned self] in self.store.dispatch(PostListState.listenPostsActionCreator) self.store.subscribe(self) { subcription in subcription.select { state in state.postListState } } }) .disposed(by: disposeBag) } func newState(state: PostListState) { postsStream.accept(state.posts) } } ͜͜Ͱ͸"DUJPO$SFBUPS ΛσΟεύον • viewWillAppearΠϕϯτΛτϦΨʔʹɺAction͕σΟεύον ͞ΕΔ

Slide 31

Slide 31 text

extension PostListState { static func listenPostsActionCreator(state: AppState, store: Store) -> Action? { let listener = db.collection("posts").addSnapshotListener { (snapshop, error) in guard let documents = snapshop?.documents else { return } let posts = documents.compactMap { Post(dictionary: $0.data()) } store.dispatch(Action.updatePosts(posts: posts)) } return Action.listenPosts(listener: listener) } } ͜ͷ෦෼ • ActionCreatorͰσʔλऔಘΛߦ͍ɺऔಘͨ͠σʔλͰAction Λੜ੒ͯ͠σΟεύον͢Δ

Slide 32

Slide 32 text

extension PostListState { static func reducer(action: ReSwift.Action, state: PostListState?) -> PostListState { var state = state ?? PostListState() if let action = action as? Action { switch action { case let .updatePosts(posts): state.posts = posts } return state } } ঢ়ଶΛมߋ͢Δ • Reducer͕ActionΛड͚औΓɺঢ়ଶΛมߋ͢Δ • ৽͍͠ঢ়ଶ͸ViewModelΛ௨ͯ͡Viewʹ௨஌͞Εɺը໘ʹσʔ λ͕දࣔ͞ΕΔ

Slide 33

Slide 33 text

͜ͷ࢓૊ΈͰMVVMͷ՝୊͕ղܾͰ͖Δ͔

Slide 34

Slide 34 text

՝୊ͷ1ͭΛ͓͞Β͍ • Ұཡը໘͔Βৄࡉը໘ʹભҠ࣌ʹϞσϧ΍ঢ়ଶΛ౉͢ • Ϟσϧ΍ঢ়ଶΛViewControllerʹ౉͢ʁViewModelʹ౉͢ʁ • ౉͢ύϥϝʔλ͕ଟ͍ͱɺίʔυมߋ࣌ʹ౉͠๨Ε͕ग़͖ͯ ͦ͏

Slide 35

Slide 35 text

class PostListViewController: UIViewController { func bind() { tableView.rx.itemSelected.asDriver() .map { $0.row } .drive(onNext: { [unowned self] row in let vc = self.resolve(PostViewController.self)! // DIίϯςφ͔Βੜ੒ vc.post = self.posts[row] // vc.state1 = ... // vc.state2 = ... self.present(vc) }) .disposed(by: disposeBag) } } 7JFX$POUSPMMFSʹ༷ʑͳ ঢ়ଶΛ౉͍ͯ͠Δ • Ұཡ͔Βߦ͕બ୒͞ΕͨΒɺৄࡉը໘ͷViewControllerΛੜ੒ ͠ɺબ୒͞Εͨߦͷσʔλ΍ԿΒ͔ͷঢ়ଶσʔλΛ౉ͯ͠ॳظ Խ͢Δඞཁ͕͋Δ

Slide 36

Slide 36 text

class PostListViewController: UIViewController { func bind() { tableView.rx.itemSelected.asDriver() .map { $0.row } .drive(viewModel.itemSelected) .disposed(by: disposeBag) tableView.rx.itemSelected.asDriver() .drive(onNext: { [unowned self] in let vc = self.resolve(PostViewController.self)! self.present(vc, animated: true) }) .disposed(by: disposeBag) } } 7JFX$POUSPMMFSʹ ͋Ε͜Ε౉͞ͳͯ͘ྑ͍ • ߦ͕બ୒͞Εͨ͜ͱΛViewModelʹ௨஌͢Δ͜ͱͰɺৄࡉը໘ ༻ͷঢ়ଶ͕มߋ͞ΕΔ • ͋ͱ͸Կ΋ߟ͑ͣৄࡉը໘Λදࣔͯ͋͛͠Ε͹ɺৄࡉը໘ଆ͸ উखʹ࠷৽ͷঢ়ଶΛ࢖ͬͯදࣔΛͯ͘͠ΕΔ

Slide 37

Slide 37 text

ᶃ selectPostΞΫγϣϯ ͕σΟεύον͞ΕΔ ᶄ ৄࡉը໘༻ͷঢ়ଶͰ͋Δ PostState͕มߋ͞ΕΔ ᶅ ৄࡉը໘͕࠷৽ͷ PostStateͰදࣔ͞ΕΔ

Slide 38

Slide 38 text

ଞͷ՝୊΋εοΩϦղܾ • ҰཡλϒͰ͓ؾʹೖΓ௥Ճˠ͓ؾʹೖΓҰཡλϒʹଈ൓ө • Ұཡը໘Ͱ͓ؾʹೖΓʹ௥Ճͨ͠Βɺ͓ؾʹೖΓҰཡը໘ʹ ΋߲໨Λ௥Ճ͢ΔActionΛσΟεύον͢Ε͹ྑ͍ • Ұཡը໘͔Βݕࡧ৚݅બ୒ը໘ʹભҠ͠ɺݕࡧ৚݅Λબ୒ͨ͠ ΒҰཡը໘ʹ໭ͬͯݕࡧΛ࣮ߦ • ݕࡧ৚݅બ୒ը໘Ͱݕࡧ৚݅Λબ୒ͨ͠ΒɺҰཡը໘ͷݕࡧ ৚݅Λมߋ͢ΔActionΛσΟεύον͢Ε͹ྑ͍

Slide 39

Slide 39 text

ࠓͷͱ͜ΖReduxʢReSwiftʣྑͦ͞͏

Slide 40

Slide 40 text

·ͱΊ • ReduxύλʔϯΛಋೖ͢Δ͜ͱͰɺෳ਺ը໘ؒͰͷঢ়ଶͷڞ༗ ํ๏ɺঢ়ଶมԽͷ௨஌ํ๏ʹ͍ͭͯͷ౷Ұͨ͠ϧʔϧ͕ੜ·Ε Δ • MVVM+ReduxύλʔϯʹΑͬͯΞϓϦશମͰίʔυͷએݴత ͳهड़͕ՄೳʹͳΓɺಡΈ΍͘͢อक͠΍͍͢ίʔυʹͳΔ

Slide 41

Slide 41 text

5IBOLZPV