Slide 1

Slide 1 text

UnioΛ։ൃͯ͠ϓϩμΫτʹ ಋೖ͔ͯ͠Β2೥͕ܦͬͨ ͋ͭ·Ε4XJGU޷͖ʂ4XJGUѪ޷ձWPM CZNBSUZTV[VLJ

Slide 2

Slide 2 text

ࣗݾ঺հ marty_suzuki marty-suzuki Taiki Suzuki

Slide 3

Slide 3 text

Unioͱ͸ʁ IUUQTHJUIVCDPNDBUTPTT6OJP 6OEJSFDUJPOBM PVUQVU JOQVU

Slide 4

Slide 4 text

ͳͥUnioΛ։ൃͨ͠ͷ͔

Slide 5

Slide 5 text

UnioΛ։ൃͨ͠ͷ͔ ͳͥ ਓҎ্Ͱ։ൃ͍ͯ͠Δͱ7JFX.PEFMͷ࣮૷΋ेਓे৭ େܕͷ৽ػೳ։ൃ΋࢝·ͬͯϨϏϡʔ͕େมʹͳΓͦ͏ͩ͠ ࠓޙͷ։ൃͷࢀߟʹͳΔΑ͏ͳ࣮૷ͰਐΊΒΕͳ͍͔ͳ

Slide 6

Slide 6 text

ViewModelͷ࣮૷͕ेਓे৭ͱ͸ final class CountViewController: UIViewController { private let countUpButton = UIButton() private let countLabel = UILabel() private let viewModel = CountViewModel() override func viewDidLoad() { super.viewDidLoad() ... } }

Slide 7

Slide 7 text

final class CountViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() viewModel.count .bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap .subscribe(onNext: { [viewModel] in viewModel.countUp() }) .disposed(by: rx.disposeBag) } } final class CountViewModel { let count: Observable private let _count = BehaviorRelay(value: 0) init() { self.count = _count.map(String.init) } func countUp() { _count.accept(_count.value + 1) } } ViewModelͷ࣮૷ͷྫ ͦͷ1

Slide 8

Slide 8 text

final class CountViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() viewModel.count .bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap .subscribe(onNext: { [viewModel] in viewModel.countUp() }) .disposed(by: rx.disposeBag) } } final class CountViewModel { let count: Observable private let _count = BehaviorRelay(value: 0) init() { self.count = _count.map(String.init) } func countUp() { _count.accept(_count.value + 1) } } ViewModelͷ࣮૷ͷྫ ͦͷ1 ֎෦΁ͷग़ྗͱ಺෦ঢ়ଶΛQSPQFSUZͰఆٛ

Slide 9

Slide 9 text

final class CountViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() viewModel.count .bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap .subscribe(onNext: { [viewModel] in viewModel.countUp() }) .disposed(by: rx.disposeBag) } } final class CountViewModel { let count: Observable private let _count = BehaviorRelay(value: 0) init() { self.count = _count.map(String.init) } func countUp() { _count.accept(_count.value + 1) } } ViewModelͷ࣮૷ͷྫ ͦͷ1 ϝιουܦ༝Ͱ಺෦ঢ়ଶͷߋ৽

Slide 10

Slide 10 text

final class CountViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() viewModel.count .bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap .subscribe(onNext: { [viewModel] in viewModel.countUp() }) .disposed(by: rx.disposeBag) } } final class CountViewModel { let count: Observable private let _count = BehaviorRelay(value: 0) init() { self.count = _count.map(String.init) } func countUp() { _count.accept(_count.value + 1) } } ViewModelͷ࣮૷ͷྫ ͦͷ1 ಺෦ঢ়ଶ͕ߋ৽͞ΕΔͱ4USJOHʹม׵ͯ͠ग़ྗ

Slide 11

Slide 11 text

final class CountViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() viewModel.count.bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap.subscribe(onNext: { [viewModel] in viewModel.countUp() }) .disposed(by: rx.disposeBag) } } final class CountViewModel { let count: Observable private let _countUp = PublishRelay() private let disposeBag = DisposeBag() init() { let _count = BehaviorRelay(value: 0) self.count = _count.map(String.init) _countUp.withLatestFrom(_count) { $1 + 1 }.bind(to: _count) .disposed(by: disposeBag) } func countUp() { _countUp.accept(()) } } ViewModelͷ࣮૷ͷྫ ͦͷ2

Slide 12

Slide 12 text

final class CountViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() viewModel.count.bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap.subscribe(onNext: { [viewModel] in viewModel.countUp() }) .disposed(by: rx.disposeBag) } } final class CountViewModel { let count: Observable private let _countUp = PublishRelay() private let disposeBag = DisposeBag() init() { let _count = BehaviorRelay(value: 0) self.count = _count.map(String.init) _countUp.withLatestFrom(_count) { $1 + 1 }.bind(to: _count) .disposed(by: disposeBag) } func countUp() { _countUp.accept(()) } } ViewModelͷ࣮૷ͷྫ ͦͷ2 ಺෦ঢ়ଶΛCJOEͳͲͰΩϟϓνϟ͞ΕΔ͜ͱΛ લఏʹϩʔΧϧʹఆٛ

Slide 13

Slide 13 text

final class CountViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() viewModel.count.bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap.subscribe(onNext: { [viewModel] in viewModel.countUp() }) .disposed(by: rx.disposeBag) } } final class CountViewModel { let count: Observable private let _countUp = PublishRelay() private let disposeBag = DisposeBag() init() { let _count = BehaviorRelay(value: 0) self.count = _count.map(String.init) _countUp.withLatestFrom(_count) { $1 + 1 }.bind(to: _count) .disposed(by: disposeBag) } func countUp() { _countUp.accept(()) } } ViewModelͷ࣮૷ͷྫ ͦͷ2 ϝιουͷݺͼग़͠ΛϋϯυϦϯά͢ΔͨΊͷ 
 1VCMJTI3FMBZΛఆٛ

Slide 14

Slide 14 text

final class CountViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() viewModel.count.bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap.subscribe(onNext: { [viewModel] in viewModel.countUp() }) .disposed(by: rx.disposeBag) } } final class CountViewModel { let count: Observable private let _countUp = PublishRelay() private let disposeBag = DisposeBag() init() { let _count = BehaviorRelay(value: 0) self.count = _count.map(String.init) _countUp.withLatestFrom(_count) { $1 + 1 }.bind(to: _count) .disposed(by: disposeBag) } func countUp() { _countUp.accept(()) } } ViewModelͷ࣮૷ͷྫ ͦͷ2 1VCMJTI3FMBZ͔ΒͷΠϕϯτΛ΋ͱʹ ঢ়ଶΛߋ৽͢Δ

Slide 15

Slide 15 text

final class CountViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() viewModel.count.bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap.bind(to: viewModel.countUp) .disposed(by: rx.disposeBag) } } final class CountViewModel { let count: Observable let countUp: AnyObserver private let disposeBag = DisposeBag() init() { let _count = BehaviorRelay(value: 0) self.count = _count.map(String.init) let _countUp = PublishSubject() self.countUp = _countUp.asObserver() _countUp.withLatestFrom(_count) { $1 + 1 }.bind(to: _count) .disposed(by: disposeBag) } } ViewModelͷ࣮૷ͷྫ ͦͷ3

Slide 16

Slide 16 text

final class CountViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() viewModel.count.bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap.bind(to: viewModel.countUp) .disposed(by: rx.disposeBag) } } final class CountViewModel { let count: Observable let countUp: AnyObserver private let disposeBag = DisposeBag() init() { let _count = BehaviorRelay(value: 0) self.count = _count.map(String.init) let _countUp = PublishSubject() self.countUp = _countUp.asObserver() _countUp.withLatestFrom(_count) { $1 + 1 }.bind(to: _count) .disposed(by: disposeBag) } } ViewModelͷ࣮૷ͷྫ ͦͷ3 "OZ0CTFSWFSͱͯ͠ೖྗΛఆٛ

Slide 17

Slide 17 text

final class CountViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() viewModel.count.bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap.bind(to: viewModel.countUp) .disposed(by: rx.disposeBag) } } final class CountViewModel { let count: Observable let countUp: AnyObserver init() { let _countUp = PublishSubject() self.countUp = _countUp.asObserver() self.count = _countUp.scan(0) { count, _ in count + 1 } .startWith(0) .map(String.init) } } ViewModelͷ࣮૷ͷྫ ͦͷ4

Slide 18

Slide 18 text

final class CountViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() viewModel.count.bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap.bind(to: viewModel.countUp) .disposed(by: rx.disposeBag) } } final class CountViewModel { let count: Observable let countUp: AnyObserver init() { let _countUp = PublishSubject() self.countUp = _countUp.asObserver() self.count = _countUp.scan(0) { count, _ in count + 1 } .startWith(0) .map(String.init) } } ViewModelͷ࣮૷ͷྫ ͦͷ4 ໌ࣔతʹ಺෦ঢ়ଶ͸ఆٛͤͣɺTDBOͳͲ Λ࢖ͬͯঢ়ଶΛߋ৽͢Δ

Slide 19

Slide 19 text

UnioΛར༻࣮ͨ͠૷

Slide 20

Slide 20 text

UnioΛར༻࣮ͨ͠૷ final class CountViewModel: UnioStream { struct Input: InputType { let countUp = PublishRelay() } struct State: StateType { let count = BehaviorRelay(value: 0) } struct Output: OutputType { let count: Observable } static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let state = dependency.state dependency.inputObservables.countUp .withLatestFrom(state.count) { $1 + 1 } .bind(to: state.count) .disposed(by: disposeBag) return Output(count: state.count.map(String.init)) } }

Slide 21

Slide 21 text

UnioΛར༻࣮ͨ͠૷ final class CountViewModel: UnioStream { struct Input: InputType { let countUp = PublishRelay() } struct State: StateType { let count = BehaviorRelay(value: 0) } struct Output: OutputType { let count: Observable } static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let state = dependency.state dependency.inputObservables.countUp .withLatestFrom(state.count) { $1 + 1 } .bind(to: state.count) .disposed(by: disposeBag) return Output(count: state.count.map(String.init)) } } ֎෦͔ΒೖྗΛఆٛ͢Δ৔ॴ QSPQFSZΛ1VCMJTI3FMBZͰఆٛͯ͠΋ ֎෦͔ΒΞΫηε͢Δͱ"OZ0CTFSWFS ʹม׵͞Εɺ಺෦͔ΒΞΫηε͢Δͱ 0CTFSWBCMFʹͳΔ

Slide 22

Slide 22 text

UnioΛར༻࣮ͨ͠૷ final class CountViewModel: UnioStream { struct Input: InputType { let countUp = PublishRelay() } struct State: StateType { let count = BehaviorRelay(value: 0) } struct Output: OutputType { let count: Observable } static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let state = dependency.state dependency.inputObservables.countUp .withLatestFrom(state.count) { $1 + 1 } .bind(to: state.count) .disposed(by: disposeBag) return Output(count: state.count.map(String.init)) } } ಺෦ͷঢ়ଶΛఆٛ͢Δ৔ॴ ֎෦͔Β͸ࢀর͞Εͳ͍

Slide 23

Slide 23 text

UnioΛར༻࣮ͨ͠૷ final class CountViewModel: UnioStream { struct Input: InputType { let countUp = PublishRelay() } struct State: StateType { let count = BehaviorRelay(value: 0) } struct Output: OutputType { let count: Observable } static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let state = dependency.state dependency.inputObservables.countUp .withLatestFrom(state.count) { $1 + 1 } .bind(to: state.count) .disposed(by: disposeBag) return Output(count: state.count.map(String.init)) } } ֎෦΁ͷग़ྗΛఆٛ͢Δ৔ॴ QSPQFSZΛ#FIBWJPS3FMBZͰఆٛͯ͠΋ ֎෦͔ΒΞΫηε͢Δͱ0CTFSWBCMF΁ ม׵·ͨ͸஋ͷΞΫηεͱͳΔ

Slide 24

Slide 24 text

UnioΛར༻࣮ͨ͠૷ final class CountViewModel: UnioStream { struct Input: InputType { let countUp = PublishRelay() } struct State: StateType { let count = BehaviorRelay(value: 0) } struct Output: OutputType { let count: Observable } static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let state = dependency.state dependency.inputObservables.countUp .withLatestFrom(state.count) { $1 + 1 } .bind(to: state.count) .disposed(by: disposeBag) return Output(count: state.count.map(String.init)) } } *OQVUɾ಺෦ͷঢ়ଶɾ֎෦ґଘ͔Β0VUQVUΛੜ੒͢Δ৔ॴ EFQFOEFODZJOQVU0CTFSWBCMFT͔Β*OQVUɺEFQFOEFODZTUBUF͔Β ಺෦ঢ়ଶɺEFQFOEFODZFYUSB͔Β֎෦ґଘΛऔಘͰ͖Δ TUBUJDGVODͳͷͰTFMGͷΩϟϓνϟΛؾʹ͢ΔඞཁͳͲ΋ͳ͍

Slide 25

Slide 25 text

UnioΛར༻࣮ͨ͠૷ final class CountViewController: UIViewController { private let countUpButton = UIButton() private let countLabel = UILabel() private let viewModel = CountViewModel(input: .init(), state: .init(), extra: .init()) override func viewDidLoad() { super.viewDidLoad() viewModel.output.count .bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap .bind(to: viewModel.input.countUp) .disposed(by: rx.disposeBag) } }

Slide 26

Slide 26 text

UnioΛར༻࣮ͨ͠૷ final class CountViewController: UIViewController { private let countUpButton = UIButton() private let countLabel = UILabel() private let viewModel = CountViewModel(input: .init(), state: .init(), extra: .init()) override func viewDidLoad() { super.viewDidLoad() viewModel.output.count .bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap .bind(to: viewModel.input.countUp) .disposed(by: rx.disposeBag) } } #FIBWJPS3FMBZͰఆٛͯ͠΋0CTFSWBCMF ·ͨ͸஋ͷΞΫηεͱͳΔ

Slide 27

Slide 27 text

UnioΛར༻࣮ͨ͠૷ final class CountViewController: UIViewController { private let countUpButton = UIButton() private let countLabel = UILabel() private let viewModel = CountViewModel(input: .init(), state: .init(), extra: .init()) override func viewDidLoad() { super.viewDidLoad() viewModel.output.count .bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap .bind(to: viewModel.input.countUp) .disposed(by: rx.disposeBag) } } 1VCMJTI3FMBZͰఆٛͯ͠΋"OZ0CTFSWFS ·ͨ͸&MFNFOUΛҾ਺ʹͱΔDMPTVSFͱͳΔ

Slide 28

Slide 28 text

Unioͷྺ࢙

Slide 29

Slide 29 text

v0.1. 0 Initial Release

Slide 30

Slide 30 text

v0.1.0 Initial Release final class CountViewModel: UnioStream { struct Input: InputType { let countUp = PublishRelay() } struct State: StateType { let count = BehaviorRelay(value: 0) } struct Output: OutputType { let count: Observable } struct Logic: LogicType { private let disposeBag = DisposeBag() func bind(from dependency: Dependency) -> Output { let state = dependency.state dependency.inputObservables.countUp .withLatestFrom(state.count) { $1 + 1 } .bind(to: state.count) .disposed(by: disposeBag) return Output(count: state.count.map(String.init)) } } }

Slide 31

Slide 31 text

v0.1.0 Initial Release final class CountViewModel: UnioStream { struct Input: InputType { let countUp = PublishRelay() } struct State: StateType { let count = BehaviorRelay(value: 0) } struct Output: OutputType { let count: Observable } struct Logic: LogicType { private let disposeBag = DisposeBag() func bind(from dependency: Dependency) -> Output { let state = dependency.state dependency.inputObservables.countUp .withLatestFrom(state.count) { $1 + 1 } .bind(to: state.count) .disposed(by: disposeBag) return Output(count: state.count.map(String.init)) } } } -PHJDͷఆ͕ٛ6OJP4USFBNࣗ਎Ͱ͸ͳ͘ ผͷܕͱͯ͠ఆٛ͞Ε͍ͯͨ

Slide 32

Slide 32 text

final class CountViewController: UIViewController { private let countUpButton = UIButton() private let countLabel = UILabel() private let viewModel = CountViewModel(input: .init(), state: .init(), extra: .init(), logic: .init()) override func viewDidLoad() { super.viewDidLoad() viewModel.output.observable(for: \.count) .bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap .bind(to: viewModel.input.accept(for: \.countUp)) .disposed(by: rx.disposeBag) } } v0.1.0 Initial Release

Slide 33

Slide 33 text

final class CountViewController: UIViewController { private let countUpButton = UIButton() private let countLabel = UILabel() private let viewModel = CountViewModel(input: .init(), state: .init(), extra: .init(), logic: .init()) override func viewDidLoad() { super.viewDidLoad() viewModel.output.observable(for: \.count) .bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap .bind(to: viewModel.input.accept(for: \.countUp)) .disposed(by: rx.disposeBag) } } v0.1.0 Initial Release ,FZ1BUIΛར༻ͯ͠0CTFSWBCMFΛऔಘ͢Δ ϝιου͕༻ҙ͞Ε͍ͯͨ

Slide 34

Slide 34 text

v0.1.0 Initial Release protocol CountViewStreamType: AnyObject { var input: InputWrapper { get } var output: OutputWrapper { get } } final class CountViewStream: UnioStream, CountViewStreamType { convenience init(extra: Extra = .init()) { self.init(input: Input(), state: State(), extra: extra) } } extension CountViewStream { struct Input: InputType { } struct Output: OutputType { } struct State: StateType { } struct Extra: ExtraType { } static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let state = dependency.state return Output() } }

Slide 35

Slide 35 text

v0.5. 0 DynamicMemberLookup Support

Slide 36

Slide 36 text

v0.5.0 DynamicMemberLookup support final class CountViewController: UIViewController { private let countUpButton = UIButton() private let countLabel = UILabel() private let viewModel = CountViewModel(input: .init(), state: .init(), extra: .init(), logic: .init()) override func viewDidLoad() { super.viewDidLoad() viewModel.output.count .bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap .bind(to: viewModel.input.countUp) .disposed(by: rx.disposeBag) } }

Slide 37

Slide 37 text

v0.5.0 DynamicMemberLookup support final class CountViewController: UIViewController { private let countUpButton = UIButton() private let countLabel = UILabel() private let viewModel = CountViewModel(input: .init(), state: .init(), extra: .init(), logic: .init()) override func viewDidLoad() { super.viewDidLoad() viewModel.output.count .bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap .bind(to: viewModel.input.countUp) .disposed(by: rx.disposeBag) } } %ZOBNJD.FNCFS-PPLVQΛ࠾༻͢Δ͜ͱͰ PCTFSWBCMF GPS ͔ΒΞΫηε͢ΔܗͰ͸ͳ͘ QSPQFSUZʹΞΫηε͢ΔΑ͏ͳܗʹͰ͖ͨ

Slide 38

Slide 38 text

v0.6. 0 Improve LogicType implementation

Slide 39

Slide 39 text

v0.6.0 Improve LogicType implementation final class CountViewModel: UnioStream { struct Input: InputType { let countUp = PublishRelay() } struct State: StateType { let count = BehaviorRelay(value: 0) } struct Output: OutputType { let count: Observable } static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let state = dependency.state dependency.inputObservables.countUp .withLatestFrom(state.count) { $1 + 1 } .bind(to: state.count) .disposed(by: disposeBag) return Output(count: state.count.map(String.init)) } }

Slide 40

Slide 40 text

v0.6.0 Improve LogicType implementation final class CountViewModel: UnioStream { struct Input: InputType { let countUp = PublishRelay() } struct State: StateType { let count = BehaviorRelay(value: 0) } struct Output: OutputType { let count: Observable } static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let state = dependency.state dependency.inputObservables.countUp .withLatestFrom(state.count) { $1 + 1 } .bind(to: state.count) .disposed(by: disposeBag) return Output(count: state.count.map(String.init)) } } -PHJD5ZQFΛผͷܕͱͯ͠ఆٛ͢ΔͷͰ͸ͳ͘ 
 6OJP4USFBNͷTVCDMBTTʹఆٛͰ͖ΔΑ͏ʹରԠ

Slide 41

Slide 41 text

v0.6.0 Improve LogicType implementation final class CountViewController: UIViewController { private let countUpButton = UIButton() private let countLabel = UILabel() private let viewModel = CountViewModel(input: .init(), state: .init(), extra: .init()) override func viewDidLoad() { super.viewDidLoad() viewModel.output.count .bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap .bind(to: viewModel.input.countUp) .disposed(by: rx.disposeBag) } }

Slide 42

Slide 42 text

v0.6.0 Improve LogicType implementation final class CountViewController: UIViewController { private let countUpButton = UIButton() private let countLabel = UILabel() private let viewModel = CountViewModel(input: .init(), state: .init(), extra: .init()) override func viewDidLoad() { super.viewDidLoad() viewModel.output.count .bind(to: countLabel.rx.text) .disposed(by: rx.disposeBag) countUpButton.rx.tap .bind(to: viewModel.input.countUp) .disposed(by: rx.disposeBag) } } -PHJD5ZQFΛ6OJP4USFBNͷTVCDMBTTʹ࣮૷Ͱ͖ΔΑ͏ʹ ͳͬͨ͜ͱͰ-PHJDΛ౉͢ඞཁ͕ͳ͘ͳͬͨ

Slide 43

Slide 43 text

v0.9. 0 Workaround for SR-12081

Slide 44

Slide 44 text

wJ04ͰϥϯλΠϜͰΫϥογϡ͢Δ wJ04ҎԼͰ͸Ϋϥογϡ͠ͳ͍ w৽نͰ௥Ճͨ͠6OJP4USFBNΛ࡟আ͢Δͱ 
 Ϋϥογϡ͠ͳ͍ w3FMFBTFϏϧυͰ͸ൃੜ͠ͳ͍ wTVQFSDMBTTͷ(FOFSJDBSHVNFOU͕ 
 TVCDMBTTࣗ਎Λࢦఆ͢Δ৔߹ʹى͖Δ v0.9.0 Workaround for SR-12081 IUUQTCVHTTXJGUPSHCSPXTF43

Slide 45

Slide 45 text

v0.9.0 Workaround for SR-12081

Slide 46

Slide 46 text

v0.9.0 Workaround for SR-12081 /4-BZPVU"ODIPS΋TVCDMBTTΛ(FOFSJDBSHVNFOUʹ 
 ࢦఆ͍ͯ͠ΔͷͰɺ࣮૷ͷ࢓ํࣗମ͕/(ͳΘ͚Ͱ͸ͳ͍

Slide 47

Slide 47 text

public typealias UnioStream = PrimitiveStream & LogicType open class PrimitiveStream { public let input: InputWrapper public let output: OutputWrapper private let _state: Logic.State private let _extra: Logic.Extra private let _disposeBag = DisposeBag() public init(input: Logic.Input, state: Logic.State, extra: Logic.Extra) { let dependency = Dependency(input: input, state: state, extra: extra) let output = Logic.bind(from: dependency, disposeBag: _disposeBag) self.input = InputWrapper(input) self.output = OutputWrapper(output) self._state = state self._extra = extra } } v0.9.0 Workaround for SR-12081

Slide 48

Slide 48 text

public typealias UnioStream = PrimitiveStream & LogicType open class PrimitiveStream { public let input: InputWrapper public let output: OutputWrapper private let _state: Logic.State private let _extra: Logic.Extra private let _disposeBag = DisposeBag() public init(input: Logic.Input, state: Logic.State, extra: Logic.Extra) { let dependency = Dependency(input: input, state: state, extra: extra) let output = Logic.bind(from: dependency, disposeBag: _disposeBag) self.input = InputWrapper(input) self.output = OutputWrapper(output) self._state = state self._extra = extra } } v0.9.0 Workaround for SR-12081 UZQFBMJBTͰ-PHJD5ZQFͷ࣮૷Λࣗ਎ʹڧ੍͢Δܗʹͭͭ͠ ࣮ଶͷ(FOFSJDBSHVNFOUʹࢦఆ͢ΔܗʹͳΔͨΊɺ43 ͷঢ়ଶʹͳͬͯ͠·͏

Slide 49

Slide 49 text

public typealias UnioStream = AnyLogicBasedStream & LogicType open class AnyLogicBasedStream: AnyLogicBasedStreamType { public let input: InputWrapper public let output: OutputWrapper private let _stream: AnyObject required public init( input: Logic.Input, state: Logic.State, extra: Logic.Extra, logic _: Logic.Type ) where Input == Logic.Input, Output == Logic.Output { let stream = PrimitiveStream(input: input, state: state, extra: extra) self.input = stream.input self.output = stream.output self._stream = stream } } v0.9.0 Workaround for SR-12081

Slide 50

Slide 50 text

public typealias UnioStream = AnyLogicBasedStream & LogicType open class AnyLogicBasedStream: AnyLogicBasedStreamType { public let input: InputWrapper public let output: OutputWrapper private let _stream: AnyObject required public init( input: Logic.Input, state: Logic.State, extra: Logic.Extra, logic _: Logic.Type ) where Input == Logic.Input, Output == Logic.Output { let stream = PrimitiveStream(input: input, state: state, extra: extra) self.input = stream.input self.output = stream.output self._stream = stream } } v0.9.0 Workaround for SR-12081 (FOFSJDBSHVNFOUʹ*OQVUͱ0VUQVU͚ͩࢦఆ͢Δܗʹͭͭ͠ ಺෦Ͱ͸΋ͱ΋ͱͷΫϥεΛॳظԽ͍ͯ͠ΔͷͰɺࣗ਎ͷ (FOFSJDBSHVNFOUʹࣗ਎Λࢦఆ͢Δঢ়ଶΛආ͚͍ͯΔ

Slide 51

Slide 51 text

public typealias UnioStream = AnyLogicBasedStream & LogicType open class AnyLogicBasedStream: AnyLogicBasedStreamType { public let input: InputWrapper public let output: OutputWrapper private let _stream: AnyObject required public init( input: Logic.Input, state: Logic.State, extra: Logic.Extra, logic _: Logic.Type ) where Input == Logic.Input, Output == Logic.Output { let stream = PrimitiveStream(input: input, state: state, extra: extra) self.input = stream.input self.output = stream.output self._stream = stream } } v0.9.0 Workaround for SR-12081 UZQFBMJBTͰ-PHJD5ZQFΛࢦఆ͢ΔΑ͏ʹ͍ͯ͠ΔͷͰ ֎෦͔Βݟͨ৔߹ʹ͸ҎલͷόʔδϣϯͱಉҰͷ΋ͷʹ ݟ͑Δ͕ɺ࣮ࡍʹ͸*OQVUͱ0VUQVUΛࢦఆ͍ͯ͠Δঢ়ଶ ʹͳ͍ͬͯΔ

Slide 52

Slide 52 text

public protocol UnioStreamType: AnyObject { associatedtype Input: InputType associatedtype Output: OutputType var input: InputWrapper { get } var output: OutputWrapper { get } } public protocol AnyLogicBasedStreamType: UnioStreamType { init( input: Logic.Input, state: Logic.State, extra: Logic.Extra, logic _: Logic.Type ) where Input == Logic.Input, Output == Logic.Output } extension AnyLogicBasedStreamType where Self: LogicType { public init(input: Input, state: State, extra: Extra) { self.init(input: input, state: state, extra: extra, logic: Self.self) } } v0.9.0 Workaround for SR-12081

Slide 53

Slide 53 text

public protocol UnioStreamType: AnyObject { associatedtype Input: InputType associatedtype Output: OutputType var input: InputWrapper { get } var output: OutputWrapper { get } } public protocol AnyLogicBasedStreamType: UnioStreamType { init( input: Logic.Input, state: Logic.State, extra: Logic.Extra, logic _: Logic.Type ) where Input == Logic.Input, Output == Logic.Output } extension AnyLogicBasedStreamType where Self: LogicType { public init(input: Input, state: State, extra: Extra) { self.init(input: input, state: state, extra: extra, logic: Self.self) } } v0.9.0 Workaround for SR-12081 1SPUPDPMΛఆٛ͠ɺࣗ਎͕-PHJD5ZQFΛ࠾༻͍ͯ͠Δ৔߹ʹ -PHJDΛҾ਺ʹ౉͞ͳͯ͘΋ࡁΉ*OJUJBMJ[FSΛఆٛ

Slide 54

Slide 54 text

v0.10. 0 Computed Support

Slide 55

Slide 55 text

v0.10.0 Computed Support final class TimetableViewModel: UnioStream { ... struct State: StateType { let dateList = BehaviorRelay<[Date]>(value: [Date()]) } struct Output: OutputType { let elapsedTimeForIndex: Computed<(Int) -> String> } struct Extra: ExtraType { let now: () -> Date } static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let state = dependency.state let extra = dependency.extra let elapsedTimeForIndex = Computed<(Int) -> String> { let date = state.dateList.value[$0] return "\(extra.now().timeIntervalSince1970 - date.timeIntervalSince1970)" } return Output(elapsedTimeForIndex: elapsedTimeForIndex) } }

Slide 56

Slide 56 text

v0.10.0 Computed Support final class TimetableViewModel: UnioStream { ... struct State: StateType { let dateList = BehaviorRelay<[Date]>(value: [Date()]) } struct Output: OutputType { let elapsedTimeForIndex: Computed<(Int) -> String> } struct Extra: ExtraType { let now: () -> Date } static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let state = dependency.state let extra = dependency.extra let elapsedTimeForIndex = Computed<(Int) -> String> { let date = state.dateList.value[$0] return "\(extra.now().timeIntervalSince1970 - date.timeIntervalSince1970)" } return Output(elapsedTimeForIndex: elapsedTimeForIndex) } } ಈతͳॲཧΛͯ͠ɺ֎෦͔ΒΞΫηεͰ͖ΔΑ͏ʹ͢Δ

Slide 57

Slide 57 text

v0.10.0 Computed Support final class TimetableViewController: UIViewController, UITableViewDataSource { private let viewModel = TimetableViewModel( input: .init(), state: .init(), extra: .init(now: Date.init) ) func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = viewModel.output.elapsedTimeForIndex(indexPath.row) return cell } }

Slide 58

Slide 58 text

v0.10.0 Computed Support final class TimetableViewController: UIViewController, UITableViewDataSource { private let viewModel = TimetableViewModel( input: .init(), state: .init(), extra: .init(now: Date.init) ) func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = viewModel.output.elapsedTimeForIndex(indexPath.row) return cell } }

Slide 59

Slide 59 text

UnioΛ࢖ͬͯΈΑ͏ʂ

Slide 60

Slide 60 text

ViewModelͷUnioStreamԽΛ࣮ԋ IUUQTHJUIVCDPNQFBLTDDJ04@[email protected]

Slide 61

Slide 61 text

కΊʹ

Slide 62

Slide 62 text

No content