Day#25 寻找其他使用者(2) 资料结构

前言

此时,我们希望可以透过query的方式将db里的用户都显示出来。
因此今天将着重存在db的资料结构调整。

资料结构

现在db里的结构如下

但是想要做到搜寻,我们没办法使用no-sql的资料结构去一个个找key。因此在此我们要修改一下资料结构。

思考一下对话的资料结构可能的样貌,由於我们想要保留每个已存在的对话可以被显示在conversation table view当中,因此每个使用者都会有自己的user array。

/*
    [
        [
            "name": ,
            "safe_email
        ],
        [
            "name": ,
            "safe_email
        ],
    ]
*/

DatabaseManager

inserUser

当我们新增(注册)一个使用者时,在insertUser新增以下的内容

我们检查当下是否有以这个帐号为key的array

self.ref.child("users").observeSingleEvent(of: .value, with: { snapshot in

有的话就append、没有的话就新增。

if var usersCollection = snapshot.value as? [[String: String]] {
    // append to user dictionary
    let newElement = [
        "name": user.firstName + " " + user.lastName,
        "email": user.safeEmail
    ]
    usersCollection.append(newElement)
    self.ref.child("users").setValue(usersCollection, withCompletionBlock: { error , _ in
        guard error == nil else {
            completion(false)
            return
        }
        completion(true)
    })

} else {
    let newCollection: [[String: String]] = [
        [ "name": user.firstName + " " + user.lastName,
          "email": user.safeEmail
        ]
    ]
    self.ref.child("users").setValue(newCollection, withCompletionBlock: { error , _ in
        guard error == nil else {
            completion(false)
            return
        }
        completion(true)
    })
}
})

completion(true)

NewConversationViewController

然後我们接下来想要做的是,如果现在手边有all user就直接filter,没有的话就先fetch再filter。

因此我们先宣告会使用到的东西。
在此result是filter後的结果,而hasFetched则是一个开关。

private let spinner = JGProgressHUD(style: .dark)

private var users = [[String: String]]()
private var results = [[String: String]]()
private var hasFetched = false

search users

接着就是将刚刚提到的逻辑实作出来,我们使用replacingOccurrences确保query的内容已经被trim过。

extension NewConversationViewController: UISearchBarDelegate {
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        guard let text = searchBar.text, !text.replacingOccurrences(of: " ", with: "").isEmpty else {
            return
        }
        
        results.removeAll()
        spinner.show(in: view)
        self.searchUsers(query: text)
    }

拿到处理好的query後,开始从db比对。

    func searchUsers(query: String) {
        // check if array has firebase result
        if hasFetched {
            self?.filterUsers(with: query)
        } else {
            // fetch then filter
            DatabaseManager.shared.getAllUsers(completion: { [weak self] result in
                switch result {
                case .success(let usersCollection):
                    self?.users = usersCollection
                    self?.filterUsers(with: query)
                case .failure(let error):
                    print("Fail to get user: \(error)")
                }
        
            })
        }
    }
    
    func filterUsers(with term: String){
        // update the UI
        guard hasFetched else {
            return
        }
        
        let results: [[String: String]] = self.users.filter {
            guard let name = $0["name"]?.lowercased() else{
                return false
            }
            return name.hasPrefix(term.lowercased())
        }
        self.results = results
        
        updateUI()
    }

最後将结果显示出来,如果有结果就将tableView刷新显示;没有的话就显示noResult的label。而这两的内容都已经有再先前写好了!

    func updateUI() {
        if results.isEmpty {
            self.noResultLabel.isHidden = false
            self.tableView.isHidden = true
        } else {
            self.noResultLabel.isHidden = true
            self.tableView.isHidden = false
            self.tableView.reloadData()
        }
    }
}

viewDidLoad

最後我们在Load进来的时候把subview都加进来

view.addSubview(noResultLabel)
view.addSubview(tableView)

tableView.delegate = self
tableView.dataSource = self

当然也别忘了跟随着tableView.delegate以及tableView.dataSource的extension。

extension

extension NewConversationViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return results.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = results[indexPath.row]["name"]
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        // start conversation
    }
}

结语

留了一个小伏笔,在didSelectRowAt方法中,也就是当点选某个row,即代表开始展开对话~
终於要开始实作这30天的重点了吗(??)

若上述内容有误或可以改进的部分,欢迎留言以及提出任何指教~
谢谢 (´・∀・`)


<<:  输家的特质

>>:  【Day27】反馈元件 - Progress circle

认识CSS(七):CSS list-style

CSS list-style 是提供网页调整列表清单中更多的显示功能,之前提到的HTML表单里有传统...

D-15 过滤器 ? filter ? attribute

filter 眼尖的小光在昨日的内容中看到了一个有趣的东西,就是MiddlewareFilter,所...

Day26 Flutter 的状态管理 Provider (五) Firebase Login

main,dart: import 'package:firebase_core/firebase_...

第7天

中秋连假结束~ 参考线完成开始放入标题~ 把textview跟参考线连在一起~ 使用TextView...

[Day 10] - Spring Boot 实作登入验证(四)(JWT登入验证)

今天就来完成登入验证的部分! 昨天已经完成发送帐号密码到api,验证ok即发送一笔JWT给clien...