My locationSearchTable doesn't always yield proper results... why? - swift

So I have this navigation app in which a user can search for a location in a search table. If they type in a store like Mcdonald's, Target etc it will always appear correctly. However if you type a specific address it won't show up. Here's my tableView code for the locationSearchTable:
import UIKit
import MapKit
class LocationSearchTable : UITableViewController {
var handleMapSearchDelegate:handleMapSearch? = nil
var matchingItems:[MKMapItem] = []
var mapView: MKMapView? = nil
var resultSearchController:UISearchController? = nil
func parseAddress(selectedItem: MKPlacemark) -> String {
// put a space between "4" and "Melrose Place"
let firstSpace = (selectedItem.subThoroughfare != nil && selectedItem.thoroughfare != nil) ? " " : ""
// put a comma between street and city/state
let comma = (selectedItem.subThoroughfare != nil || selectedItem.thoroughfare != nil) && (selectedItem.subAdministrativeArea != nil || selectedItem.administrativeArea != nil) ? ", " : ""
// put a space between "Washington" and "DC"
let secondSpace = (selectedItem.subAdministrativeArea != nil && selectedItem.administrativeArea != nil) ? " " : ""
let addressLine = String(
format:"%#%#%#%#%#%#%#",
// street number
selectedItem.subThoroughfare ?? "",
firstSpace,
// street name
selectedItem.thoroughfare ?? "",
comma,
// city
selectedItem.locality ?? "",
secondSpace,
// state
selectedItem.administrativeArea ?? ""
)
return addressLine
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return matchingItems.count - 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "searchCell") as! ResultsDisplayCell
let selectedIndex = self.matchingItems[indexPath.row]
cell.titleLabel?.text = selectedIndex.name
cell.detailTextLabel?.text = selectedIndex.placemark.title
//cell.detailTextLabel?.text =
//cell.detailTextLabel?.text = selectedIndex.name
//cell.detailTextLabel?.text = selectedIndex.placemark.subtitle
return cell
}
}
extension LocationSearchTable : UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
guard let mapView = mapView,
let searchBarText = searchController.searchBar.text else { return }
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = searchBarText
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.start { response, _ in
guard let response = response else {
return
}
for mapItems in response.mapItems{
self.matchingItems.append(mapItems as MKMapItem)
print("thisisthearray:\(self.matchingItems)")
self.matchingItems = response.mapItems
self.tableView.reloadData()
}
}
}
}
extension LocationSearchTable {
override func tableView(_ tableView: UITableView, didSelectRowAt
indexPath: IndexPath) {
let selectedItem = matchingItems[indexPath.row].placemark
self.dismiss(animated: true, completion: {
self.handleMapSearchDelegate?.dropPinZoomIn(placemark: selectedItem)
})
}
}
I'm not really sure why it won't find the addresses correctly. Maybe I'm not referencing the parseAddress function correctly?
Here's a GitHub link: https://github.com/derekvandermark/VHNavigationAppOfficial-v-0.93

Related

Getting "Thread 1 fatal error index out of range"

I am getting the following error "thread 1 fatal error index out of range", which is appearing in my feedViewController and AppDelegate
What could be the cause of this? I am very new to coding and mainly following tutorials, however this error has not been covered.
#IBOutlet weak var tableView: UITableView!
var userEmailArray = [String]()
var userCommentArray = [String]()
var likeArray = [Int]()
var userImageArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// Do any additional setup after loading the view.
getDataFromFirestore()
}
func getDataFromFirestore() {
let fireStoreDatabase = Firestore.firestore()
/* let settings = fireStoreDatabase.settings
settings.areTimestampsInSnapshotsEnabled = true
fireStoreDatabase.settings = settings
*/
fireStoreDatabase.collection("Posts").order(by: "date", descending: true).addSnapshotListener { (snapshot, error) in
if error != nil {
print(error?.localizedDescription)
} else {
if snapshot?.isEmpty != true && snapshot != nil {
self.userImageArray.removeAll(keepingCapacity: false)
self.userEmailArray.removeAll(keepingCapacity: false)
self.userCommentArray.removeAll(keepingCapacity: false)
self.likeArray.removeAll(keepingCapacity: false)
for document in snapshot!.documents {
let documentID = document.documentID
if let postedBy = document.get("postedBy") as? String {
self.userEmailArray.append(postedBy)
}
if let postComment = document.get("postComment") as? String {
self.userCommentArray.append(postComment)
}
if let likes = document.get("likes") as? Int {
self.likeArray.append(likes)
}
if let imageUrl = document.get("imageUrl") as? String {
self.userImageArray.append(imageUrl)
}
}
self.tableView.reloadData()
}
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userEmailArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FeedCell
cell.userEmailLabel.text = userEmailArray[indexPath.row]
cell.likeLabel.text = String(likeArray[indexPath.row])
cell.commentLabel.text = userCommentArray[indexPath.row]
cell.userImageView.sd_setImage(with: URL(string: self.userImageArray[indexPath.row]))
return cell
}
}
This is the line where the error is:
cell.userImageView.sd_setImage(with: URL(string: self.userImageArray[indexPath.row]))

Swift - Firebase : How to search with multi data? [duplicate]

This question already has answers here:
Query based on multiple where clauses in Firebase
(8 answers)
Closed 2 years ago.
I want to know how to search for multi-entry in Firebase.
Now I only can search with one entry(activity name).
The Activity table and i marked * for which i need add to search entry:
Activity{
* act_id
* act_name
* act_region
* act_district
* act_subDistrict
* act_fee
* act_type
* act_startTime
* act_endTime
act_status
act_owner
act_intro
}
This is my search controller code. Thank you for your help.
import UIKit
import FirebaseDatabase
class SearchTableViewController: UITableViewController, UISearchResultsUpdating {
let searchController = UISearchController(searchResultsController: nil)
#IBOutlet var searchCenterTableView: UITableView!
var userArray = [NSDictionary?]()
var filteredUsers = [NSDictionary?]()
var databaseRef = Database.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
databaseRef.child("Activity").queryOrdered(byChild: "act_name").observe(.childAdded, with: { (snapshot) in
self.userArray.append(snapshot.value as? NSDictionary)
self.searchCenterTableView.insertRows(at: [IndexPath(row: self.userArray.count-1, section: 0)], with: UITableViewRowAnimation.automatic)
}) { (error) in
print(error.localizedDescription)
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive && searchController.searchBar.text != ""{
return filteredUsers.count
}
return self.userArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let user : NSDictionary?
if searchController.isActive && searchController.searchBar.text != ""{
user = filteredUsers[indexPath.row]
}else{
user = self.userArray[indexPath.row]
}
cell.textLabel?.text = user?["act_name"] as? String
cell.detailTextLabel?.text = user?["act_subDistrict"] as? String
return cell
}
func updateSearchResults(for searchController: UISearchController) {
filterContent(searchText: self.searchController.searchBar.text!)
}
func filterContent(searchText:String){
self.filteredUsers = self.userArray.filter{ user in
let username = user!["act_name"] as? String
return(username?.lowercased().contains(searchText.lowercased()))!
}
tableView.reloadData()
}
}
func filterContent(searchText:String, entries:[String]){
self.filteredUsers = self.userArray.filter{ user in
guard let user = user as? User else { return false }
var array:[Bool] = [Bool]()
for key in entries {
let value = user[key] as? String
let entry = value?.lowercased().contains(searchText.lowercased()) ?? false
array.append(entry)
}
return(array.contains(true))
}
tableView.reloadData()
}

Swift Fatal Error index out of range while using searchbar in tableview

i try to load an json data with tableView and i wanna add search data using uiSearchBar, data is search well but when i try to hit 'x' button on the searchBar or when i try to backspace the input it crash...Any help guys? or should i change my search method? thanks for the help. I'm still new in swift so if theres a better method to search please let me know:)
struct ProjectSumName: Decodable {
let id : Int
let name : String
enum CodingKeys : String, CodingKey {
case id = "id"
case name = "name"
}
}
class ProjectSumController: UIViewController {
#IBOutlet weak var SearchBar: UISearchBar!
#IBOutlet weak var ProjectSumTableView: UITableView!
var projectSum = [ProjectSumName]()
var filterProject : [ProjectSumName] = [ProjectSumName]()
var isSearch : Bool = false
override func viewDidLoad() {
super.viewDidLoad()
SearchBar.delegate = self
Loading()
let jsonUrl = "http://\(GlobalVariable.ip):7000/api/projectApi?UserId=\(GlobalVariable.UserIdProjectSum)"
guard let url = URL(string: jsonUrl) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
do{
let projectsum = try JSONDecoder().decode([ProjectSumName].self, from: data)
self.projectSum = projectsum
self.filterProject = projectsum
DispatchQueue.main.async {
SVProgressHUD.dismiss()
self.ProjectSumTableView.reloadData()
}
}catch {
print(error)
}
}.resume()
}
}
extension ProjectSumController : UISearchBarDelegate, UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isSearch{
return filterProject.count
}else{
return projectSum.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let proc = projectSum[indexPath.row]
let proc1 = filterProject[indexPath.row]
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? ProjectSumTableCell else {return UITableViewCell()}
if isSearch{
cell.NameLbl.text = proc1.name
}else{
cell.NameLbl.text = proc.name
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let sum = projectSum[indexPath.row]
let sum1 = filterProject[indexPath.row]
if isSearch{
performSegue(withIdentifier: "Segue", sender: sum1)
let projectIDs = sum1.id
GlobalVariable.ProjectId = String(projectIDs)
}else{
performSegue(withIdentifier: "Segue", sender: sum)
let projectID = sum.id
GlobalVariable.ProjectId = String(projectID)
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText.isEmpty{
self.isSearch = false;
self.ProjectSumTableView.reloadData()
} else {
self.filterProject = self.projectSum.filter({ (ProjectSumName) -> Bool in
let tmp : NSString = NSString.init(string: ProjectSumName.name)
let range = tmp.range(of: searchText, options: NSString.CompareOptions.caseInsensitive)
return range.location != NSNotFound && range.location == 0
})
if(self.filterProject.count == 0){
self.isSearch = false;
}else{
self.isSearch = true;
}
self.ProjectSumTableView.reloadData()
}
}
"Fatal error: Index out of range
2019-06-27 09:43:46.167472+0700 ImmobiTracker[806:30114] Fatal error: Index out of range"
that crash come up everything i try to clear my searchbar...so when i try to type the first time to search its filtering the data, but when i try to clear the search bar it pop crash
There are two places where you are getting this error.
in cellForRowAt:
let proc = projectSum[indexPath.row]
let proc1 = filterProject[indexPath.row]
didSelectRowAt
let sum = projectSum[indexPath.row]
let sum1 = filterProject[indexPath.row]
Why:
You are trying to get an element from filterProject without using isSearch i.e. filterPoject array is empty. When isSearch is false then the error occurs because you are trying to get the element from the empty array.
How to Solve this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let sum: ProjectSumName
if isSearch{
sum = filterProject[indexPath.row]
}else{
sum = projectSum[indexPath.row]
}
GlobalVariable.ProjectId = String(sum.id)
performSegue(withIdentifier: "Segue", sender: sum)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? ProjectSumTableCell else {return UITableViewCell()}
let proc: ProjectSumName
if isSearch{
proc = filterProject[indexPath.row]
}else{
proc = projectSum[indexPath.row]
}
cell.NameLbl.text = proc.name
return cell
}

App crashing trying to pass data between a view controller and a tableviewcontroller

So I have a navigation controller that consist of a view controller and a UITableViewController. The view controller contains a mapview and a textfield and the tableviewcontroller searches for the locations.
When i choose a location in the table view controller, I would like to be able to store the address in the textfield. I can get the address that I want to print
However whenever I try to assign that address to the textfield, the app crashes with a "fatal error: unexpectedly found nil while unwrapping an Optional value" Does anyone know why? This is my tableview controller code:
import UIKit
import MapKit
class LocationSearchTable: UITableViewController {
weak var handleMapSearchDelegate: HandleMapSearch?
var matchingItems: [MKMapItem] = []
var mapView: MKMapView?
func parseAddress(_ selectedItem:MKPlacemark) -> String {
// put a space between "4" and "Melrose Place"
let firstSpace = (selectedItem.subThoroughfare != nil &&
selectedItem.thoroughfare != nil) ? " " : ""
// put a comma between street and city/state
let comma = (selectedItem.subThoroughfare != nil || selectedItem.thoroughfare != nil) &&
(selectedItem.subAdministrativeArea != nil || selectedItem.administrativeArea != nil) ? ", " : ""
// put a space between "Washington" and "DC"
let secondSpace = (selectedItem.subAdministrativeArea != nil &&
selectedItem.administrativeArea != nil) ? " " : ""
let addressLine = String(
format:"%#%#%#%#%#%#%#",
// street number
selectedItem.subThoroughfare ?? "",
firstSpace,
// street name
selectedItem.thoroughfare ?? "",
comma,
// city
selectedItem.locality ?? "",
secondSpace,
// state
selectedItem.administrativeArea ?? ""
)
return addressLine
}
}
extension LocationSearchTable : UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
guard let mapView = mapView,
let searchBarText = searchController.searchBar.text else { return }
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = searchBarText
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.start { response, _ in
guard let response = response else {
return
}
self.matchingItems = response.mapItems
self.tableView.reloadData()
}
}
}
extension LocationSearchTable {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return matchingItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
let selectedItem = matchingItems[indexPath.row].placemark
cell.textLabel?.text = selectedItem.name
cell.detailTextLabel?.text = parseAddress(selectedItem)
// let right = (cell.detailTextLabel?.text)!
// print(right)
//print(selectedItem.name)
// print(parseAddress(selectedItem))
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedItem = matchingItems[indexPath.row].placemark
handleMapSearchDelegate?.dropPinZoomIn(selectedItem)
dismiss(animated: true, completion: nil)
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
cell.detailTextLabel?.text = parseAddress(selectedItem)
DispatchQueue.main.async {
let right = cell.detailTextLabel?.text
print (right!)
let vc = ViewController()
vc.testTxt.text = right!
}
}
}
You should not be doing this cell = tableView.dequeueReusableCell(withIdentifier: "cell")! in your didSelectRow method. If you want to see which item is selected, you need to get it from array by indexPath.row as index like this
verride func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedItem = matchingItems[indexPath.row].placemark
}
To pass this data to the previous view controller, you need an unwind segue like this post.
In your extension in the didSelectRow you're dismissing the controller then trying to dequeue the cell. Since the controller no longer exists I think tableView is nil and dequeueing causes a crash. What you probably want to do is when pushing the TableViewController you should pass in the reference of the current controller and then use that to set the textfield property
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedItem = matchingItems[indexPath.row].placemark
handleMapSearchDelegate?.dropPinZoomIn(selectedItem)
dismiss(animated: true, completion: nil)
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
cell.detailTextLabel?.text = parseAddress(selectedItem)
}

cellForRowAtIndexPath is not called while tableView.reloadData is called

I'm working for the tableview with search bar.
When the search text is changed, I call tableView.reloadData to make cells about the search result.
However, even I call tableView.reloadData, cellForRowAtIndexPath is not called.
So I don't got any cells about the search result.
import UIKit
class RestaurantsViewController: UITableViewController,UISearchBarDelegate, UISearchResultsUpdating {
#IBOutlet weak var menuButton: UIBarButtonItem!
var restaurantsData = RestaurantsData()
override func viewDidLoad() {
super.viewDidLoad()
if self.revealViewController() != nil {
menuButton.target = self.revealViewController()
menuButton.action = #selector(SWRevealViewController.revealToggle(_:))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
loadRestaurantsData()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
}
private func loadRestaurantsData() {
let 굽네치킨 = Restaurant(name: "굽네치킨", contact: "051-9196-0940", menus: ["고추바사삭":"20000원"])
let 천궁반점 = Restaurant(name: "천궁반점", contact: "051-2254-0940", menus: ["간짜장":"5000원"])
restaurantsData.addRestaurant("치킨", restaurant: 굽네치킨)
restaurantsData.addRestaurant("중식", restaurant: 천궁반점)
}
// MARK: - UITableViewDelegates
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if searchController.active && searchController.searchBar.text != "" {
return 0
}
return restaurantsData.restaurantTypes.count
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return restaurantsData.restaurantTypes[section]
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.active && searchController.searchBar.text != "" {
return filteredRestaurants.count
}
return restaurantsData.restaurants[section].count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let restaurant: Restaurant
print("\(searchController.active), \(self.searchController.searchBar.text != "")")
print(searchController.searchBar.text)
print(searchText)
print(2)
if searchController.active && searchText != "" {
restaurant = filteredRestaurants[indexPath.row]
print("searched")
} else {
restaurant = restaurantsData.restaurants[indexPath.section][indexPath.row]
}
cell.textLabel?.text = restaurant.name
return cell
}
// MARK: - UISearchBarDelegates
let searchController = UISearchController(searchResultsController: nil)
var searchText: String = ""
var filteredRestaurants = [Restaurant]() {
didSet { print(filteredRestaurants) }
}
func filterContentForSearchText(searchText: String, scope: String = "All") {
filteredRestaurants = restaurantsData.allRestaurants.filter { restaurant in
return restaurant.info.containsString(searchText.lowercaseString)
}
print("REloaded")
self.tableView.reloadData()
print(3)
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
searchText = searchController.searchBar.text!
print(1)
}
}
When I search, the filteredRestaurants array gots info about filtered research, however dose not present on search result table view because cellForRowAtIndexPath is not called.
You're returning 0 for number of sections when the searchController is active in this part of your code.
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
if searchController.active && searchController.searchBar.text != "" {
return 0
}
return restaurantsData.restaurantTypes.count
}
Zero sections is an empty table with no cells thats why cellForRowAtIndexPath is not being called.
Try replacing return 0 with return 1 so the table has a section to display your search results.
You have the following code in numberOfSectionsInTableView function:
searchController.active && searchController.searchBar.text != ""
It should be (== instead of !=):
searchController.active && searchController.searchBar.text == ""