
This video is only available to subscribers. Start a subscription today to get access to this and 469 other videos.
Moving Review Logic
This episode is part of a series: Refactoring to Coordinators.
1. Why Coordinators? 15 min |
2. Handling Actions from Users 11 min |
3. Moving Review Logic 17 min |
4. Handling the Photo Transition 8 min |
Episode Links
A little Cleanup
The AppCoordinator
is starting to take shape, so it's time to move it into its own file. We start by creating AppCoordinator.swift
and moving the code into it:
// AppCoordinator.swift
import UIKit
class AppCoordinator : RestaurantsViewControllerDelegate {
let navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start() {
let restaurantsVC = navigationController.topViewController as! RestaurantsViewController
restaurantsVC.delegate = self
}
func didSelect(restaurant: Restaurant) {
let restaurantDetail = RestaurantViewController.makeFromStoryboard()
restaurantDetail.restaurantDelegate = self
restaurantDetail.restaurant = restaurant
navigationController.pushViewController(restaurantDetail, animated: true)
}
func addReviewTapped(_ vc: RestaurantViewController) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let nav = storyboard.instantiateViewController(withIdentifier: "AddReviewNavigationController") as! UINavigationController
let reviewVC = nav.topViewController as! ReviewViewController
reviewVC.addReviewBlock = { [weak self] rvc in
let review = Review(author: rvc.nameTextField.text ?? "",
comment: rvc.commentTextView.text,
rating: Float(rvc.ratingView.value),
restaurantID: vc.restaurantID)
Restaurants.add(review: review)
self?.navigationController.dismiss(animated: true) {
vc.insertReview(review)
}
}
navigationController.present(nav, animated: true)
}
}
This can live right next to the AppDelegate.swift
in the project organizer.
Adding our Next Transition
The next transition to handle is the Add Review logic. Currently this is done as a modal transition by the RestaurantViewController
.
We'll start by removing the segue in the Storyboard. Then we can add the appropriate delegate protocol so the coordinator can handle the actions triggered by this view controller:
protocol RestaurantViewControllerDelegate : class {
func addReviewTapped(_ vc: RestaurantViewController)
}
class RestaurantViewController : UITableViewController {
weak var restaurantDelegate: RestaurantViewControllerDelegate?
// ...
}
The naming of the property here is slightly awkward because UITableViewController
already has a delegate
property. Other names we might choose: userActionDelegate
, or perhaps coordinationDelegate
.
Defining the Action Explicitly
Using segues we can wire up a transition from a button with no code. Since we don't have segues anymore we need to add an @IBAction
for the button tap.
We'll just send this off to our restaurantDelegate
to handle it.
@IBAction func addReview(_ sender: Any) {
restaurantDelegate?.addReviewTapped(self)
}
Comforming to the Protocol
Our AppCoordinator
will handle these actions, so it will need to conform to this protocol. We can move the code that was previously in our prepare(for segue: sender:)
code.
class AppCoordinator : RestaurantsViewControllerDelegate, RestaurantViewControllerDelegate {
// ...
func addReviewTapped(_ vc: RestaurantViewController) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let nav = storyboard.instantiateViewController(withIdentifier: "AddReviewNavigationController") as! UINavigationController
let reviewVC = nav.topViewController as! ReviewViewController
reviewVC.addReviewBlock = { [weak self] rvc in
let review = Review(author: rvc.nameTextField.text ?? "",
comment: rvc.commentTextView.text,
rating: Float(rvc.ratingView.value),
restaurantID: vc.restaurantID)
Restaurants.add(review: review)
self?.navigationController.dismiss(animated: true) {
vc.insertReview(review)
}
}
navigationController.present(nav, animated: true)
}
}
We also need to wire up this delegate:
func didSelect(restaurant: Restaurant) {
let restaurantDetail = RestaurantViewController.makeFromStoryboard()
restaurantDetail.restaurantDelegate = self
restaurantDetail.restaurant = restaurant
navigationController.pushViewController(restaurantDetail, animated: true)
}
With this in place we can remove our segue handling code, as this is already implemented in our coordinator.
Reviewing What We've Done
If you look at the RestaurantViewController
now, it is still aware of reviews and the review model, but has no reference whatsoever to the AddReviewViewController
. Reducing dependencies in this way can clean up code and make your code more flexible and amenable to change.