рд╣рдо рдХрд┐рд╕ рддрд░рд╣ QIWI рдореЗрдВ MVVM рдХреЗ рднреАрддрд░ View рдФрд░ ViewModel рдХреЗ рдмреАрдЪ рдмрд╛рддрдЪреАрдд рдХреА рдПрдХ рдЖрдо рд╢реИрд▓реА рдореЗрдВ рдЖрдП

рдкреНрд░рд╛рд░рдВрдн рдореЗрдВ, рдкреВрд░реА рдкрд░рд┐рдпреЛрдЬрдирд╛ рдСрдмреНрдЬреЗрдХреНрдЯрд┐рд╡-рд╕реА рдореЗрдВ рд▓рд┐рдЦреА рдЧрдИ рдереА рдФрд░ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рд░рд┐рдПрдХреНрдЯрд┐рд╡рдХреЛрдЖ рд╕рдВрд╕реНрдХрд░рдг 2.0 рдореЗрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛


рд╡реНрдпреВ рдореЙрдбрд▓ рдХреЗ рдЧреБрдгреЛрдВ рдХреЗ рдмрд╛рдЗрдВрдбрд┐рдВрдЧ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ View рдФрд░ ViewModel рдХреЗ рдмреАрдЪ рдмрд╛рддрдЪреАрдд рдХреЛ рдЕрдВрдЬрд╛рдо рджрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛, рдФрд░ рд╕рднреА рдареАрдХ рд╣реЛ рдЬрд╛рдПрдВрдЧреЗ, рд╕рд┐рд╡рд╛рдп рдЗрд╕рдХреЗ рдХрд┐ рдЗрд╕ рддрд░рд╣ рдХреЗ рдХреЛрдб рдХреЛ рдбрд┐рдмрдЧ рдХрд░рдирд╛ рдмрд╣реБрдд рдореБрд╢реНрдХрд┐рд▓ рдерд╛ред рд╕реНрдЯреИрдХ рдЯреНрд░реЗрд╕ рдореЗрдВ рдЯрд╛рдЗрдкрд┐рдВрдЧ рдФрд░ рджрд▓рд┐рдпрд╛ рдХреА рдХрдореА рдХреЗ рдХрд╛рд░рдг рд╕рднреА :(


рдФрд░ рдЕрдм рдпрд╣ рд╕реНрд╡рд┐рдлреНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рд╕рдордп рд╣реИред рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╣рдордиреЗ рдмрд┐рд▓реНрдХреБрд▓ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХрд┐рдП рдмрд┐рдирд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ред ViewModel рдкрд░ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдХрд╣реЗ рдЧрдП рддрд░реАрдХреЗ рджреЗрдЦреЗрдВ, рдФрд░ ViewModel рдиреЗ рдПрдХ рдкреНрд░рддрд┐рдирд┐рдзрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдкрдиреЗ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреА рд╕реВрдЪрдирд╛ рджреА:


protocol ViewModelDelegate { func didUpdateTitle(newTitle: String) } class View: UIView, ViewModelDelegate { var viewModel: ViewModel func didUpdateTitle(newTitle: String) { //handle viewModel updates } } class ViewModel { weak var delegate: ViewModelDelegate? func handleTouch() { //respond to some user action } } 

рдпрд╣ рдЕрдЪреНрдЫрд╛ рд▓рдЧ рд░рд╣рд╛ рд╣реИред рд▓реЗрдХрд┐рди рдЬреИрд╕реЗ-рдЬреИрд╕реЗ ViewModel рдмрдврд╝рддрд╛ рдЧрдпрд╛, рд╣рдордиреЗ ViewModel рджреНрд╡рд╛рд░рд╛ рдЙрддреНрдкрд╛рджрд┐рдд рд╣рд░ рдЫреАрдВрдХ рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рддрд┐рдирд┐рдзрд┐ рдореЗрдВ рддрд░реАрдХреЛрдВ рдХрд╛ рдПрдХ рдЧреБрдЪреНрдЫрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд┐рдпрд╛:


 protocol ViewModelDelegate { func didUpdate(title: String) func didUpdate(subtitle: String) func didReceive(items: [SomeItem]) func didReceive(error: Error) func didChangeLoading(isLoafing: Bool) //...  } 

рдкреНрд░рддреНрдпреЗрдХ рд╡рд┐рдзрд┐ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдФрд░ рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк рд╣рдореЗрдВ рджреГрд╢реНрдп рдореЗрдВ рддрд░реАрдХреЛрдВ рд╕реЗ рдПрдХ рд╡рд┐рд╢рд╛рд▓ рдлреБрдЯрдХреНрд▓реЙрде рдорд┐рд▓рддрд╛ рд╣реИред рдпрд╣ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рдирд╣реАрдВ рд▓рдЧрддрд╛ рд╣реИред рдмрд┐рд▓реНрдХреБрд▓ рд╢рд╛рдВрдд рдирд╣реАрдВред рдпрджрд┐ рдЖрдк рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╕реЛрдЪрддреЗ рд╣реИрдВ, рдпрджрд┐ рдЖрдк RxSwift рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдЖрдкрдХреЛ рдПрдХ рд╕рдорд╛рди рд╕реНрдерд┐рддрд┐ рдорд┐рд▓реЗрдЧреА, рд▓реЗрдХрд┐рди рдкреНрд░рддрд┐рдирд┐рдзрд┐ рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп, рд╡рд┐рднрд┐рдиреНрди ViewModel рдЧреБрдгреЛрдВ рдХреЗ рд▓рд┐рдП рдмрд╛рдЗрдВрдбрд░реЛрдВ рдХрд╛ рдПрдХ рдЧреБрдЪреНрдЫрд╛ рд╣реЛрдЧрд╛ред


рдЖрдЙрдЯрдкреБрдЯ рд╕реНрд╡рдпрдВ рд╕реБрдЭрд╛рд╡ рджреЗрддрд╛ рд╣реИ: рдЖрдкрдХреЛ рд╕рднреА рддрд░реАрдХреЛрдВ рдХреЛ рдПрдХ рдореЗрдВ рдорд┐рд▓рд╛рдиреЗ рдХреА рдЬрд░реВрд░рдд рд╣реИ рдФрд░ рдПрдиреНрдпреВрдорд░реЗрд╢рди рдЧреБрдг рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рд╕реЗ рд╣реИрдВ:


 enum ViewModelEvent { case updateTitle(String) case updateSubtitle(String) case items([SomeItem]) case error(Error) case loading(Bool) //...  } 

рдкрд╣рд▓реА рдирдЬрд╝рд░ рдореЗрдВ, рд╕рд╛рд░ рдирд╣реАрдВ рдмрджрд▓рддрд╛ рд╣реИред рд▓реЗрдХрд┐рди рдЫрд╣ рддрд░реАрдХреЛрдВ рдХреЗ рдмрдЬрд╛рдп, рд╣рдореЗрдВ рдПрдХ рд╕реНрд╡рд┐рдЪ рдХреЗ рд╕рд╛рде рдорд┐рд▓рддрд╛ рд╣реИ:


 func handle(event: ViewModelEvent) { switch event { case .updateTitle(let newTitle): //... case .updateSubtitle(let newSubtitle): //... case .items(let newItems): //... case .error(let error): //... case .loading(let isLoading): //... } } 

рд╕рдорд░реВрдкрддрд╛ рдХреЗ рд▓рд┐рдП, рдЖрдк ViewModel рдореЗрдВ рдПрдХ рдФрд░ рдЧрдгрдирд╛ рдФрд░ рдЙрд╕рдХреЗ рд╣реИрдВрдбрд▓рд░ рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВ:


 enum ViewEvent { case touchButton case swipeLeft } class ViewModel { func handle(event: ViewEvent) { switch event { case .touchButton: //... case .swipeLeft: //... } } } 

рдпрд╣ рд╕рдм рдмрд╣реБрдд рдЕрдзрд┐рдХ рд╕рдВрдХреНрд╖рд┐рдкреНрдд рджрд┐рдЦрддрд╛ рд╣реИ, рд╕рд╛рде рд╣реА рдпрд╣ рд╡реНрдпреВ рдФрд░ рд╡реНрдпреВрдореЙрдбрд▓ рдХреЗ рдмреАрдЪ рдмрд╛рддрдЪреАрдд рдХрд╛ рдПрдХ рдПрдХрд▓ рдмрд┐рдВрджреБ рджреЗрддрд╛ рд╣реИ, рдЬреЛ рдХреЛрдб рдХреА рдкрдардиреАрдпрддрд╛ рдХреЛ рдмрд╣реБрдд рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рдкреНрд░рднрд╛рд╡рд┐рдд рдХрд░рддрд╛ рд╣реИред рдпрд╣ рдЬреАрдд-рдЬреАрдд рдмрддрд╛рддреА рд╣реИ - рдФрд░ рдкреБрд▓ рдЕрдиреБрд░реЛрдз рдХреА рд╕рдореАрдХреНрд╖рд╛ рддреНрд╡рд░рд┐рдд рд╣реЛ рдЬрд╛рддреА рд╣реИ, рдФрд░ рдирд╡рд╛рдЧрдВрддреБрдХ рдЬрд▓реНрджреА рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ рдЖрддреЗ рд╣реИрдВред


рд▓реЗрдХрд┐рди рд░рд╛рдордмрд╛рдг рдирд╣реАрдВред рд╕рдорд╕реНрдпрд╛ рддрдм рдЙрддреНрдкрдиреНрди рд╣реЛрддреА рд╣реИ рдЬрдм рдПрдХ рджреГрд╢реНрдп рдореЙрдбрд▓ рдЕрдкрдиреА рдШрдЯрдирд╛рдУрдВ рдХреЛ рдХрдИ рджреГрд╢реНрдпреЛрдВ рдореЗрдВ рд░рд┐рдкреЛрд░реНрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдХрдВрдЯреЗрдирд░рд╡реНрдпреВ рдФрд░ рдХрдВрдЯреЗрдВрдЯрд╡реНрдпреВ (рдПрдХ рджреВрд╕рд░реЗ рдореЗрдВ рдПрдореНрдмреЗрдбреЗрдб рд╣реИ)ред рд╕рдорд╛рдзрд╛рди, рдлрд┐рд░ рд╕реЗ, рдЕрдкрдиреЗ рдЖрдк рдЙрдарддрд╛ рд╣реИ, рд╣рдо рдкреНрд░рддрд┐рдирд┐рдзрд┐ рдХреЗ рдмрдЬрд╛рдп рдПрдХ рдирдпрд╛ рд╡рд░реНрдЧ рд▓рд┐рдЦрддреЗ рд╣реИрдВ:


 class Output<Event> { var handlers = [(Event) -> Void]() func send(_ event: Event) { for handler in handlers { handler(event) } } } 

handlers рд╕рдВрдкрддреНрддрд┐ рдореЗрдВ, handlers рдХреЙрд▓ рдХреЛ handle(event:) рд╡рд┐рдзрд┐рдпреЛрдВ handle(event:) рд╕рд╛рде рдмреБрдХрдорд╛рд░реНрдХ рд╕реНрдЯреЛрд░ рдХрд░рддреЗ рд╣реИрдВ, рдФрд░ рдЬрдм рд╣рдо send(_ event:) рд╡рд┐рдзрд┐ рдХрд╣рддреЗ рд╣реИрдВ, рддреЛ рд╣рдо рдЗрд╕ рдЗрд╡реЗрдВрдЯ рдХреЗ рд╕рд╛рде рд╕рднреА рд╣реИрдВрдбрд▓рд░ рдХреЛ рдХреЙрд▓ рдХрд░рддреЗ рд╣реИрдВред рдФрд░ рдлрд┐рд░, рд╕рдорд╕реНрдпрд╛ рд╣рд▓ рд╣реЛ рдЧрдИ рд▓рдЧрддреА рд╣реИ, рд▓реЗрдХрд┐рди рд╣рд░ рдмрд╛рд░ рдЬрдм рдЖрдк View - ViewModel рдХреЛ рдмрд╛рдВрдзрддреЗ рд╣реИрдВ, рддреЛ рдЖрдкрдХреЛ рдпрд╣ рд▓рд┐рдЦрдирд╛ рд╣реЛрдЧрд╛:


 vm.output.handlers.append({ [weak view] event in DispatchQueue.main.async { view?.handle(event: event) } }) view.output.handlers.append({ [weak vm] event in vm?.handle(event: event) }) 

рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рдирд╣реАрдВ рд╣реИред
рд╣рдо рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХреЗ рд╕рд╛рде рджреГрд╢реНрдп рдФрд░ ViewModel рдХреЛ рдмрдВрдж рдХрд░рддреЗ рд╣реИрдВ:


 protocol ViewModel { associatedtype ViewEvent associatedtype ViewModelEvent var output: Output<ViewModelEvent> { get } func handle(event: ViewEvent) func start() } protocol View: ViewModelContainer { associatedtype ViewModelEvent associatedtype ViewEvent var output: Output<ViewEvent> { get } func setupBindings() func handle(event: ViewModelEvent) } 

рдХреНрдпреЛрдВ start() рдФрд░ setupBindings() рддрд░реАрдХреЛрдВ рдХреА рдЬрд░реВрд░рдд рд╣реИ - рд╣рдо рдмрд╛рдж рдореЗрдВ рд╡рд░реНрдгрди рдХрд░реЗрдВрдЧреЗред рд╣рдо рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХреЗ рд▓рд┐рдП рдПрдХреНрд╕рдЯреЗрдВрд╢рди рд▓рд┐рдЦ рд░рд╣реЗ рд╣реИрдВ:


 extension View where Self: NSObject { func bind<ViewModelType: ViewModel>(with vm: ViewModelType?) where ViewModelType.ViewModelEvent == ViewModelEvent, ViewModelType.ViewEvent == ViewEvent { guard let vm = vm else { return } vm.output.handlers.append({ [weak self] event in DispatchQueue.main.async { self?.handle(event: event) } }) output.handlers.append({ [weak vm] event in vm?.handle(event: event) }) setupBindings() vm.start() } } 

рдФрд░ рд╣рдореЗрдВ рдХрд┐рд╕реА рднреА View - ViewModel рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рддреИрдпрд╛рд░ рд╡рд┐рдзрд┐ рдкреНрд░рд╛рдкреНрдд рд╣реЛрддреА рд╣реИ, рдЬрд┐рд╕рдореЗрдВ рд╕реЗ рдореИрдЪ рд╣реЛрддреЗ рд╣реИрдВред start() рд╡рд┐рдзрд┐ рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рддреА рд╣реИ рдХрд┐ рдЬрдм рдЗрд╕реЗ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рджреГрд╢реНрдп рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рд╕рднреА рдШрдЯрдирд╛рдУрдВ рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдЧрд╛ рдЬреЛ ViewModel рд╕реЗ рднреЗрдЬреЗ рдЬрд╛рдПрдВрдЧреЗ, рдФрд░ рдЕрдЧрд░ рдЖрдкрдХреЛ ViewModel рдХреЛ рдЕрдкрдиреЗ рд╕реНрд╡рдпрдВ рдХреЗ setupBindings() рдлреЗрдВрдХрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рддреЛ setupBindings() рд╡рд┐рдзрд┐ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреА, рдЗрд╕рд▓рд┐рдП рдЗрд╕ рд╡рд┐рдзрд┐ рдХреЛ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рд╡рд┐рд╕реНрддрд╛рд░ рд╕реЗ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ ' рдИред


рдпрд╣ рдкрддрд╛ рдЪрд▓рддрд╛ рд╣реИ рдХрд┐ View рдФрд░ ViewModel рдХреЗ рдмреАрдЪ рд╕рдВрдмрдВрдз рдХреЗ рд▓рд┐рдП, рдЙрдирдХреЗ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдмрд┐рд▓реНрдХреБрд▓ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдирд╣реАрдВ рд╣реИрдВ, рдореБрдЦреНрдп рдмрд╛рдд рдпрд╣ рд╣реИ рдХрд┐ View ViewModel рдШрдЯрдирд╛рдУрдВ рдХреЛ рд╕рдВрднрд╛рд▓ рд╕рдХрддрд╛ рд╣реИ, рдФрд░ рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрддред рдФрд░ рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП рд╕реНрдЯреЛрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП ViewModel рдХрд╛ рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рд▓рд┐рдВрдХ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдЗрд╕рдХрд╛ рд╕рд╛рдорд╛рдиреНрдпреАрдХреГрдд рд╕рдВрд╕реНрдХрд░рдг рд╣реИ, рдЖрдк рдПрдХ рдЕрддрд┐рд░рд┐рдХреНрдд TypeEureure рдЖрд╡рд░рдг рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВ (рдХреНрдпреЛрдВрдХрд┐ associatedtype рдкреНрд░рдХрд╛рд░ рдХреЗ рд╕рд╛рде рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдкреНрд░рдХрд╛рд░ рдХреЗ рдЧреБрдгреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдЕрд╕рдВрднрд╡ рд╣реИ):


 class AnyViewModel<ViewModelEvent, ViewEvent>: ViewModel { var output: Output<ViewModelEvent> let startClosure: EmptyClosure let handleClosure: (ViewEvent) -> Void let vm: Any? private var isStarted = false init?<ViewModelType: ViewModel>(with vm: ViewModelType?) where ViewModelType.ViewModelEvent == ViewModelEvent, ViewModelType.ViewEvent == ViewEvent { guard let vm = vm else { return nil } self.output = vm.output self.vm = vm self.startClosure = { [weak vm] in vm?.start() } self.handleClosure = { [weak vm] in vm?.handle(event: $0) }//vm.handle } func start() { if !isStarted { isStarted = true startClosure() } } func handle(event: ViewEvent) { handleClosure(event) } } 

рдЖрдЧреЗ рдФрд░ рднреА


рд╣рдордиреЗ рдЖрдЧреЗ рдЬрд╛рдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛, рдФрд░ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рд╕рдВрдкрддреНрддрд┐ рдХреЛ рджреГрд╢реНрдп рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдирд╣реАрдВ рдХрд┐рдпрд╛, рд▓реЗрдХрд┐рди рд░рдирдЯрд╛рдЗрдо рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЗрд╕реЗ рд╕реЗрдЯ рдХрд░реЗрдВ, рдХреБрд▓ рдореЗрдВ, View рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХрд╛ рд╡рд┐рд╕реНрддрд╛рд░ рдЗрд╕ рддрд░рд╣ рд╕реЗ рдирд┐рдХрд▓рд╛:


 extension View where Self: NSObject { func bind<ViewModelType: ViewModel>(with vm: ViewModelType?) where ViewModelType.ViewModelEvent == ViewModelEvent, ViewModelType.ViewEvent == ViewEvent { guard let vm = AnyViewModel(with: vm) else { return } vm.output.handlers.append({ [weak self] event in if #available(iOS 10.0, *) { RunLoop.main.perform(inModes: [.default], block: { self?.handle(event: event) }) } else { DispatchQueue.main.async { self?.handle(event: event) } } }) output.handlers.append({ [weak vm] event in vm?.handle(event: event) }) p_viewModelSaving = vm setupBindings() vm.start() } private var p_viewModelSaving: Any? { get { return objc_getAssociatedObject(self, &ViewModelSavingHandle) } set { objc_setAssociatedObject(self, &ViewModelSavingHandle, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } var viewModel: AnyViewModel<ViewModelEvent, ViewEvent>? { return p_viewModelSaving as? AnyViewModel<ViewModelEvent, ViewEvent> } } 

рдпрд╣ рдПрдХ рд╡рд┐рд╡рд╛рджрд╛рд╕реНрдкрдж рдХреНрд╖рдг рд╣реИ, рд▓реЗрдХрд┐рди рд╣рдордиреЗ рддрдп рдХрд┐рдпрд╛ рдХрд┐ рд╣рд░ рдмрд╛рд░ рдЗрд╕ рд╕рдВрдкрддреНрддрд┐ рдХреА рдШреЛрд╖рдгрд╛ рди рдХрд░рдирд╛ рдЕрдзрд┐рдХ рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реЛрдЧрд╛ред


рдЯреЗрдореНрдкрд▓реЗрдЯреНрд╕


рдпрд╣ рджреГрд╖реНрдЯрд┐рдХреЛрдг Xcode рдЯреЗрдореНрдкреНрд▓реЗрдЯ рдХреЗ рд╕рд╛рде рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдлрд┐рдЯ рдмреИрдарддрд╛ рд╣реИ рдФрд░ рдЖрдкрдХреЛ рдХреБрдЫ рдХреНрд▓рд┐рдХреЛрдВ рдореЗрдВ рдмрд╣реБрдд рдЬрд▓реНрджреА рдореЙрдбреНрдпреВрд▓ рдЙрддреНрдкрдиреНрди рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП рдЯреЗрдореНрдкрд▓реЗрдЯ рджреЗрдЦреЗрдВ:


 final class ___VARIABLE_moduleName___ViewController: UIView, View { var output = Output<___VARIABLE_moduleName___ViewModel.ViewEvent>() override func viewDidLoad() { super.viewDidLoad() setupViews() } private func setupViews() { //Do layout and more } func handle(event: ___VARIABLE_moduleName___ViewModel.ViewModelEvent) { } } 

рдФрд░ ViewModel рдХреЗ рд▓рд┐рдП:


 final class ___VARIABLE_moduleName___ViewModel: ViewModel { var output = Output<ViewModelEvent>() func start() { } func handle(event: ViewEvent) { } } extension ___VARIABLE_moduleName___ViewModel { enum ViewEvent { } enum ViewModelEvent { } } 

рдФрд░ рдХреЛрдб рдореЗрдВ рдореЙрдбреНрдпреВрд▓ рдЗрдирд┐рд╢рд┐рдпрд▓рд╛рдЗрдЬрд╝реЗрд╢рди рдмрдирд╛рдиреЗ рдореЗрдВ рдХреЗрд╡рд▓ рддреАрди рд▓рд╛рдЗрдиреЗрдВ рд▓рдЧрддреА рд╣реИрдВ:


 let viewModel = SomeViewModel() let view = SomeView() view.bind(with: viewModel) 

рдирд┐рд╖реНрдХрд░реНрд╖


рдирддреАрдЬрддрди, рд╣рдореЗрдВ View рдФрд░ ViewModel рдХреЗ рдмреАрдЪ рд╕рдВрджреЗрд╢реЛрдВ рдХреЗ рдЖрджрд╛рди-рдкреНрд░рджрд╛рди рдХрд╛ рдПрдХ рд▓рдЪреАрд▓рд╛ рддрд░реАрдХрд╛ рдорд┐рд▓рд╛, рдЬрд┐рд╕рдореЗрдВ рдПрдХ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдмрд┐рдВрджреБ рд╣реИ рдФрд░ рдпрд╣ Xcode рдХреЛрдб рдкреАрдврд╝реА рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИред рдЗрд╕ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдиреЗ рд╕реБрд╡рд┐рдзрд╛рдУрдВ рдХреЗ рд╡рд┐рдХрд╛рд╕ рдХреЛ рдЧрддрд┐ рджреЗрдирд╛ рдФрд░ рдкреБрд▓-рд░рд┐рд╡реНрдпреВ рдХреА рд╕рдореАрдХреНрд╖рд╛ рдХреЛ рдЧрддрд┐ рджреЗрдирд╛ рд╕рдВрднрд╡ рдмрдирд╛ рджрд┐рдпрд╛, рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛ рдЗрд╕рдиреЗ рдХреЛрдб рдХреА рдкрдардиреАрдпрддрд╛ рдФрд░ рд╕рд░рд▓рддрд╛ рдХреЛ рдмрдврд╝рд╛рдпрд╛ рдФрд░ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рд▓реЗрдЦрди рдХреЛ рд╕рд░рд▓ рдмрдирд╛рдпрд╛ (рдЗрд╕ рддрдереНрдп рдХреЗ рдХрд╛рд░рдг рдХрд┐, рджреГрд╢реНрдп рдореЙрдбрд▓ рд╕реЗ рдШрдЯрдирд╛рдУрдВ рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд╡рд╛рдВрдЫрд┐рдд рдЕрдиреБрдХреНрд░рдо рдХреЛ рдЬрд╛рдирддреЗ рд╣реБрдП, рдпреВрдирд┐рдЯ-рдЯреЗрд╕реНрдЯ рд▓рд┐рдЦрдирд╛ рдЖрд╕рд╛рди рд╣реИ, рдЬрд┐рд╕рдХреЗ рд╕рд╛рде рдпрд╣ рдХреНрд░рдо рд╣реИ рдЧрд╛рд░рдВрдЯреА рджреА рдЬрд╛ рд╕рдХрддреА рд╣реИ)ред рдпрджреНрдпрдкрд┐ рдпрд╣ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╣рдорд╛рд░реЗ рд╕рд╛рде рд╣рд╛рд▓ рд╣реА рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рд╢реБрд░реВ рд╣реБрдЖ рд╣реИ, рд╣рдо рдЖрд╢рд╛ рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рдпрд╣ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЕрдкрдиреЗ рдЖрдк рдХреЛ рдФрдЪрд┐рддреНрдп рджреЗрдЧрд╛ рдФрд░ рд╡рд┐рдХрд╛рд╕ рдХреЛ рдмрд╣реБрдд рд╕рд░рд▓ рдХрд░реЗрдЧрд╛ред


рдкреБрдирд╢реНрдЪ


рдФрд░ рдЖрдИрдУрдПрд╕ рдХреЗ рд▓рд┐рдП рд╡рд┐рдХрд╛рд╕ рдХреЗ рдкреНрд░реЗрдорд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдЫреЛрдЯреА рд╕реА рдШреЛрд╖рдгрд╛ - рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЗрд╕ рдЧреБрд░реБрд╡рд╛рд░, 25 рдЬреБрд▓рд╛рдИ, рд╣рдо рдПрдЖрд░рдЯреА-рд╕реНрдкреЗрд╕ рдореЗрдВ рдПрдХ рдЖрдИрдУрдПрд╕ mitap рдкрдХрдбрд╝ рд▓реЗрдВрдЧреЗ, рдкреНрд░рд╡реЗрд╢ рдирд┐: рд╢реБрд▓реНрдХ рд╣реИ, рдЖрдУред

Source: https://habr.com/ru/post/hi460875/


All Articles