Upload
rambler-ios
View
2.482
Download
0
Embed Size (px)
Citation preview
•Структура модуля
•Конфигурация модуля
• 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! }
•Корректную конфигурацию проверять в тестах
Конфигурация модуля: 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
@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 поддерживают большинство возможностей классов
•Оптимизации
Переходы между модулями: 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) }
Тестирование: 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 } }
Зачем переходить на Swift
•Меньше файлов, выразительный синтаксис
•Модификаторы доступа, пространства имен
•Строгая типизация и явный nil
• Immutability из коробки
•Мультипарадигменность
Viper+ Swift
• Революции не случилось
•Модули пишутся быстрее
•Можно эффективно использовать возможности языка
• Тесты пишутся медленнее