                        payload: GetRoutesErrorPayload(type: title, description: description, url: requestUrl))
         ])
     }
-    
+
     func validCoordinates(startCoord: CLLocationCoordinate2D, endCoord: CLLocationCoordinate2D) -> Bool {
         let latitudeValues = [startCoord.latitude, endCoord.latitude]
         let longitudeValues  = [startCoord.longitude, endCoord.longitude]
-        
+
         let validLatitudes = latitudeValues.reduce(true) { (result, latitude) -> Bool in
             return result && latitude <= Constants.Values.RouteBorders.northBorder &&
                 latitude >= Constants.Values.RouteBorders.southBorder
         }
-        
+
         let validLongitudes = longitudeValues.reduce(true) { (result, longitude) -> Bool in
             return result && longitude <= Constants.Values.RouteBorders.eastBorder &&
                 longitude >= Constants.Values.RouteBorders.westBorder
         }
-        
+
         return validLatitudes && validLongitudes
     }
-    
+
     func requestDidFinish(perform actions: [RequestAction]) {
-        
+
         for action in actions {
-            
+
             switch action {
-                
+
             case .showAlert(title: let title, message: let message, actionTitle: let actionTitle):
                 let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
                 let action = UIAlertAction(title: actionTitle, style: .cancel, handler: nil)
                 alertController.addAction(action)
                 present(alertController, animated: true, completion: nil)
-                
+
             case .showError(bannerInfo: let bannerInfo, payload: let payload):
                 banner = StatusBarNotificationBanner(title: bannerInfo.title, style: bannerInfo.style)
                 banner?.autoDismiss = false
                 banner?.show(queuePosition: .front, on: navigationController)
                 Analytics.shared.log(payload)
-                
+
             case .hideBanner:
                 banner?.dismiss()
                 mediumTapticGenerator.impactOccurred()
 
             }
-            
-        }
-        
+
+        }
+
         showRouteSearchingLoader = false
         routeResults.reloadData()
     }
 
-    // MARK: Location Manager Delegate
-
+    // MARK: Date Picker
+
+    private func setupDatePicker() {
+        setupDatePickerView()
+        setupDatePickerOverlay()
+    }
+
+    private func setupDatePickerView() {
+        datePickerView = DatePickerView(frame: CGRect(x: 0, y: view.frame.height, width: view.frame.width, height: 254))
+
+        datePickerView.positionSubviews()
+        datePickerView.addSubviews()
+
+        datePickerView.cancelButton.addTarget(self, action: #selector(self.dismissDatePicker), for: .touchUpInside)
+        datePickerView.doneButton.addTarget(self, action: #selector(self.saveDatePickerDate), for: .touchUpInside)
+    }
+
+    private func setupDatePickerOverlay() {
+        datePickerOverlay = UIView(frame: CGRect(x: 0, y: -12, width: view.frame.width, height: view.frame.height + 12)) // 12 for sliver that shows up when click datepicker immediately after transition from HomeVC
+        datePickerOverlay.backgroundColor = .black
+        datePickerOverlay.alpha = 0
+
+        datePickerOverlay.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dismissDatePicker)))
+    }
+
+    private func addHeightToDatepicker(_ height: CGFloat) {
+        let oldFrame = datePickerView.frame
+        let newFrame = CGRect(x: oldFrame.minX, y: oldFrame.minY, width: oldFrame.width, height: oldFrame.height + height)
+
+        datePickerView.frame = newFrame
+    }
+
+    @objc func showDatePicker(sender: UIButton) {
+
+        view.bringSubviewToFront(datePickerOverlay)
+        view.bringSubviewToFront(datePickerView)
+
+        // set up date on datepicker view
+        if let time = searchTime {
+            datePickerView.setDatepickerDate(date: time)
+        }
+
+        datePickerView.setDatepickerTimeType(searchTimeType: searchTimeType)
+
+        UIView.animate(withDuration: 0.5) {
+            self.datePickerView.center.y = self.view.frame.height - (self.datePickerView.frame.height/2)
+            self.datePickerOverlay.alpha = 0.6 // darken screen when pull up datepicker
+        }
+
+        let payload = RouteOptionsSettingsPayload(description: "Date Picker Accessed")
+        Analytics.shared.log(payload)
+
+    }
+
+    @objc func dismissDatePicker(sender: UIButton) {
+        UIView.animate(withDuration: 0.5, animations: {
+            self.datePickerView.center.y = self.view.frame.height + (self.datePickerView.frame.height/2)
+            self.datePickerOverlay.alpha = 0.0
+        }) { (_) in
+            self.view.sendSubviewToBack(self.datePickerOverlay)
+            self.view.sendSubviewToBack(self.datePickerView)
+        }
+    }
+
+    @objc func saveDatePickerDate(sender: UIButton) {
+
+        let date = datePickerView.getDate()
+        searchTime = date
+
+        let typeToSegmentControlElements = datePickerView.typeToSegmentControlElements
+        let timeTypeSegmentControl = datePickerView.timeTypeSegmentedControl
+        let leaveNowSegmentControl = datePickerView.leaveNowSegmentedControl
+
+        var buttonTapped = ""
+
+        // Get selected time type
+        if leaveNowSegmentControl.selectedSegmentIndex == typeToSegmentControlElements[.leaveNow]!.index {
+            searchTimeType = .leaveNow
+            buttonTapped = "Leave Now Tapped"
+        } else if timeTypeSegmentControl.selectedSegmentIndex == typeToSegmentControlElements[.arriveBy]!.index {
+            searchTimeType = .arriveBy
+            buttonTapped = "Arrive By Tapped"
+        } else {
+            searchTimeType = .leaveAt
+            buttonTapped = "Leave At Tapped"
+        }
+
+        routeSelection.setDatepicker(withDate: date, withSearchTimeType: searchTimeType)
+
+        dismissDatePicker(sender: sender)
+
+        searchForRoutes()
+
+        let payload = RouteOptionsSettingsPayload(description: buttonTapped)
+        Analytics.shared.log(payload)
+
+    }
+
+    // MARK: Reachability
+
+    private func setupReachability() {
+        NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged(notification:)),
+                                               name: .reachabilityChanged, object: reachability)
+
+        do {
+            try reachability?.startNotifier()
+        } catch {
+            print("\(fileName) \(#function): Could not start reachability notifier")
+        }
+    }
+
+    @objc func reachabilityChanged(notification: Notification) {
+        if let reachability = notification.object as? Reachability {
+
+            // Dismiss current banner, if any
+            banner?.dismiss()
+            banner = nil
+
+            switch reachability.connection {
+            case .none:
+                banner = StatusBarNotificationBanner(title: Constants.Banner.noInternetConnection, style: .danger)
+                banner?.autoDismiss = false
+                banner?.show(queuePosition: .front, bannerPosition: .top, on: navigationController)
+                setUserInteraction(to: false)
+            case .cellular, .wifi:
+                setUserInteraction(to: true)
+            }
+        }
+    }
+
+    private func setUserInteraction(to userInteraction: Bool) {
+        cellUserInteraction = userInteraction
+
+        for cell in routeResults.visibleCells {
+            setCellUserInteraction(cell, to: userInteraction)
+        }
+
+        routeSelection.isUserInteractionEnabled = userInteraction
+    }
+
+    private func setCellUserInteraction(_ cell: UITableViewCell?, to userInteraction: Bool) {
+        cell?.isUserInteractionEnabled = userInteraction
+        cell?.selectionStyle = .none // userInteraction ? .default : .none
+    }
+
+    // MARK: Refresh Control
+
+    private func setupRefreshControl() {
+        refreshControl = UIRefreshControl()
+        refreshControl.isHidden = true
+
+        if #available(iOS 10.0, *) {
+            routeResults.refreshControl = refreshControl
+        } else {
+            routeResults.addSubview(refreshControl)
+        }
+    }
+
+    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
+        if refreshControl.isRefreshing {
+            // Update leave now time in pull to refresh
+            if searchTimeType == .leaveNow {
+                let now = Date()
+                searchTime = now
+                routeSelection.setDatepicker(withDate: now, withSearchTimeType: searchTimeType)
+            }
+
+            searchForRoutes()
+        }
+    }
+
+    func hideRefreshControl() {
+        if #available(iOS 10.0, *) {
+            routeResults.refreshControl?.endRefreshing()
+        } else {
+            refreshControl.endRefreshing()
+        }
+    }
+
+    // MARK: RouteDetailViewController
+
+    func createRouteDetailViewController(from indexPath: IndexPath) -> RouteDetailViewController? {
+
+        let route = routes[indexPath.row]
+        var routeDetailCurrentLocation = currentLocation
+        if searchTo?.name != Constants.Stops.currentLocation && searchFrom?.name != Constants.Stops.currentLocation {
+            routeDetailCurrentLocation = nil // If route doesn't involve current location, don't pass along for view.
+        }
+
+        let routeOptionsCell = routeResults.cellForRow(at: indexPath) as? RouteTableViewCell
+
+        let contentViewController = RouteDetailContentViewController(route: route,
+                                                                     currentLocation: routeDetailCurrentLocation,
+                                                                     routeOptionsCell: routeOptionsCell)
+
+        guard let drawerViewController = contentViewController.drawerDisplayController else {
+            return nil
+        }
+        return RouteDetailViewController(contentViewController: contentViewController,
+                                         drawerViewController: drawerViewController)
+    }
+
+    // MARK: Previewing Delegate
+
+    @objc func handleLongPressGesture(_ sender: UILongPressGestureRecognizer) {
+        if sender.state == .began {
+            let point = sender.location(in: routeResults)
+            if let indexPath = routeResults.indexPathForRow(at: point), let cell = routeResults.cellForRow(at: indexPath) {
+                presentShareSheet(from: view, for: routes[indexPath.row], with: cell.getImage())
+            }
+        }
+    }
+
+    func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
+        let point = view.convert(location, to: routeResults)
+
+        guard
+            let indexPath = routeResults.indexPathForRow(at: point),
+            let cell = routeResults.cellForRow(at: indexPath) as? RouteTableViewCell,
+            let routeDetailViewController = createRouteDetailViewController(from: indexPath)
+        else {
+            return nil
+        }
+
+        routeDetailViewController.preferredContentSize = .zero
+        routeDetailViewController.isPeeking = true
+        cell.transform = .identity
+        previewingContext.sourceRect = routeResults.convert(cell.frame, to: view)
+
+        let payload = RouteResultsCellPeekedPayload()
+        Analytics.shared.log(payload)
+
+        return routeDetailViewController
+    }
+
+    func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
+        (viewControllerToCommit as? RouteDetailViewController)?.isPeeking = false
+        navigationController?.pushViewController(viewControllerToCommit, animated: true)
+    }
+
+}
+
+// MARK: Location Manager Delegate
+extension RouteOptionsViewController: CLLocationManagerDelegate {
     private func setupLocationManager() {
         locationManager = CLLocationManager()
         locationManager.delegate = self

        guard let location = manager.location else {
            return
        }
        
        currentLocation = location.coordinate
        
        updateSearchBarCurrentLocation(withCoordinate: location.coordinate)
        
        if let busStop = searchTo as? BusStop {
            if busStop.name == Constants.Stops.currentLocation {
               updateCurrentLocation(busStop, withCoordinate: location.coordinate)
            }
        }
        
        if let busStop = searchFrom as? BusStop {
            if busStop.name == Constants.Stops.currentLocation {
                updateCurrentLocation(busStop, withCoordinate: location.coordinate)
            }
        }
        
        // If haven't selected start location, set to current location
        if searchFrom == nil {
            let currentLocationStop = BusStop(name: Constants.Stops.currentLocation,

        }

        cell?.setData(route: routes[indexPath.row], rowNum: indexPath.row)

        // Activate timers
        let timerDoesNotExist = (timers[indexPath.row] == nil)
        if timerDoesNotExist {
            timers[indexPath.row] = Timer.scheduledTimer(timeInterval: 5.0, target: cell!, selector: #selector(RouteTableViewCell.updateLiveElementsWithDelay), userInfo: nil, repeats: true)
        }

        cell?.addRouteDiagramSubviews()
        cell?.activateRouteDiagramConstraints()

        // Add share action for long press gestures on non 3D Touch devices
        let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressGesture(_:)))
        cell?.addGestureRecognizer(longPressGestureRecognizer)

        setCellUserInteraction(cell, to: cellUserInteraction)

        return cell!
    }

}

// MARK: TableView Delegate
extension RouteOptionsViewController: UITableViewDelegate {
    private func setupRouteResultsTableView() {
        routeResults = UITableView(frame: CGRect(x: 0, y: routeSelection.frame.maxY, width: view.frame.width, height: view.frame.height - routeSelection.frame.height - (navigationController?.navigationBar.frame.height ?? 0)), style: .plain)
        routeResults.delegate = self
