Fly Me to the
View
Jorge D. Ortiz-Fuentes
@jdortiz
A Canonical
Examples
Production
#AdvArchMobile
Agenda
★ Architecture & Responsibilities
★ A modular solution
• Storyboards
• XIBs only
★ Benefits
★ Recap
Persistance FW
View
Network
LocationFW
Presenter
Entity Gateway
Clean Architecture
Interactor
Entity
Where’s the beef?
navigation
#AdvArchMobile
Navigation
★ Events vs Semantics
★ Storyboard vs Decoupled VC
★ And without storyboards?
★ Dependency Injection
★ Memory Management
Responsibili
ties
MVP
#AdvArchMobile
View
★ Known by its beautiful face
and its passivity
★ Receives the events and
passes them to the presenter
★ Knows how to navigate
#AdvArchMobile
Presenter
★ Provides meaning to the user
events
★ Tells the view what to update
★ Knows when and where to
navigate
#AdvArchMobile
Connector
★ Knows the dependencies and
how to connect them
★ Takes you to the next
connector
What about MVVM?
#AdvArchMobile
No worries
★ MVP Tells
★ MVVM Notifies
★ Feel free to embrace & extend
A modular
solution
Decoupling
#AdvArchMobile
View controller
★ Separate event from semantics
★ Still, navigation is done here
A new VC
View (VC) Presenter Interactor
Entity
Gateway
Connector
View (VC) Presenter Interactor
Entity
Gateway
Connector
1 23
4
Segue from VC
#AdvArchMobile
View Controller: Events
@IBAction func add(_ sender: UIBarButtonItem) {
presenter.addButtonTapped()
}
override func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
presenter.select(row: indexPath.row)
}
#AdvArchMobile
Presenter: Navigation
Semantics
func addButtonTapped() {
view.navigateToAddProgrammer()
}
func select(row: Int) {
if row < 0 !|| row !>= programmers.count {
selectedId = nil
} else {
let programmer = programmers[row]
selectedId = programmer.id
view.navigateToProgrammerDetail()
}
}
#AdvArchMobile
View Controller:
Navigation
func navigateToAddProgrammer() {
performSegue(withIdentifier:
SegueIdentifier.addProgrammer, sender: self)
}
func navigateToProgrammerDetail() {
performSegue(withIdentifier:
SegueIdentifier.showProgrammer, sender: self)
}
#AdvArchMobile
View Controller:
Delegate DI
override func prepare(for segue: UIStoryboardSegue, sender:
Any?) {
let identifier = try! segueIdentifier(for: segue)
switch identifier {
case .addProgrammer:
connector.prepareAddProgrammer(viewController:
segue.destination)
case .showProgrammer:
connector.prepareProgrammerDetail(viewController:
segue.destination, presenter: presenter)
}
}
#AdvArchMobile
Child Connector:
Assemble
func assembleModule(view:
ProgrammerEditViewController) {
let presenter =
ProgrammerEditPresenter(otherDep: depencency)
view.presenter = presenter
view.connector = self
presenter.view = view
}
#MobAppArch
Implementing navigation
and back
★ Navigation destination is NOT decided by
the view event
★ When presented modally, navigation back
requires Unwind segue
★ In a navigation controller things happen
naturally
Memory
Management
Dismiss a VC
View (VC) Presenter Interactor
Entity
Gateway
Connector
View (VC) Presenter Interactor
Entity
Gateway
Connector
1 2
Passing parameters
#AdvArchMobile
Having the Information
★ Perform segue & prepare for segue are
disconnected
★ Information for injection:
• Use sender: DON’T
• State in View Controller
• State in Presenter (preferred)
XIBs only
#AdvArchMobile
Differences
★ Easier to pass the information
★ Easier to do the injection: Initializer instead
of property
★ Presentation is done by the source
★ No “big picture” or unwind segues
#AdvArchMobile
VC Injection
via Initializer
var presenter: ProgrammerEditPresenter
init(presenter: ProgrammerEditPresenter,
nibName: String? = nil, bundle: Bundle? =
nil) {
self.presenter = presenter
super.init(nibName: nil, bundle: nil)
presenter.view = self
}
#AdvArchMobile
Passing Information &
Presentation
func navigateToDetail(parameter: String) {
let detailViewController =
connector.prepareProgrammerDetail(paramete
r: parameter)
navigationController!?.pushViewController(d
etailViewController, animated: true)
}
Benefits
Modify Dependency
Injection
#AdvArchMobile
Dependencies Change
★ Create them in the connector
★ Pass them from one connector to the next
Testable
#AdvArchMobile
Test Everything
func testSegueToAddProgrammerInvokesConnectorToNavigate() {
let connectorMock = ProgrammersListConnectorMock(some
Dep: DepTestDummy())
sut.connector = connectorMock
sut.performSegue(withIdentifier:
ProgrammersTableViewControllerMock.SegueIdentifier.addProgr
ammer, sender: sut)
XCTAssertEqual(1,
connectorMock.prepareAddProgrammerInvoked)
}
A/B Testing
#AdvArchMobile
Presenter
func addButtonTapped() {
if abSelector.isA {
view.navigateToAddProgrammerA()
} else {
view.navigateToAddProgrammerB()
}
}
#AdvArchMobile
Recap
★ Navigation is a key part of an advanced
architecture:
• Modular
• Allows passive view
• Not bound to implementation (storyboards)
★ Easy to implement & maintain
★ Clear benefits
– Anonymous iOS developer
“Fly me to the View
Let me play among the Apps”
Thank
You!
谢谢
Terima
kasih!
ந"#
@jdortiz
#AdvArchMobile

201710 Fly Me to the View - iOS Conf SG