For my new project I'm using a lot of Data Input and even when Eureka is awesome by itself it's lacking of search capability in the view controller used by PushRow, the Readme doesn't provide too much info but after digging up a bit I came with my custom Row that is able to work with a generic type capable of matching with search.
//
// SearchablePushRow.swift
//
import Eureka
public class _SearchablePushRow<T: Equatable, Cell: CellType where Cell: BaseCell, Cell: TypedCellType, Cell.Value == T, T: SearchableItem, T: CustomStringConvertible> : SelectorRow<T, Cell, SearchableViewController<T>> {
public required init(tag: String?) {
super.init(tag: tag)
presentationMode = .Show(controllerProvider: ControllerProvider.Callback { return SearchableViewController<T>(){ _ in } }, completionCallback: { vc in vc.navigationController?.popViewControllerAnimated(true) })
}
}
/// Selector Controller (used to select one option among a list)
public class SearchableViewController<T:Equatable where T:SearchableItem, T: CustomStringConvertible> : _SearchableViewController<T, ListCheckRow<T>> {
required public init() {
super.init()
}
convenience public init(_ callback: (UIViewController) -> ()){
self.init()
completionCallback = callback
}
}
public class _SearchableViewController<T: Equatable, Row: SelectableRowType where Row: BaseRow, Row: TypedRowType, Row.Value == T, Row.Cell.Value == T, T: SearchableItem, T: CustomStringConvertible> : UITableViewController, UISearchResultsUpdating, TypedRowControllerType {
public var row: RowOf<Row.Value>!
let searchController = UISearchController(searchResultsController: nil)
required public init() {
super.init(style: .Grouped)
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
self.definesPresentationContext = true
}
var originalOptions = [T]()
var currentOptions = [T]()
public override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
tableView!.tableHeaderView = searchController.searchBar
if let options = row.dataProvider?.arrayData {
self.originalOptions = options
self.currentOptions = options
}
}
private func filter(query: String) {
if query == "" {
currentOptions = self.originalOptions
} else {
currentOptions = self.originalOptions.filter{ $0.matchesSearchQuery(query) }
}
self.tableView.reloadData()
}
public override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
public override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return self.row?.title
}
public override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return currentOptions.count
}
public override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let option = self.currentOptions[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = option.description
return cell
}
public override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let option = self.currentOptions[indexPath.row]
row.value = option
completionCallback?(self)
}
public var completionCallback : ((UIViewController) -> ())?
public func updateSearchResultsForSearchController(searchController: UISearchController) {
filter(searchController.searchBar.text!)
}
}
/// A selector row where the user can pick an option from a pushed view controller
public final class SearchablePushRow<T: Equatable where T: SearchableItem, T: CustomStringConvertible> : _SearchablePushRow<T, PushSelectorCell<T>>, RowType {
public required init(tag: String?) {
super.init(tag: tag)
}
}
public protocol SearchableItem {
func matchesSearchQuery(query: String) -> Bool
}
The real magic happens with the SearchableItem
that you implement in the swift types you intent to make searchable in the PushRow.
struct LocalityNeighborhood : CustomStringConvertible, Equatable, SearchableItem {
let id: Int
let name: String
var description: String {
return name
}
func matchesSearchQuery(query: String) -> Bool {
return name.containsString(query)
}
}
func ==(rhs: LocalityNeighborhood, lhs: LocalityNeighborhood) -> Bool {
return rhs.id == lhs.id
}
Then you just use it in your Form:
form +++ SearchablePushRow<LocalityNeighborhood>("neighborhood")
Luckily you'd end up with a push row that present search results similar to this(without the spanish of-course xD):