viper и swift

40
SWIFT + VIPER Что изменилось с переходом на Swift

Upload: rambler-ios

Post on 22-Jan-2017

2.482 views

Category:

Technology


0 download

TRANSCRIPT

SWIFT + VIPERЧто изменилось с переходом на Swift

•Структура модуля

•Конфигурация модуля

• PONSO -> POSO

•Переходы между модулями

•Тестирование

•Общие впечатления

Структура модуля

Структура модуля: MFVIPEMPERVVM

View: ViewInput Presenter:ViewOutput, ModuleInput Interactor: InteractorInput

Router: RouterInput

router

output

view

interactor

output

Router Promise

Reactive Interactor

View Model

Future View Presentation Model

Wireframe

Passive View

Passive View

Assembly

Структура модуля

View (ViewInput)

Presenter(ViewOutput, ModuleInput)

Interactor (InteractorInput)

Router (RouterInput)

router

output

view interactor

output

Service

Service

Service

Entity

Entity

Структура модуля

View Presenter Interactor

Router

Service

Service

Service

Entity

Entity

NSObject Pure Swift

Структура модуля

protocol TodayWeatherInteractorOutput: class {

func setCurrentWeather(currentWeather: WeatherItem) }

•Разделять протоколы по файлам

•Не забывать :class для weak ссылок

Структура модуля

class TodayWeatherPresenter: TodayWeatherModuleInput, TodayWeatherViewOutput { //MARK: TodayWeatherModuleInput //MARK: TodayWeatherViewOutput }

•Ошибка: Declarations from extensions cannot be overriden yet

•Нарушается целостность кода

Структура модуля

class TodayWeatherPresenter: TodayWeatherModuleInput, TodayWeatherViewOutput { weak var view: TodayWeatherViewInput! var interactor: TodayWeatherInteractorInput! var router: TodayWeatherRouterInput! }

•Корректную конфигурацию проверять в тестах

Структура модуля

Generamba

Конфигурация модуля

Конфигурация модуля: Typhoon

class TodayWeatherAssembly: TyphoonAssembly {

enum ViperSel: Selector { case view = "view" case router = "router" //... } dynamic func presenter() -> AnyObject { return TyphoonDefinition.withClass (TodayWeatherPresenter.self) { (definition) in definition.injectProperty(ViperSel.router, with: self.router()) definition.injectProperty(ViperSel.view, with: self.view()) } } //... }

Конфигурация модуля: Typhoon

typealias WeatherItem = (String, Int)

@objc protocol TodayWeatherInteractorInput { func getWeatherData() -> WeatherItem }

method cannot be marked @objc because ... cannot be represented in Objective-C

Получаем ошибку:

Конфигурация модуля: Swift DI frameworks

Swinject

let container = Container() container.register(TodayWeatherRouterInput.self) { _ in TodayWeatherRouter() } container.register(TodayWeatherViewOutput.self) { registrator in TodayWeatherPresenter(router: registrator.resolve(TodayWeatherRouterInput.self)!) }

//Configurator let presenter = container.resolve(TodayWeatherRouterInput.self)

Конфигурация модуля: Swift DI frameworks

Dip

let dependencyContainer = DependencyContainer() { container in container.register { TodayWeatherRouter() as TodayWeatherRouterInput } container.register { TodayWeatherPresenter(router: try! container.resolve() as TodayWeatherRouterInput) as TodayWeatherViewOutput } }

//Configurator let router = try! dependencyContainer.resolve() as TodayWeatherRouter

Swift DI frameworks: Проблемы

•Существующие решения - Service locators

• Runtime возможности только для NSObject наследников

• Readonly Reflection API

Конфигурация модуля: Storyboard DI

class TodayWeatherModuleInitializer: NSObject { @IBOutlet weak var viewController: TodayWeatherViewController! override func awakeFromNib() { let configurator = TodayWeatherModuleConfigurator() configurator. configureModuleForViewInput(viewController) } }

Конфигурация модуля: Конфигуратор

class TodayWeatherModuleConfigurator: BaseModuleConfiguratorProtocol { func configureModuleForViewInput<UIViewController> (viewInput: UIViewController) { if let todayWeatherController = viewInput as? TodayWeatherViewController { configure(todayWeatherController) } } //... }

Конфигурация модуля: Конфигуратор

private func configure(viewController: TodayWeatherViewController) {

let router = TodayWeatherRouter() router.viewController = viewController let presenter = TodayWeatherPresenter() presenter.view = viewController presenter.router = router let interactor = TodayWeatherInteractor() interactor.output = presenter presenter.interactor = interactor viewController.output = presenter }

Структура модуля

View (ViewController)

Presenter Interactor

Router

Service

Service

Service

Entity

Entity

Configurator

Initializer

Storyboard

PONSO -> POSO

PONSO -> POSO

@interface DayWeather : NSObject

@property (readonly) NSString *weather; @property (readonly) NSString *temperature; @property (readonly) NSString *temperatureIcon;

- (instancetype)initWithWeather:(NSString *)weather temperature:(NSString *)temperature temperatureIcon:(NSString *)icon;

+ (instancetype)weatherWithWeather:(NSString *)weather temperature:(NSString *)temperature temperatureIcon:(NSString *)icon;

@end

enum TimeOfDay: String { case Now = "now" case Morning = "morning" case Afternoon = "afternoon" case Evening = "evening" case Night = "night" }

struct WeatherItem { let timeOfDay: TimeOfDay? let weather: String? let temperature: String? let temperatureIconName: String? }

PONSO -> POSO

PONSO -> POSO

• Immutability, thread safe из коробки

• struct и enum поддерживают большинство возможностей классов

•Оптимизации

Переходы между модулями

Переходы между модулями

Swizzling ?

Переходы между модулями

Protocol Extensions

Переходы между модулями: Transition Handler

protocol ViperModuleTransitionHandler: class { func openModule(segueIdentifier: String, configurationBlock: ConfigurationBlock) func closeModule() }

extension ViperModuleTransitionHandler where Self: UIViewController { func openModule(segueIdentifier:String, block:ConfigurationBlock) { let segueInfo = SegueInfo() segueInfo.configurationBlock = block performSegueWithIdentifier(segueIdentifier, sender: segueInfo) }}

Переходы между модулями: Transition Handler

Переходы между модулями: Module View

override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject?) { guard let configurationHolder = segue.destinationViewController else {return} configurationBlock(configurationHolder.moduleInput) }

Тестирование

Тестирование

OCMock

OCMProtocolMock

OCMClassMockOCMVerify

OCMStub

Тестирование

Spy Stub Mock

Писать моки самому не сложно

Тестирование: Mocking

func testConfigureWithWeatherId() { //given let presenter = TodayWeatherPresenter() let interactor = MockInteractorInput() presenter.interactor = interactor //when presenter.configureViewForWeatherId("testWeatherId") //then XCTAssertTrue(interactor.didCallObtain) XCTAssertEqual("testOfferId", interactor.offerId) }

Тестирование: Mocking

class MockInteractorInput : TodayWeatherInteractorInput { var didCallObtain = false var weatherId : String! func obtainWeatherById(weatherId: String) { didCallObtain = true self.weatherId = weatherId } }

Тестирование

PRIVATE

INTERNAL

PUBLIC

@testable

В результате ?

Зачем переходить на Swift

•Меньше файлов, выразительный синтаксис

•Модификаторы доступа, пространства имен

•Строгая типизация и явный nil

• Immutability из коробки

•Мультипарадигменность

Viper+ Swift

• Революции не случилось

•Модули пишутся быстрее

•Можно эффективно использовать возможности языка

• Тесты пишутся медленнее

Спасибо за внимание