Stuff…
This commit is contained in:
182
TagTunes/SearchController.swift
Executable file
182
TagTunes/SearchController.swift
Executable file
@@ -0,0 +1,182 @@
|
||||
//
|
||||
// SearchController.swift
|
||||
// TagTunes
|
||||
//
|
||||
// Created by Kim Wittenburg on 06.03.16.
|
||||
// Copyright © 2016 Kim Wittenburg. All rights reserved.
|
||||
//
|
||||
|
||||
import SearchAPI
|
||||
import AppKitPlus
|
||||
|
||||
/// A search delegate is notified when a `SearchController` selects an item.
|
||||
protocol SearchDelegate: class {
|
||||
|
||||
/// The view controller displaying the content.
|
||||
var contentViewController: ContentViewController! { get }
|
||||
|
||||
/// Invoked when the `searchController` selected a item. In the
|
||||
/// implementation the selected `searchResult` should be added to the
|
||||
/// `contentViewController`.
|
||||
func searchController(searchController: SearchController, didSelectSearchResult searchResult: TagTunesItem)
|
||||
|
||||
}
|
||||
|
||||
/// Manages the user interface for search results.
|
||||
public class SearchController: NSObject, PopUpSearchFieldDelegate {
|
||||
|
||||
/// This struct contains *magic numbers* like references to storyboard
|
||||
/// identifiers.
|
||||
private struct Constants {
|
||||
|
||||
/// The identifier used for the rows in the search results pop up.
|
||||
static let AlbumTableCellViewIdentifier = "AlbumTableCellViewIdentifier"
|
||||
|
||||
/// The identifier used for the `No Results` row in the search results
|
||||
/// pop up.
|
||||
static let NoResultsTableCellViewIdentifier = "NoResultsTableCellViewIdentifier"
|
||||
|
||||
}
|
||||
|
||||
weak var delegate: SearchDelegate?
|
||||
|
||||
/// The search field. This should be set by the view controller containing
|
||||
/// the user interface for searching.
|
||||
public weak var searchField: PopUpSearchField! {
|
||||
willSet {
|
||||
searchField?.popUpDelegate = nil
|
||||
}
|
||||
didSet {
|
||||
searchField?.popUpDelegate = self
|
||||
}
|
||||
}
|
||||
|
||||
/// Used for searching and loading search results
|
||||
private let urlSession = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: nil, delegateQueue: NSOperationQueue.mainQueue())
|
||||
|
||||
/// The URL task currently loading the search results.
|
||||
private var searchTask: NSURLSessionTask? {
|
||||
didSet {
|
||||
searching = searchTask != nil
|
||||
}
|
||||
}
|
||||
|
||||
public private(set) dynamic var searching: Bool = false
|
||||
|
||||
/// The search results for the current search term.
|
||||
private var searchResults = [Album]() {
|
||||
didSet {
|
||||
searchField.updatePopUp()
|
||||
}
|
||||
}
|
||||
|
||||
/// The error that occured during searching, if any.
|
||||
private var searchError: NSError?
|
||||
|
||||
/// Begins searching for the `searchField`'s `stringValue`.
|
||||
public func beginSearch() {
|
||||
let searchString = searchField.stringValue
|
||||
cancelSearch()
|
||||
if searchString == "" {
|
||||
searchResults = []
|
||||
} else {
|
||||
var request = SearchAPIRequest(searchRequestWithTerm: searchString)
|
||||
request.mediaType = .Music
|
||||
request.entity = .Album
|
||||
request.maximumNumberOfResults = UInt(Preferences.sharedPreferences.numberOfSearchResults)
|
||||
if Preferences.sharedPreferences.useEnglishTags {
|
||||
request.language = .English
|
||||
}
|
||||
request.country = Preferences.sharedPreferences.iTunesStore
|
||||
searchTask = urlSession.dataTaskWithURL(request.URL, completionHandler: processSearchResults)
|
||||
searchTask!.resume()
|
||||
}
|
||||
}
|
||||
|
||||
/// Cancels the current search (if there is one) and removes the search
|
||||
/// results.
|
||||
private func cancelSearch() {
|
||||
searchTask?.cancel()
|
||||
}
|
||||
|
||||
/// Processes the data returned from a network request into the
|
||||
/// `searchResults` array.
|
||||
private func processSearchResults(data: NSData?, response: NSURLResponse?, error: NSError?) {
|
||||
searchTask = nil
|
||||
var newResults = [Album]()
|
||||
if let theData = data where error == nil {
|
||||
do {
|
||||
let result = try SearchAPIResult(data: theData)
|
||||
newResults = result.resultEntities.flatMap{ $0 as? Album }
|
||||
} catch let error as NSError {
|
||||
searchError = error
|
||||
}
|
||||
} else if let theError = error {
|
||||
searchError = theError
|
||||
}
|
||||
searchResults = newResults
|
||||
}
|
||||
|
||||
public func popUpSearchFieldWillHidePopUp(searchField: PopUpSearchField) {
|
||||
cancelSearch()
|
||||
}
|
||||
|
||||
public func popUpSearchFieldShouldShowPopUp(searchField: PopUpSearchField) -> Bool {
|
||||
return !searchField.stringValue.isEmpty
|
||||
}
|
||||
|
||||
public func popUpSearchField(searchField: PopUpSearchField, didSelectPopUpEntryAtRow row: Int) {
|
||||
let searchResult = searchResults[row]
|
||||
let albumItem = AlbumItem(entity: searchResult)
|
||||
albumItem.beginLoadingChildren()
|
||||
delegate?.searchController(self, didSelectSearchResult: albumItem)
|
||||
searchField.reloadPopUpRow(row)
|
||||
}
|
||||
|
||||
private var shouldDisplayNoResults: Bool {
|
||||
return searchResults.isEmpty && !searchField.stringValue.isEmpty && searchTask == nil
|
||||
}
|
||||
|
||||
public func numberOfRowsInTableView(tableView: NSTableView) -> Int {
|
||||
if shouldDisplayNoResults {
|
||||
return 1
|
||||
}
|
||||
return searchResults.count
|
||||
}
|
||||
|
||||
public func tableView(tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
|
||||
return 39
|
||||
}
|
||||
|
||||
public func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
if shouldDisplayNoResults {
|
||||
var view = tableView.makeViewWithIdentifier(Constants.NoResultsTableCellViewIdentifier, owner: nil) as? CenteredTableCellView
|
||||
if view == nil {
|
||||
view = CenteredTableCellView()
|
||||
view?.identifier = Constants.NoResultsTableCellViewIdentifier
|
||||
}
|
||||
view?.setupForNoResults()
|
||||
return view
|
||||
} else {
|
||||
var view = tableView.makeViewWithIdentifier(Constants.AlbumTableCellViewIdentifier, owner: nil) as? AlbumTableCellView
|
||||
if view == nil {
|
||||
view = AlbumTableCellView()
|
||||
view?.identifier = Constants.AlbumTableCellViewIdentifier
|
||||
}
|
||||
let searchResult = searchResults[row]
|
||||
let height = self.tableView(tableView, heightOfRow: row)
|
||||
let albumEnabled = delegate?.contentViewController.itemForEntity(searchResult) == nil
|
||||
view?.setupForAlbum(searchResult, enabled: albumEnabled, height: height)
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
||||
public func tableView(tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
|
||||
if shouldDisplayNoResults {
|
||||
return false
|
||||
} else {
|
||||
return delegate?.contentViewController?.itemForEntity(searchResults[row]) == nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user