Как вы выполняете поиск по данным firebase в tableview с помощью панели поиска?

#ios #swift #firebase #tableview #searchbar

#iOS #swift #firebase #просмотр таблицы #панель поиска

Вопрос:

Я настроил tableview, который извлекает данные из базы данных firebase в реальном времени и сохраняет эти данные в массиве (‘posts’) в таком формате:

(имя: nil, contactEmail: необязательно («35345345235»), contactPhoneNum: необязательно(«contact@gmail.com «), возраст: необязательно («25»), пол: ноль, последнее просмотренное: ноль, описание профиля: ноль)

Я хочу реализовать панель поиска для фильтрации значения имени записей и возврата записей, которые содержат искомое имя в tableview, и не уверен, как это сделать.

Вот мой код:

 import UIKit
import Firebase
import FirebaseDatabase
import SwiftKeychainWrapper
import FirebaseAuth

class FeedVC: UITableViewController, UISearchBarDelegate{
    
    @IBOutlet weak var searchBar: UISearchBar!
    
    var currentUserImageUrl: String!
    var posts = [postStruct]()
    var selectedPost: Post!
    var filteredPosts = [postStruct]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        getUsersData()
        getPosts()
        searchBar.delegate = self
        // Do any additional setup after loading the view.
   //     tableView.register(PostCell.self, forCellReuseIdentifier: "PostCell")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    
    func getUsersData(){

        guard let userID = Auth.auth().currentUser?.uid else { return }
        Database.database().reference().child("users").child(userID).observeSingleEvent(of: .value) { (snapshot) in
            if let postDict = snapshot.value as? [String : AnyObject] {
                self.tableView.reloadData()
            }
        }
    }
    
    struct postStruct {
        let name : String!
        let contactEmail : String!
        let contactPhoneNum : String!
        let age : String!
        let gender : String!
        let lastSeen : String!
        let profileDescription : String!
    }
    
    func getPosts() {
        let databaseRef = Database.database().reference()
        databaseRef.child("firstName").queryOrderedByKey().observe( .childAdded, with: {
                    snapshot in
                    let name = (snapshot.value as? NSDictionary)!["name"] as? String
                    let contactEmail = (snapshot.value as? NSDictionary
                    )!["contactEmail"] as? String
                    let contactPhoneNum = (snapshot.value as? NSDictionary
                    )!["contactPhoneNum"] as? String
                    let age = (snapshot.value as? NSDictionary
                    )!["age"] as? String
                    let gender = (snapshot.value as? NSDictionary
                    )!["gender"] as? String
                    let lastSeen = (snapshot.value as? NSDictionary
                    )!["lastSeen"] as? String
                    let profileDescription = (snapshot.value as? NSDictionary
                    )!["profileDescription"] as? String
            self.posts.append(postStruct(name: name,contactEmail:contactEmail, contactPhoneNum:contactPhoneNum, age:age, gender:gender, lastSeen:lastSeen, profileDescription:profileDescription))
            DispatchQueue.main.async {
                        self.tableView.reloadData()
                    }
                })
    }
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        print("posts count = ", filteredPosts.count)
        return filteredPosts.count

   }
    
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
      //  tableView.dequeueReusableCell(withIdentifier: "PostCell")!.frame.size.height
        return 230
    }
   override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell") as? PostCell else { return UITableViewCell() }
    cell.nameLabel?.text = filteredPosts[indexPath.row].name
        cell.contactEmailLabel?.text = filteredPosts[indexPath.row].contactEmail
        cell.contactPhoneNumLabel?.text = filteredPosts[indexPath.row].contactPhoneNum
        cell.ageLabel?.text = filteredPosts[indexPath.row].age
        cell.genderLabel?.text = filteredPosts[indexPath.row].gender
        cell.lastSeenLabel?.text = filteredPosts[indexPath.row].lastSeen
        cell.profileDescriptionLabel?.text = filteredPosts[indexPath.row].profileDescription
        print(filteredPosts)
        return cell
    }
    
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
          filteredPosts = posts.filter { $0.name?.lowercased().contains(searchText.lowercased()) == true }
}


}

  

Комментарии:

1. Требуется некоторое уточнение; Вы хотите загружать каждую запись данные в память из Firebase и сохранять их все в массиве, а затем фильтровать это? Или вы хотите запросить данные firebase и вернуть только это подмножество данных? Это важно, поскольку, если у вас миллион записей, вы перегрузите устройство загрузкой всего этого. Кроме того, ваш массив содержит только ‘name’ (как в строке) или он содержит всю ‘post’?

2. Извините, я довольно новичок в firebase. Я хочу выполнить поиск по «имени» каждой записи, чтобы вернуть совпадающие имена, поэтому я не уверен, какой подход выбрать. Массив ‘posts’ содержит все данные post, сохраненные как postStruct (формат, показанный выше).

3. Вам нужно будет принять решение, поскольку подход сильно отличается. Если у вас много записей, это может перегрузить устройство, как упоминалось, но если вы запрашиваете firebase, вам потребуется вернуть только подмножество данных.

4. На самом деле это не плохой вопрос — просто нужна некоторая ясность, выполняет ли OP поиск по массиву или Firebase. Проголосовали за повторное открытие

5. Я бы предпочел запрашивать firebase для имен

Ответ №1:

Зацикливание на posts предоставит элемент типа Post not String . Вот исправление:

 func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    filteredPosts = []
    for post in posts {
        if post.name?.lowercased().contains(searchText.lowercased()) == true {
            filteredPosts.append(post)
        }
    }
}
  

Или просто используйте метод более высокого порядка, такой filter как вдохновленный комментарием @ Jay.

 func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    filteredPosts = posts.filter { $0.name?.lowercased().contains(searchText.lowercased()) == true }
}
  

Комментарии:

1. @maya В опубликованном ответе нет ничего плохого, кроме расширенных циклов, которых следует избегать. Это немного короче let filteredResults = posts.filter { $0.name.lowercased() == "string you're searching for".lowercased() }

2. @Jay Я даже не знаю, почему я добавил дополнительный цикл в свой ответ. Я внес несколько изменений в ваше предложение, например, нам не нужно объявлять новую переменную, filteredResults которой мы могли бы напрямую назначить результат, filteredPosts также содержит поиск и необязательное name свойство :).

3. @Jay Я изменил свой код, чтобы включить это (см. Редактирование), но в filteredResults нет никаких значений: print(filteredPosts.count) возвращает количество записей = 0

4. @maya Смотрите комментарии к вопросу, но если вам нужна помощь, вам нужно выполнить дополнительные действия по устранению неполадок. Например, действительно ли массив posts содержит какие-либо данные? Действительно ли вызывается функция делегирования при вводе текста? Вы добавили точку останова и проверяли переменные по мере ввода? Каков же был результат? Видите — мы не можем сделать это за вас, но базовое устранение неполадок может выявить причину и предоставит более точную информацию для включения в вопрос, чтобы нам было, над чем работать. Прямо сейчас — мы просто предполагаем.