View
41
Download
0
Category
Preview:
Citation preview
#SwiftMars
Needs to Address
Fast growth
Robustness
• testable
• decoupled
• debuggable (blame)
Team collaboration
Reusable
Defer decisions
Replaceable frameworks
App
Architecture?View Controller
View Controller
View Controller
View Controller
Sys
tem
Fram
ewor
ks
ArchitectureView Controller
View Controller
View Controller
View Controller
Sys
tem
Fram
ewor
ks
System
Frameworks
View Controller
View ControllerView Controller ModelPresenterViewModel
Persistance FW
View
Presenter
Entity Gateway
Clean Architecture
Interactor
Entity
Net
wor
k
Loca
tion
FW
#SwiftMars
Single Responsibility
MVC is not enough
More classes, but more cohesive: only one reason to change
#SwiftMars
Business Logicextension ShowAllSpeakersInteractor: InteractorCommandProtocol { func execute() { let entities = entityGateway.fetchAllSpeakers() let displayData = entities.map({entity in return SpeakerDisplayData(speaker: entity)}) presenter?.presentAllSpeakers(displayData) } }
#SwiftMars
Passive Viewclass SpeakersTableViewController: UITableViewController, SegueHandlerTypeProtocol {
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return numberOfRows }
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier(SpeakersTableViewController.speakerCellIdentifier, forIndexPath: indexPath) as! SpeakerTableViewCell eventHandler?.presentCell(cell, indexPath: indexPath) return cell } }
#SwiftMars
Presentationclass SpeakersListsPresenter { weak var view: SpeakersListViewProtocol? let interactor: ShowAllSpeakersInteractor private var speakers: [SpeakerDisplayData]=[] }
extension SpeakersListsPresenter: SpeakersListPresenterProtocol { func presentAllSpeakers(speakers: [SpeakerDisplayData]) { view?.configureListWithNumberOfRows(speakers.count) self.speakers = speakers let addedIndexPaths = speakers.enumerate() .map({(index, _) in return NSIndexPath(forRow: index, inSection: 0)}) view?.addRowsAtIndexPaths(addedIndexPaths) } }
#SwiftMars
Deletion Use Caseclass DeleteSpeakerInteractor { let entityGateway: EntityGatewayDeleteSpeakerProtocol var id: String?
init(entityGateway: EntityGatewayDeleteSpeakerProtocol) { self.entityGateway = entityGateway } }
extension DeleteSpeakerInteractor : InteractorCommandProtocol { func execute() { guard let id = self.id else { return } entityGateway.deleteSpeaker(id) } }
#SwiftMars
Interface Segregation
Don’t force a class to depend on methods that it won’t use
One interactor will not likely use every functionality of the entity gateway
#SwiftMars
class InMemorySpeakersRepo { }
extension InMemorySpeakersRepo: EntityGatewayFetchSpeakersProtocol { func fetchAllSpeakers() -> [Speaker] { … } }
extension InMemorySpeakersRepo: EntityGatewayCreateSpeakerProtocol { func createSpeaker(name name: String, title: String, synopsis: String, dateSubmitted: NSDate) { … } }
extension InMemorySpeakersRepo: EntityGatewayDeleteSpeakerProtocol { func deleteSpeaker(id: String) { … } }
Different Functions of the Entity Gateway
Persistance FW
View
Presenter
Entity Gateway
Clean Architecture
Interactor
Entity
Net
wor
k
Loca
tion
FW
Take me Outta Here
func presentCell(cell: SpeakerCellProtocol, indexPath: NSIndexPath) { let index = indexPath.row guard index < speakers.count else { return } let speaker = speakers[index] cell.displayName(speaker.name) cell.displayTitle(speaker.title) }
Know That!
#SwiftMars
Ready for Lifeclass Houston { func launchSpaceship() throws { guard fuel >= 1.0 else { throw LaunchError.NoFuel } guard astronaut != "" else { throw LaunchError.NoAstronaut } guard spaceshipOK else { throw LaunchError.BrokenShip("Engine") } print("Launching spaceship") } } var control = Houston(fuel: 1.0, astronaut: nil, spaceshipOK: true) do { try control.launchSpaceship() } catch Houston.LaunchError.NoFuel { print("Adding fuel") } catch Houston.LaunchError.NoAstronaut { print("Next in line") } catch Houston.LaunchError.BrokenShip(let problem) { print(problem) } catch let unknowError { // }
Know That!
#SwiftMars
Null Objectclass Sample { let identifier: String let biohazard: Bool func check() { let result = (biohazard ? "Care with sample \(identifier)" : "OK") print(result) } }
func fetchSample() -> Sample? { return nil }
if let sample = fetchSample() { sample.check() } else { print("OK") }
#SwiftMars
Null Objectclass Sample { let identifier: String let biohazard: Bool /* … */ }
class NullSample: Sample { init() { super.init(identifier: "", biohazard: false) } override func check() { print("OK (empty)") } }
func fetchSample() -> Sample { return NullSample() }
let sample = fetchSample() sample.check()
Poor Templateclass PoorAstronaut { func getIntoSpaceShip() { print("Commencing countdown engines on") }
func travelTo(destination: String) { fatalError() }
func comeBack() { print("Planet earth is blue") }
func performMissionTo(destination: String) { getIntoSpaceShip() travelTo(destination) comeBack() } }
let astronaut = PoorAstronaut() astronaut.performMissionTo("Moon")
Swifty Templateprotocol Mission { func getIntoSpaceShip() func travelTo(destination: String) func comeBack() func performMissionTo(destination: String) }
extension Mission { func getIntoSpaceShip() { print("Commencing countdown engines on") } func comeBack() { print("Planet earth is blue") } func performMissionTo(destination: String) { getIntoSpaceShip() travelTo(destination) comeBack() } }
class Astronaut: Mission { func travelTo(destination: String) { print("I always wanted to visit \(destination)") } }
let markWatney = Astronaut() markWatney.performMissionTo("Mars")
#SwiftMars
Recommendations
Pay attention to your architecture; It always pays off
Use Principles to take decisions
Take advantage of the language and adapt the patterns to it
In case of doubt, ask for an architecture loan and ship it
Recommended