Jorge D. Ortiz-Fuentes
@jdortiz
Escape from Mars
Thank your Architecture
#SwiftMars
A Canonical
Examples
production
#SwiftMars
#SwiftMars
Agenda
Learning from NASA
SOLID Architecture
Design patterns
Learning
from NASA
Agility and the MVP
Always grow
NO SPoF
Know where
problems come from
Last long
Teams can work
together
Shit happens
Do you
have any
problems?
#SwiftMars
Needs to Address
Fast growth
Robustness
• testable
• decoupled
• debuggable
(blame)
Team collaboration
Reusable
Defer decisions
Replaceable
frameworks
There is a way:
SOLID Principles
AKA
Clean
Architecture
Impossible to grow
anything in Mars?
SOLID
Architecture
App
Architecture?
View Controller
View Controller
View Controller
View Controller
System
Frameworks
Architecture
View Controller
View Controller
View Controller
View Controller
System
Frameworks
System
Frameworks
View Controller
View ControllerView Controller ModelPresenterViewModel
Persistance FW
View
Presenter
Entity Gateway
Clean Architecture
Interactor
Entity
Network
LocationFW
Clean Architecture
AppDelegate
View (VC) Presenter Interactor Entity Gateway
Connector
#SwiftMars
Single Responsibility
MVC is not enough
More classes, but more cohesive: only one
reason to change
#SwiftMars
Business Logic
extension ShowAllSpeakersInteractor:
InteractorCommandProtocol {
func execute() {
let entities =
entityGateway.fetchAllSpeakers()
let displayData = entities.map({entity in
return SpeakerDisplayData(speaker:
entity)})
presenter?.presentAllSpeakers(displayData)
}
}
#SwiftMars
Open Close
Open to Extension, Closed to Modification
#SwiftMars
Passive View
class 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.sp
eakerCellIdentifier, forIndexPath: indexPath) as! SpeakerTableViewCell
eventHandler?.presentCell(cell, indexPath: indexPath)
return cell
}
}
#SwiftMars
Liskov Substitution
It should be possible to use derived classes
without special care
#SwiftMars
Presentation
class 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
Dependency Inversion
The business case SHOULDN’T depend on
the frameworks
#SwiftMars
Deletion Use Case
class 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
Network
LocationFW
Design
Patterns
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 Life
class 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 Object
class 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 Object
class 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 Template
class 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 Template
protocol 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")
Next Steps
#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
canonicalexamples.com
coupon:
CODEMOTIONVIII
Thank
you!
@jdortiz
#SwiftMars
Escape from Mars

Escape from Mars