Swift Xcode 13 Programmati c UITableViewController ноль делегат - PullRequest
0 голосов
/ 01 марта 2020

Делегированная функция запускается, но вылетает как ее ноль, объекты в массиве элементов заполняются CoreData, этот var model: CoreDataModel = CoreDataModel(CoreDataController.shared) должен быть создан, а не как ожидалось в viewDidLoad, чтобы предотвратить нулевую ошибку для счетчика строк представления таблицы. (model.items.count)

При запуске массив элементов представляет собой полную базу данных Sqlite, при поиске ее подмножество таблицы и печать в консоль подтверждают, что массив изменен и имеет только подмножество альбомов.

BaseViewController

import UIKit
import CoreData

protocol UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
}

protocol MasterModel {
    var client: LastFMClient { get }
    func searchFeed(with userSearchTerm: String?, completion: @escaping (Bool) -> Void)
}

protocol DataReloadTableViewDelegate: class {
    func reloadAlbumsTable()
}

class BaseViewController: UITableViewController, MasterModel {

    let cellId = "sdlfjowieurewfn3489844224947824dslaksjfs;ad"
    let logoContainer = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
    let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
    let image = UIImage(named: "lastFMRedBlack")
    let searchBar = UISearchBar()
    let client = LastFMClient()
    var model: CoreDataModel = CoreDataModel(CoreDataController.shared)
    private var searchResults: Root?

    override func viewDidLoad() {
        super.viewDidLoad()
        setupSearchController()
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
        tableView.register(SubtitleTableViewCell.self, forCellReuseIdentifier: cellId)
        tableView.tableFooterView = UIView(frame: CGRect.zero)
        tableView.separatorColor = UIColor(red: 72.5/255, green: 0/255, blue: 0/255, alpha: 1)

        imageView.contentMode = .scaleAspectFit
        imageView.image = image
        logoContainer.addSubview(imageView)
        navigationItem.titleView = logoContainer

        print(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask))

        model.delegate = self
        model.fetchAllAlbums()
    }

    // MARK - SearchBar
    private func setupSearchController() {

        searchBar.sizeToFit()
        searchBar.placeholder = "Search for Album"
        searchBar.delegate = self
        showSearchBarButton(shouldShow: true)
    }

    func showSearchBarButton (shouldShow: Bool) {

        if shouldShow {
            navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .search, target: self, action: #selector(handleShowSearchBar))

        } else {
            searchBar.showsCancelButton = true
            navigationItem.rightBarButtonItem = nil
        }
    }

    func search(shouldShow: Bool) {
        showSearchBarButton(shouldShow: !shouldShow)
        navigationItem.titleView = shouldShow  ? searchBar : logoContainer
    }


    @objc func handleShowSearchBar(){

        search(shouldShow: true)
        searchBar.becomeFirstResponder()
    }


    // MARK - API Request
    func searchFeed(with userSearchTerm: String?, completion: @escaping (Bool) -> Void) {

        // Use the API to get data
        client.getFeed(from: LastFMRequest.albumSearch(userSearchTerm: userSearchTerm) ) { result in

            switch result {
            case .success(let data):

                do {
                    let data = try DataParser.parse(data, type: RootNode.self)

                    self.searchResults = data.results
                    completion(true)

                } catch {
                    print(error.localizedDescription)
                    completion(false)
                }

            case .failure(let error):
                print(error.localizedDescription)
                completion(false)
            }
        }
    }
}


extension BaseViewController: UISearchBarDelegate {

    func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
        searchBar.text = nil
    }

    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {

        search(shouldShow:  false)
    }

    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {

        guard let searchTextString = searchBar.text else { return }

        searchFeed(with: searchTextString.replacingOccurrences(of: " ", with: "+").lowercased(), completion: {_ in

            if self.searchResults!.albumMatches.album.count == 0 {

                DispatchQueue.main.async {
                    let alertController = UIAlertController(title: "No Albums Found", message: "Try Another Keyword(s)", preferredStyle: .alert)
                    let OKAction = UIAlertAction(title: "OK", style: .default) { action in
                        print("Pressed OK")
                    }
                    alertController.addAction(OKAction)
                    self.present(alertController, animated: true, completion: nil)
                }

            } else {

                let dataManager = DataManager(data: self.searchResults!)
                do {
                    try dataManager.saveData()

                } catch {

                    print(error)
                }

            }
        })

        search(shouldShow:  false)
        searchBar.resignFirstResponder()
    }
}

class SubtitleTableViewCell: UITableViewCell {

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension BaseViewController: UITableViewDataSource {

    var numberOrSections: Int { return 1 }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        guard section >= 0 && section < numberOrSections else { return 0 }

        return model.items.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)

        let albumItem = model.items[indexPath.row]
        cell.textLabel?.text = albumItem.value(forKeyPath: "name") as? String
        cell.detailTextLabel?.text = albumItem.value(forKeyPath: "artist") as? String

        cell.accessoryType = .disclosureIndicator
        // Populate the cell from the object
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        let vc = DetailViewController()
        let albumItem = model.items[indexPath.row]
        vc.iamgeURL = albumItem.value(forKeyPath: "imageUrl") as? String
        vc.albumName = albumItem.value(forKeyPath: "name") as? String
        navigationController?.pushViewController(vc, animated: true)
    }


}

extension BaseViewController: DataReloadTableViewDelegate {

    func reloadAlbumsTable(){
        DispatchQueue.main.async {
            print(self.model.items.count)
            self.tableView.reloadData()
        }
    }  
}

CoreDataModel

import Foundation import CoreData

, класс CoreDataModel {

weak var delegate: DataReloadTableViewDelegate?
let coreDataController: CoreDataController
var items:[Albums] = []

init(_ coreDataController: CoreDataController) {
    self.coreDataController = coreDataController
    self.coreDataController.mainContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
}

internal func saveSearchAlbums(responseData: Root) throws {

    let newSearch = Searches(context: coreDataController.mainContext)

    newSearch.searchQuery = responseData.attr.forField

    for (_, element) in responseData.albumMatches.album.enumerated() {

        let newAlbum = Albums(context: coreDataController.mainContext)

        let artistName = element.artist
        let albumName = element.name
        let imageUrlTwo = element.image[2].text
        let imageUrlZero = element.image[0].text
        let imageUrlOne = element.image[1].text

        var imageUrl: String = ""

        if !JustLetters.blank(text: imageUrlTwo) {
            imageUrl = imageUrlTwo
        }

        if !JustLetters.blank(text: imageUrlZero) {
            imageUrl = imageUrlZero
        }

        if !JustLetters.blank(text: imageUrlOne) {
            imageUrl = imageUrlOne
        }

        if !JustLetters.blank(text: artistName) && !JustLetters.blank(text: albumName) && !JustLetters.blank(text: imageUrl) {

            newAlbum.searches = newSearch
            newAlbum.artist = artistName
            newAlbum.name = albumName
            newAlbum.imageUrl = imageUrl

            newSearch.addToAlbums(newAlbum)
        }

    }
    // Save context
    coreDataController.saveContext()
    fetchAlbumsByKeyword(searchTerm: responseData.attr.forField)
}

internal func fetchAlbumsByKeyword(searchTerm: String) {

    // Create Fetch Request
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Albums")

    // Add Sort Descriptor
    let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
    fetchRequest.sortDescriptors = [sortDescriptor]

    // Add Predicate
    let predicate = NSPredicate(format: "name CONTAINS[c] %@", searchTerm)
    fetchRequest.predicate = predicate

    do {
        items = try  coreDataController.mainContext.fetch(fetchRequest) as! [Albums]
    } catch {
        print(error)
    }
    delegate!.reloadAlbumsTable()
}

internal func fetchAllAlbums() {

    // Create the FetchRequest for all searches
    let allAlbums: NSFetchRequest = Albums.fetchRequest()

    do {
        items = try coreDataController.mainContext.fetch(allAlbums)
    } catch {
        print(error)
    }
}

}

1 Ответ

0 голосов
/ 01 марта 2020

Делегат назначается / устанавливается для имени класса, а не для любого идентификатора экземпляра, поэтому делегат может быть установлен только для класса с одним экземпляром

Я не могу показать конкретное c доказательство, полагаюсь по причине и следствию одного изменения, чтобы сделать вышеупомянутое утверждение.

У меня было более одного экземпляра CoreDataModel, я установил делегат для первого экземпляра в viewDidLoad, второй экземпляр устанавливается при щелчке поиска , Я произвел рефакторинг класса DataManager, который сам создает, и экземпляра CoreDataModel.

Окончательный результат заключается в том, что делегат не равен нулю и работает как ожидалось. Репо 'show_album_search_results' ветка

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...