Swift 3 table search - swift

I have populated a table using json data from a remote server.
I am now trying to add a search bar which will filter the results.
The issue I am facing is that I am storing the json data in several arrays.
name, company, job title etc
This means that when the user searches only the name array is filtered and displayed in the table correctly, the other information is out of sync as it remains unfiltered.
Am I approaching this in the correct way?
class attendees: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
var tableData = ""
var value:String!
var searchString = ""
var firstname: [String] = []
var lastname: [String] = []
var fullname: [String] = []
var company: [String] = []
var jobtitle: [String] = []
var image: [String] = []
var filteredAppleProducts = [String]()
var resultSearchController = UISearchController()
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
print(value)
searchBar.delegate = self
self.tableView.reloadData()
let nib = UINib(nibName: "vwTblCell2", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "cell2")
}
override func viewDidAppear(_ animated: Bool) {
getTableData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if filteredAppleProducts != []{
return self.filteredAppleProducts.count
}
else
{
if searchString != "[]" {
return self.firstname.count
}else {
return 0
}
}
}
// 3
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell2: TblCell2 = self.tableView.dequeueReusableCell(withIdentifier: "cell2") as! TblCell2
print(filteredAppleProducts)
if filteredAppleProducts != []{
cell2.nameLabel.text = self.filteredAppleProducts[indexPath.row]
return cell2
}
else
{
if searchString != "[]"{
cell2.nameLabel.text = "\(self.firstname[indexPath.row]) \(self.lastname[indexPath.row])"
cell2.companyLabel.text = self.company[indexPath.row]
cell2.jobTitleLabel.text = self.jobtitle[indexPath.row]
let url = URL(string: "https://www.asmserver.co.uk/wellpleased/backend/profileimages/\(self.image[indexPath.row])")
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
cell2.userImage.image = UIImage(data: data!)
}
return cell2
}
}
// 4
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
// 5
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 90
}
func updateSearchResults(){
self.filteredAppleProducts.removeAll(keepingCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchString)
let array = (self.fullname as NSArray).filtered(using: searchPredicate)
self.filteredAppleProducts = array as! [String]
self.tableView.reloadData()
print(filteredAppleProducts)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print("searchText \(searchText)")
print(filteredAppleProducts)
searchString = searchText
updateSearchResults()
}
func getTableData(){
self.firstname.removeAll()
self.lastname.removeAll()
self.fullname.removeAll()
self.company.removeAll()
self.jobtitle.removeAll()
self.image.removeAll()
let defaults = UserDefaults()
let userid = defaults.string(forKey: "id")
let url = NSURL(string: "https://www.asmserver.co.uk/wellpleased/backend/searchattendees.php?userid=\(userid!)&eventid=\(value!)")
print(url)
let task = URLSession.shared.dataTask(with: url as! URL) { (data, response, error) -> Void in
if let urlContent = data {
do {
if let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: []) as? [[String:AnyObject]] {
var i = 0
while i < jsonResult.count {
self.firstname.append(jsonResult[i]["firstname"]! as! String)
self.lastname.append(jsonResult[i]["lastname"]! as! String)
let fname = jsonResult[i]["firstname"]! as! String
let lname = jsonResult[i]["lastname"]! as! String
let fullname1 = "\(fname) \(lname)"
self.fullname.append(fullname1)
self.company.append(jsonResult[i]["company"]! as! String)
self.jobtitle.append(jsonResult[i]["jobtitle"]! as! String)
self.image.append(jsonResult[i]["image"]! as! String)
i = i + 1
}
}
} catch {
print("JSON serialization failed")
}
} else {
print("ERROR FOUND HERE")
}
DispatchQueue.main.async(execute: { () -> Void in
self.tableView.reloadData()
})
self.tableView.isUserInteractionEnabled = true
}
task.resume()
}
}

Use a struct or class for the data. This way it'll be easier to keep track of the data, and in any case it looks like you don't have any good reason to keep track of seven different arrays.
As an example:
struct Data {
var firstName: String
var lastName: String
var fullName: String
var company: String
var jobTitle: String
var image: String
}
And populate just the one array:
var dataSource: [Data] = []
Access with property name, instead of arrayName[index]:
let name = dataSource[index].firstName

Related

SearchBar doesn't returns results until activated and typing

I see empty rows when my tableView is loaded for the first time. But if I activate searchBar, write something into textField everything works correctly even i clean all the text. I want the app works without these extra steps. But I don't understand where exactly I am making the mistake.
class NextTableViewCell: UITableViewController, UISearchBarDelegate, UISearchControllerDelegate {
#IBOutlet weak var searchBar: UISearchBar!
var ref: DatabaseReference?
let db = Firestore.firestore()
var messages: [Message] = []
var filteredMessages: [Message] = []
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
tableView.dataSource = self
loadMessages()
filteredMessages = messages
}
Func loadMessages retrieves data from Firebase
func loadMessages() {
let user = Auth.auth().currentUser?.email
let docRef = db.collection(K.FStore.collectionName).document(user!)
docRef.addSnapshotListener { (querySnapshot, error) in
self.messages = []
if let e = error {
print(e)
} else {
if let snapshotDocuments = querySnapshot?.data(){
for item in snapshotDocuments {
if let key = item.key as? String, let translate = item.value as? String {
let newMessage = Message(key: key, value: translate)
self.messages.append(newMessage)
}
}
DispatchQueue.main.async { [self] in
self.messages.sort(by: {$1.key > $0.key})
self.tableView.reloadData()
}
}
}
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredMessages.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let message = filteredMessages[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "ListVC", for: indexPath)
cell.textLabel?.text = message.key + " - " + message.value
return cell
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredMessages = []
if searchText == "" {
filteredMessages = messages
}else{
for item in messages {
if item.key.lowercased().contains(searchText.lowercased()){
if let key = item.key as? String, let translate = item.value as? String {
let newMessage = Message(key: key, value: translate)
self.filteredMessages.append(newMessage)
}
}
}
}
tableView.reloadData()
}
The problem is that filteredMessages is empty when the view controller loads and only gets populated when you search.
Since filteredMessages is essentially a subset of messages, you need to set filteredMessages to messages upon fetching them from the database.
Try adding a line that does that in your loadMessages() method:
DispatchQueue.main.async { [self] in
self.messages.sort(by: {$1.key > $0.key})
self.filteredMessages = self.messages // Add this line
self.tableView.reloadData()
}

Swift Error with (outlets, keyPath, key value and UITableViewCellContentView)

I'm new to swift and I was just working on this program that has a table view with a list of names that the user can search through. It was working fine, until I tried to implement a UISearchbar and it gave me the error
Failed to set (keyPath) user defined inspected property on (UITableViewCellContentView): [<UITableViewCellContentView 0x7fb4624048c0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key keyPath.
I've done some research and I'm figured out that this error normally occurs when there is a problem with IB Outlets, but my outlets seem to be doing fine and this is what they look like-
Photo of my Outlet Connections
If you think there might be a problem in my code- here it is (Sorry it is messy):
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool {
if searchBar.text == "" {
searchBar.placeholder = "Enter Name"
return true
} else {
return true
}
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchBar.endEditing(true)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching == true {
return searchingPeople.count
} else {
return people.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tblView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
if searching == true {
cell.textLabel?.text = searchingPeople[indexPath.row]
} else {
cell.textLabel?.text = people[indexPath.row]
}
return cell
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
searchingPeople = people.filter({$0.lowercased().prefix(searchText.count) == searchText.lowercased()})
searching = true
tblView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
view.endEditing(true)
searching = false
searchBar.text = ""
tblView.reloadData()
}
var people: [String] = []
var searchingPeople = [String()]
var searching = false
#IBOutlet weak var tblView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
tblView.delegate = self
tblView.dataSource = self
searching = false
searchBar.text = ""
guard let path = Bundle.main.path(forResource: "finalDataPapa", ofType: "json") else { return }
let url = URL(fileURLWithPath: path)
do {
let data = try Data(contentsOf: url)
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
// print(json)
guard let array = json as? [Any] else { return }
//38104 people
var i: Int = 1
while i < 38104 {
guard let personDict = array[i] as? [String: Any] else { return }
guard let personfName = personDict["FIRST NAME"] as? String else { print("Persons name in numbers"); return }
guard let personlName = personDict["LAST NAME"] as? String else { print("Persons name in numbers"); return }
//guard let personmName = personDict["MIDDLE"] as? String else { print("Persons name in numbers"); return }
let fullName = "\(personfName) \(personlName)"
people.append(fullName)
i = i + 1
}
DispatchQueue.main.async {
self.tblView.reloadData()
}
}
catch {
print(error)
}
class People: Decodable {
let LASTNAME: String?
let FIRSTNAME: String?
let MIDDLE: String?
enum CodingKeys: String, CodingKey {
case FIRSTNAME = "FIRST NAME"
case LASTNAME = "LAST NAME"
case MIDDLE = "MIDDLE"
}
}
}
}

search in retrieved data from json in swift3

I retrieve data from service URL in swift3 and displayed in tableview. I tried to search names, it shows filtered names but another cell is not updating. please check my below code.
class MyViewController: UIViewController,UITableViewDataSource,UITableViewDelegate,UISearchBarDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
var filteredArray = [String]()
var shouldShowSearchResults = false
var nameArray = [String]()
var emailArray = [String]()
var tableData = [String]()
override func viewDidLoad() {
super.viewDidLoad()
downloadData()
createSearchBar()
// Do any additional setup after loading the view.
}
func createSearchBar(){
searchBar.showsCancelButton = false
searchBar.placeholder = "Enter your search"
searchBar.delegate = self
self.navigationItem.titleView = searchBar
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
tableData = nameArray
if shouldShowSearchResults
{
return filteredArray.count
}
else
{
return tableData.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:mycell! = tableView.dequeueReusableCell(withIdentifier: "cell") as! mycell
if shouldShowSearchResults
{
cell.name.text = filteredArray[indexPath.row]
cell.email.text = emailArray[indexPath.row]
return cell
}
else
{
cell.name.text = tableData[indexPath.row]
cell.email.text = emailArray[indexPath.row]
return cell
}
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
searchBar.endEditing(true)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
shouldShowSearchResults = true
searchBar.endEditing(true)
self.tableView.reloadData()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredArray = tableData.filter({
(names:String) -> Bool in
return names.lowercased().range(of: searchText.lowercased()) != nil
})
if searchText != ""
{
shouldShowSearchResults = true
self.tableView.reloadData()
}
else
{
shouldShowSearchResults = false
self.tableView.reloadData()
}
}
func downloadData()
{
let url = URL(string: "http://www.json-generator.com/api/json/get/crdvbKvLoy?indent=2")!
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: 10000)
URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil {
print(error!)
return
}
do {
if let jsonData = try JSONSerialization.jsonObject(with:data!, options: []) as? [[String:AnyObject]] {
print(jsonData)
// Utility.SharedInstance.dict_UserDetails3 = jsonData as AnyObject
for item in jsonData {
if let name = item["Name"] as? AnyObject {
self.nameArray.append(name as! String)
}
if let email = item["Email"] as? AnyObject{
self.emailArray.append(email as! String)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
} catch let error as NSError {
print(error)
}
}.resume()
}
}
output:
search___________
Name Email
----------------
Wasim wasim#gmail.com
Dravid dravid#gmail.com
Kohli virat#gmail.com
Kallis Jaques#gmail.com
I entered in search text as K
-----------------------------
search_____K______
Name Email
----------------
Kohli wasim#gmail.com
Kallis dravid#gmail.com
In this search works, but Email field not updating. please check once. I am tried more samples but not solved. please check once, what changes I do to solve this problem.
you filter name array but not the email address array. so the indexes don't match up.
The best solution is to not separate related data. Structure it properly.
struct Person {
var name: String
var email: String
}
var people: [Person]?
var filteredResults: [Person]?
Using a struct for a person that holds the name and email together, you can filter this array easier and the data wont get out of sync.

Search Bar Swift 3 - Can't use in/contains operator with collection

I am implementing a search bar into my project but I am being presented with the below error.
reason: 'Can't use in/contains operator with collection wellpleased.attendees.UserData(firstname: "Ben", lastname: "Delonge", fullname: "Ben Delonge", company: "AllStar.com", jobtitle: "Business Development Manager", image: "6.jpg") (not a collection)'
I have done plenty of searching around the NSPredicate but cannot seem to prevent this crashing.
I am using the code below, any assistance resolving this would be much appreciated.
class attendees: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
var tableData = ""
var value:String!
var searchString: String = ""
var dataSource: [UserData] = []
struct UserData {
var firstname: String
var lastname: String
var fullname: String
var company: String
var jobtitle: String
var image: String
}
var filteredAppleProducts = [String]()
var resultSearchController = UISearchController()
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
print(value)
searchBar.delegate = self
self.tableView.reloadData()
let nib = UINib(nibName: "vwTblCell2", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "cell2")
}
override func viewDidAppear(_ animated: Bool) {
getTableData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if filteredAppleProducts != []{
return self.filteredAppleProducts.count
}
else
{
if searchString != "[]" {
return self.dataSource.count
}else {
return 0
}
}
}
// 3
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell2: TblCell2 = self.tableView.dequeueReusableCell(withIdentifier: "cell2") as! TblCell2
print(filteredAppleProducts)
if filteredAppleProducts != []{
cell2.nameLabel.text = self.filteredAppleProducts[indexPath.row]
return cell2
}
else
{
if searchString != "[]"{
cell2.nameLabel.text = self.dataSource[indexPath.row].fullname
cell2.companyLabel.text = self.dataSource[indexPath.row].company
cell2.jobTitleLabel.text = self.dataSource[indexPath.row].jobtitle
let url = URL(string: "https://www.asmserver.co.uk/wellpleased/backend/profileimages/\(self.dataSource[indexPath.row].image)")
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
cell2.userImage.image = UIImage(data: data!)
}
return cell2
}
}
// 4
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
// 5
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 90
}
func updateSearchResults(){
self.filteredAppleProducts.removeAll(keepingCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchString)
let array = (self.dataSource as NSArray).filtered(using: searchPredicate)
self.filteredAppleProducts = array as! [String]
self.tableView.reloadData()
print(filteredAppleProducts)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print("searchText \(searchText)")
print(filteredAppleProducts)
searchString = searchText
updateSearchResults()
}
func getTableData(){
self.dataSource.removeAll()
let defaults = UserDefaults()
let userid = defaults.string(forKey: "id")
let url = NSURL(string: "https://www.******.co.uk/wellpleased/backend/searchattendees.php?userid=\(userid!)&eventid=\(value!)")
print(url)
let task = URLSession.shared.dataTask(with: url as! URL) { (data, response, error) -> Void in
if let urlContent = data {
do {
if let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: []) as? [[String:AnyObject]] {
var i = 0
while i < jsonResult.count {
self.dataSource.append(UserData(firstname:"\(jsonResult[i]["firstname"]! as! String)", lastname: "\(jsonResult[i]["lastname"]! as! String)", fullname:"\(jsonResult[i]["fullname"]! as! String)", company: "\(jsonResult[i]["company"]! as! String)", jobtitle:"\(jsonResult[i]["jobtitle"]! as! String)", image:"\(jsonResult[i]["image"]! as! String)"))
i = i + 1
}
}
} catch {
print("JSON serialization failed")
}
} else {
print("ERROR FOUND HERE")
}
DispatchQueue.main.async(execute: { () -> Void in
self.tableView.reloadData()
})
self.tableView.isUserInteractionEnabled = true
}
task.resume()
}
}
I have also tired:
let searchPredicate = NSPredicate(format: "fullname CONTAINS[c] %#", searchString as String)
which returns the error:
this class is not key value coding-compliant for the key fullname
NSPredicate is a Cocoa feature that lives in the Objective-C world. It's never going to work on an array of UserData because UserData is a Swift struct — and Objective-C cannot see a Swift struct at all (and even if it could, it certainly can't see any type namespaced inside a class, as your UserData is).
You would have an easy time of this if you simply used the built-in Swift filter method to filter the dataSource array. For example (if this is what you're trying to do):
let array = self.dataSource.filter{$0.fullname.contains(searchString)}
In Swift 3, you can combine NSArray with NSPredicate like this:
let searchPredicate = NSPredicate(format: "%K CONTAINS[c] %#", "fullname",searchString)
let array = NSArray(array: self.dataSource).filtered(using: searchPredicate)

Swift 3 Custom TableViewCell Error

I am having trouble communicating with the "TableView". How can I solve the problem?
Error Image
Code Image
var arrayOfData = [cellData]()
var fullname1 : String!
var code1 : String!
var name1 : String!
var buy1 : Double!
var sell1 : Double!
var change_rate1 : Double!
var update_date1 : Double!
var arrayFullName : [String] = ["asdas","asds","asds","sadas","asdasd","asdas"]
#IBOutlet weak var tblDoviz: UITableView!
#IBOutlet weak var openBarButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
arrayOfData = [cellData(fullName: "Amerikan Doları", name: "", code: "USD", updateDate: 2, changeRate: 2, buy: 3.44, sell: 3.47)]
openBarButton.target = self.revealViewController()
openBarButton.action = Selector("revealToggle:")
tblDoviz.delegate = self
tblDoviz.dataSource = self
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
/* Doviz Sitesinden Bilgiler Çekiliyor */
let url = NSURL(string: "http://www.doviz.com/api/v1/currencies/all/latest")
let task = URLSession.shared.dataTask(with: url as! URL){(data,response,error) ->Void in
if error != nil
{
}
else
{
if let urlContent = data
{
do
{
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let currencyRate = jsonResult as? NSArray
{
for i in 0..<currencyRate.count
{
if let name = (currencyRate[i] as? NSDictionary)?["name"] as? String
{
self.name1 = name
}
if let fullname = (currencyRate[i] as? NSDictionary)?["full_name"] as? String
{
self.fullname1 = fullname
}
if let change_rate = (currencyRate[i] as? NSDictionary)?["change_rate"] as? Double
{
self.change_rate1 = change_rate
}
if let code = (currencyRate[i] as? NSDictionary)?["code"] as? String
{
self.code1 = code
}
if let update_date = (currencyRate[i] as? NSDictionary)?["update_date"] as? Double
{
self.update_date1 = update_date
}
if let buying = (currencyRate[i] as? NSDictionary)?["buying"] as? Double
{
self.buy1 = buying
}
if let selling = (currencyRate[i] as? NSDictionary)?["selling"] as? Double
{
self.sell1 = selling
}
//Array'a Yükleme Yapılıyor
//cellData.init(fullName: self.fullname1, name: self.name1, code: self.code1, updateDate: self.update_date1, changeRate: self.change_rate1, buy: self.buy1, sell: self.sell1)
//self.arrayOfData = [cellData(fullName: self.fullname1, name: self.name1, code : self.code1, updateDate: self.update_date1, changeRate: self.change_rate1, buy: self.buy1, sell: self.sell1)]
//print(self.arrayOfData)
//print(cellData())
}
print(self.arrayFullName.count)
}
}
catch
{
}
}
}
}
task.resume()
//--------------------------------------------------
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellDoviz",for:indexPath) as! DovizCell1
Error-------------------------------------
cell.lblText.text = arrayFullName[indexPath.row]
Error -----------------------------------
return cell as DovizCell1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 94
}
}
Try adding this to your code
let cell:DovizCell? = tableView.dequeueReusableCell(withIdentifier: "cellDoviz",for:indexPath) as! DovizCell
if cell == nil {
cell = NSBundle.mainBundle().loadNibNamed("DovizCell").first as! DovizCell
}
Your DovizCell might have been nil which is why the code crashed