Post on 09-Feb-2017
www.softwarehut.pl 1
Dependency Injection + iOSMichał Karpowicz
www.softwarehut.pl 2
00WSTĘP
www.softwarehut.pl 3
1.Dependency Injection2.TDD3.DYI4.Typhoon
www.softwarehut.pl 4
Projekt
• Dwa View Controllery – DashboardViewController i CustomersViewController• CustomersDataSource• CustomerService• SessionManager
www.softwarehut.pl 5
01Dependency Injection
www.softwarehut.pl 6
Inversion of Control
www.softwarehut.pl 7
Cele
•Podział na moduły•Rozszerzalność
www.softwarehut.pl 8
Dependency Injection
class CustomersDataSource: NSObject { let service: CustomerService override init() { service = CustomerService(manager: SessionManager(baseUrl: "https://api.customer.com", configuration: NSURLSessionConfiguration(), timeout: 30)) } }
www.softwarehut.pl 9
Dependency Injection
www.softwarehut.pl 10
Rodzaje DI
•Extract and override•Method injection•Property injection•Constructor injection
www.softwarehut.pl 11
DEPENDENCY INJECTION?
import UIKitimport Foundation
class UserCredentials { private let authorizationTokenKey = "AuthorizationTokenKey" func getAuthorizationToken() -> String { let value = NSUserDefaults.standardUserDefaults().stringForKey(authorizationTokenKey) guard let retVal = value else { return "" } return retVal }}
www.softwarehut.pl 12
EXTRACT AND OVERRIDE
private let authorizationTokenKey = "AuthorizationTokenKey" func getAuthorizationToken() -> String { let value = userDefaults().stringForKey(authorizationTokenKey) guard let retVal = value else { return "" } return retVal } func userDefaults() -> NSUserDefaults { return NSUserDefaults.standardUserDefaults() }
www.softwarehut.pl 13
METHOD INJECTION
private let authorizationTokenKey = "AuthorizationTokenKey" func getAuthorizationToken(userDefaults userDefaults: NSUserDefaults) -> String { let value = userDefaults.stringForKey(authorizationTokenKey) guard let retVal = value else { return "" } return retVal }
www.softwarehut.pl 14
PROPERTY INJECTION
class UserCredentials { var userDefaults: NSUserDefaults! private let authorizationTokenKey = "AuthorizationTokenKey" func getAuthorizationToken() -> String { let value = userDefaults.stringForKey(authorizationTokenKey) guard let retVal = value else { return "" } return retVal }}
let credentials = UserCredentials()credentials.userDefaults = NSUserDefaults.standardUserDefaults()
www.softwarehut.pl 15
CONSTRUCTOR INJECTION
private let userDefaults: NSUserDefaults private let authorizationTokenKey = "AuthorizationTokenKey" init(userDefaults: NSUserDefaults) { self.userDefaults = userDefaults } func getAuthorizationToken() -> String { let value = userDefaults.stringForKey(authorizationTokenKey) guard let retVal = value else { return "" } return retVal }
let credentials = UserCredentials(NSUserDefaults.standardUserDefaults())
www.softwarehut.pl 16
Dependency Injection
class CustomersDataSource: NSObject { let service: CustomerService override init() { service = CustomerService(manager: SessionManager(baseUrl: "https://api.customer.com", configuration: NSURLSessionConfiguration(), timeout: 30)) } }
www.softwarehut.pl 17
Dependency Injection
let service: CustomerService init(customerService: CustomerService) { service = customerService }
www.softwarehut.pl 18
Dependency Injection
• Niezmienność obiektów• Klasy łatwe do testowania
www.softwarehut.pl 19
02TDD
www.softwarehut.pl 20
TDD
TDD + Dependency Injection = <3
www.softwarehut.pl 21
TDD
Networking Tests
www.softwarehut.pl 22
TDD
Response mock
class MockGETSessionManager: NSObject, SessionManagerProtocol { var success: ((AnyObject) -> ())! func GET(url: String, parameters: AnyObject?, success: (AnyObject) -> (), failure: () -> ()) { self.success = success } }
protocol SessionManagerProtocol { func GET(url: String, parameters: AnyObject?, success: (AnyObject) -> (), failure: () -> ())}
www.softwarehut.pl 23
TDD
Testowanie mockafunc testMockGET() { let mockSessionManager = MockGETSessionManager() let customerService = CustomerService(sessionManager: mockSessionManager) let customerDataSource = CustomersDataSource(customerService: customerService) customerDataSource.fetchData() mockSessionManager.success(NSData()) XCTAssertNotNil(customerDataSource.customers) }
www.softwarehut.pl 24
TDD
Frameworki• OCMockito (Objective-C) - https://github.com/jonreid/OCMockito• Mockingjay (Swift) - https://github.com/kylef/Mockingjay
www.softwarehut.pl 25
03DYI
www.softwarehut.pl 26
DIY
www.softwarehut.pl 27
DIY
class Injector: NSObject { func dashboardViewController() -> DashboardViewController { let viewController = DashboardViewController() viewController.injector = self return viewController }}
www.softwarehut.pl 28
DIY
class AppDelegate: UIResponder, UIApplicationDelegate {
/…/let injector = Injector()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window?.rootViewController = injector.dashboardViewController() return true }/.../}
www.softwarehut.pl 29
DIY
func sessionManager() -> SessionManager { let sessionManager = SessionManager(baseUrl: "https://api.customer.com", configuration: NSURLSessionConfiguration(), timeout: 30) return sessionManager} func customerService() -> CustomerService { let customerService = CustomerService(sessionManager: sessionManager()) return customerService}func customerDataSource() -> CustomersDataSource { let dataSource = CustomersDataSource(customerService: customerService()) return dataSource }func customersViewController() -> CustomersViewController { let viewController = CustomersViewController() viewController.injector = self viewController.dataSource = customerDataSource() return viewController}
www.softwarehut.pl 30
DIY
class DashboardViewController: UIViewController {
/.../
func showCustomersView() { let viewController = injector?.customersViewController() navigationController?.pushViewController(viewController!, animated: true) }}
www.softwarehut.pl 31
04TYPHOON
www.softwarehut.pl 32
Typhoon?
http://typhoonframework.org/
• Nie inwazyjny• Wsparcie constructor i property injection• Wstrzykiwanie view controllerów• Integracja ze storyboardami• Minimalne obciążenie CPU• Battle-tested• Aktywnie rozwijany• “Swift”!
www.softwarehut.pl 33
Typhoon - Podstawy
1.Assembly – TyphoonAssembly2.Definition – TyphoonDefinition
www.softwarehut.pl 34
Typhoon - Setup
1.Instalacja2.Stworzenie ApplicationAssembly – subclass TyphoonAssembly3.Dodanie listy TyphoonInitialAssembies do Info.plis
That’s it!
www.softwarehut.pl 35
Typhoon - Instalacja
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
target 'DI-Typhoon' do pod 'Typhoon'End
Podfile:
pod installTerminal:
www.softwarehut.pl 36
Typhoon – ApplicationAssembly
import Typhoon
class ApplicationAssembly: TyphoonAssembly { dynamic func appDelegate() -> AnyObject { return TyphoonDefinition.withClass(AppDelegate.self) { definition in } }}
www.softwarehut.pl 37
Typhoon
Real life scenario
www.softwarehut.pl 38
Typhoon – Projekt startowy
Lista TyphoonAssemblies:• ApplicationAssembly• ViewControllerAssembly• DataSourceAssembly• CoreServicesAssembly
www.softwarehut.pl 39
ApplicationAssembly
class ApplicationAssembly: TyphoonAssembly { var viewControllerAssembly: ViewControllerAssembly! dynamic func appDelegate() -> AnyObject { return TyphoonDefinition.withClass(AppDelegate.self) { definition in definition.injectProperty("viewControllerAssembly", with: self.viewControllerAssembly) } }}
www.softwarehut.pl 40
ViewControllerAssembly
class ViewControllerAssembly: TyphoonAssembly { var dataSourceAssembly: DataSourceAssembly! dynamic func customersViewController() -> AnyObject { return TyphoonDefinition.withClass(CustomersViewController.self) { definition in definition.useInitializer("init") { initializer in } definition.injectProperty("dataSource", with: self.dataSourceAssembly.customerDataSource()) definition.injectProperty("viewControllerAssembly", with: self) } } /.../}
www.softwarehut.pl 41
DataSourceAssembly
class DataSourceAssembly: TyphoonAssembly { var coreServicesAssembly: CoreServicesAssembly! dynamic func customerDataSource() -> AnyObject { return TyphoonDefinition.withClass(CustomersDataSource.self) { definition in definition.useInitializer("initWithCustomerService:") { initializer in initializer.injectParameterWith(self.coreServicesAssembly.customerService()) } } }}
www.softwarehut.pl 42
CoreServicesAssembly
dynamic func sessionManager() -> AnyObject { return TyphoonDefinition.withClass(SessionManager.self) { definition in definition.useInitializer("initWithBaseUrl:configuration:timeout:") { initializer in initializer.injectParameterWith("https://api.customer.com") initializer.injectParameterWith(NSURLSessionConfiguration()) initializer.injectParameterWith(30) } } }dynamic func customerService() -> AnyObject { return TyphoonDefinition.withClass(CustomerService.self) { definition in definition.useInitializer("initWithSessionManager:") { initializer in initializer.injectParameterWith(self.sessionManager()) } } }
www.softwarehut.pl 43
AppDelegate
class AppDelegate: UIResponder, UIApplicationDelegate {
/…/var viewControllerAssembly: ViewControllerAssembly!
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window?.rootViewController = viewControllerAssembly.dashboardViewController() as! DashboardViewController return true }/.../}
www.softwarehut.pl 44
Typhoon
Dlaczego “Swift”?
www.softwarehut.pl 45
Typhoon
www.softwarehut.pl 46
PYTANIA?
www.softwarehut.pl 47
PYTANIA I CHABORY
www.softwarehut.pl 48
DZIĘKUJĘ ZA UWAGĘ
Siedziba firmy:Sienkiewicza 11015-005 Białystok