searching in the textField of the tableView.
I need to search the names from the tableView. So for that i have tableView and textField in the UIViewController.
And i need to search from the Api. For that i have used the Alamofire method to fetch the data.
I need to search. Now i implement to display the names from the api to tableView And i got the output. But i need to implement searching .how to do in the mvvm.
my model:-
class SearchModel: NSObject {
var restaurantname :String!
init?(dictionary :JSONDictionary) {
guard let name = dictionary["name"] as? String else {
return
}
self.restaurantname = name
}
}
My viewmodel:-
class SearchViewModel: NSObject {
var datasourceModel:SearchDataSourceModel
init(withdatasource newDatasourceModel: SearchDataSourceModel) {
datasourceModel = newDatasourceModel
}
func datafordisplay(atindex indexPath: IndexPath) -> SearchModel{
return datasourceModel.dataListArray![indexPath.row]
}
func numberOfRowsInSection(section:Int) -> Int {
return (datasourceModel.dataListArray?.count)!
}
func loadData(completion :#escaping (_ isSucess:Bool) -> ()){
loadFromWebserviceData { (newDataSourceModel) in
if(newDataSourceModel != nil) {
self.datasourceModel = newDataSourceModel!
completion(true)
}
else {
completion(false)
}
}
}
//}
func loadFromWebserviceData(completion :#escaping (SearchDataSourceModel?) -> ()) {
//with using Alamofire ..............
Alamofire.request("http://www.example.com").validate(statusCode: 200..<300).validate(contentType: ["application/json"]).responseJSON{ response in
switch response.result {
case .success(let data):
print("success",data)
let result = response.result
if let wholedata = result.value as? [String:Any]{
if let data = wholedata["data"] as? Array<[String:Any]>{
// print(data["name"] as! String)
print(data)
print(response)
let newDataSource:SearchDataSourceModel = SearchDataSourceModel(array: data)
completion(newDataSource)
// }
}
}
// case .failure(let data):
// print("fail",data)
case .failure(let encodingError ):
print(encodingError)
// if response.response?.statusCode == 404 {
print(encodingError.localizedDescription)
completion(nil)
// }
}
}}
}
my DataSourcemodel:-
class SearchDataSourceModel: NSObject {
var dataListArray:Array<SearchModel>? = []
init(array :Array<[String:Any]>?) {
super.init()
var newArray:Array<[String:Any]> = []
if array == nil {
// newArray = self.getJsonDataStored44()
}
else {
newArray = array!
}
var datalist:Array<SearchModel> = []
for dict in newArray {
let model = SearchModel(dictionary: dict)
datalist.append(model!)
}
self.dataListArray = datalist
}
}
my viewController class:-
class SearchViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet private weak var tableView: UITableView!
private var searchViewModel :SearchViewModel!
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?, withViewModel viewModel:SearchViewModel) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
searchViewModel = viewModel
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
// tableView.dataSource = self
// filteredData = data
searchViewModel.loadData { (isSuccess) in
if(isSuccess == true) {
self.tableView .reloadData()
}
else {
}
}
// self.tableView .reloadData()
// Do any additional setup after loading the view.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchViewModel.numberOfRowsInSection(section: section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "searchcell"
var cell: SearchCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? SearchCell
if cell == nil {
tableView.register(UINib(nibName: "SearchCell", bundle: nil), forCellReuseIdentifier: identifier)
cell = tableView.dequeueReusableCell(withIdentifier: identifier) as? SearchCell
}
cell.setsearchData(search: searchViewModel.datafordisplay(atindex: indexPath))
// cell.name.text = searchViewModel.datafordisplay(atindex: indexPath)
// cell.name.text = filteredData[indexPath.row]
// cell.setsearchData(search: searchViewModel.datafordisplay(atindex: indexPath))
return cell
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
tableViewCell:-
class SearchCell: UITableViewCell {
#IBOutlet weak var name: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func setsearchData(search:SearchModel) {
self.name.text = search.restaurantname
}
}
This is my code .Now how to implement the searching here.
There are t way of searching.
when you enter text and press the search button.
When You type then search without using the search button.
A solution for 1:
You need to call a method on the search button
Search button.
func Search() {
predicate = NSPredicate(format: "Self.YourSearchListName beginsWith[c]%#", SearchName)
GetsearchList = YourArrayForSearch.filtered(using: predicate) as NSArray
YourTable.reloadData()
}
2nd Solution
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == self.YourTextFieldName{
Search()
}
}
for 2nd Solution don't forget to set the delegate.
filteredContactListArray is the array holding the filtered data to reload the tableview when user start searching for particular text.
#IBOutlet var searchCityTextField: UITextField!
var filteredSearchArray :SearchViewModel!
within in Viewdidload() function
searchCityTextField.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
UItextfieldDelegate Method
func textFieldDidChange(textField: UITextField) {
if textField.text == "" {
filteredSearchArray = searchViewModel// contactListArray is the actual array with all the list of data.
citiesTableView.reloadData()
}else{
filterContentForSearchText(textField.text!)
}
}
func filterContentForSearchText(searchText: String) {
filteredSearchArray = NSMutableArray(array:searchViewModel. dataListArray.filter({(ele:AnyObject) -> Bool in
return (ele as! searchViewModel).cityName.lowercased().contains(searchText.lowercased())
}))
citiesTableView.reloadData()
}
UItableviewDelegate Method
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredContactListArray.count
}
Related
I am using firebase when I follow user his post show up nicely in the UITableView . but when unfollow him , his post still in the UITableView. UITableView not reloading data after unfollow.
View Controller :
import UIKit
import SVProgressHUD
import SDWebImage
class HomeVC: UIViewController {
#IBOutlet weak var tableViewOutLet: UITableView!
#IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
var postContentArray = [Post]() // contine all Posts .
var userContentArray = [UserModel]()
override func viewDidLoad() {
super.viewDidLoad()
tableViewOutLet.dataSource = self
tableViewOutLet.estimatedRowHeight = 521
tableViewOutLet.rowHeight = UITableView.automaticDimension
loadPosts()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.tabBar.isHidden = false
}
#objc func loadPosts () {
// activityIndicatorView.startAnimating()
API.Feed.ObserveFeedWithMyPostAndFollowerPost(withid: API.User.Curren_User!.uid) { (postr) in
self.fetchUserInfo(PostUserID: postr.userPostId!, Completed: {
self.postContentArray.append(postr)
// self.activityIndicatorView.stopAnimating()
self.tableViewOutLet.reloadData()
})
}
API.Feed.ObserveFeedReomved(withid: API.User.Curren_User!.uid) { (key) in
// print(key)
self.postContentArray = self.postContentArray.filter {$0.userPostId != key }
self.tableViewOutLet.reloadData()
}
}
#objc func fetchUserInfo (PostUserID : String , Completed : #escaping ()-> Void) {
API.User.observeUserInformation(CommentUserID: PostUserID) { (User) in
self.userContentArray.append(User)
Completed()
}
}
#IBAction func logOutButton(_ sender: Any) {
AuthServices.logout(OnSuccess: {
SVProgressHUD.showSuccess(withStatus: "تم تسجيل الخروج")
let storyBoard = UIStoryboard(name: "Start", bundle: nil)
let signInvc = storyBoard.instantiateViewController(withIdentifier: "SignInVC")
self.present(signInvc, animated: true, completion: nil)
}) { (error) in
SVProgressHUD.showError(withStatus: error)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "commentIconSequeToCommentPage" {
let commentvc = segue.destination as! CommentsVC
commentvc.postID = sender as? String
}
}
}
extension HomeVC: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return postContentArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as! HomeTableViewCell
let postContent = postContentArray[indexPath.row]
let userContent = userContentArray[indexPath.row]
cell.postContent = postContent
cell.userContentInfo = userContent
cell.homeView = self
return cell
}
}
Observe Functions :
import Foundation
import FirebaseDatabase
class FeedAPI {
var REF_FEED = Database.database().reference().child("Feed")
func ObserveFeedWithMyPostAndFollowerPost (withid id : String , complation : #escaping (Post)->Void) {
REF_FEED.child(id).observe(DataEventType.childAdded) { (snapshto : DataSnapshot) in
let key = snapshto.key
API.Post.observePostFuntion(withID: key, Complation: { (post) in
complation(post)
})
}
}
func ObserveFeedReomved (withid id : String , complation : #escaping (String)->Void) {
REF_FEED.child(id).observe(DataEventType.childRemoved) { (snapshot : DataSnapshot) in
let key = snapshot.key
complation(key)
}
}
}
where exactly is the unfollow function?
Also you should set the tableview delegate
override func viewDidLoad() {
tableViewOutLet.delegate = self
}
Please show where exactly are you preforming the unfollow function.
my follow And unfollow func
class FollowAPI {
var REF_FOLLOWERS = Database.database().reference().child("Follower")
var REF_FOLLOWING = Database.database().reference().child("Following")
func followAction (withId id : String) {
API.Follow.REF_FOLLOWERS.child(id).child(API.User.Curren_User!.uid).setValue(true)
API.Follow.REF_FOLLOWING.child(API.User.Curren_User!.uid).child(id).setValue(true)
API.myPOSTS.REF_MYPOSTS.child(id).observeSingleEvent(of: DataEventType.value) { (snapshot : DataSnapshot) in
print(snapshot.key)
if let dic = snapshot.value as? [String : Any]{
for key in dic.keys {
API.Feed.REF_FEED.child(API.User.Curren_User!.uid).child(key).setValue(true)
}
}
}
}
func UnFollowAction (withid id : String) {
API.Follow.REF_FOLLOWERS.child(id).child(API.User.Curren_User!.uid).setValue(NSNull())
API.Follow.REF_FOLLOWING.child(API.User.Curren_User!.uid).child(id).setValue(NSNull())
API.myPOSTS.REF_MYPOSTS.child(id).observeSingleEvent(of: DataEventType.value) { (snapshot : DataSnapshot) in
print(snapshot.key)
if let dic = snapshot.value as? [String : Any]{
for key in dic.keys {
API.Feed.REF_FEED.child(API.User.Curren_User!.uid).child(key).removeValue()
}
}
}
}
func isFollwoing (userid : String , Complated : #escaping (Bool)->Void) {
REF_FOLLOWERS.child(userid).child(API.User.Curren_User!.uid).observeSingleEvent(of: .value) { (snapshot ) in
if let _ = snapshot.value as? NSNull {
Complated(false)
} else {
Complated(true)
}
}
}
}
I need to search in the tableview.I have tableview and textfield.While clicking on the textfield it must search the tableview.
my model:-
class SearchModel: NSObject {
var restaurantname :String!
init?(dictionary :JSONDictionary) {
guard let name = dictionary["name"] as? String else {
return
}
self.restaurantname = name
}
}
my viewmodel:-
class SearchViewModel: NSObject {
var datasourceModel:SearchDataSourceModel
init(withdatasource newDatasourceModel: SearchDataSourceModel) {
datasourceModel = newDatasourceModel
}
func datafordisplay(atindex indexPath: IndexPath) -> SearchModel{
return datasourceModel.dataListArray![indexPath.row]
}
func numberOfRowsInSection(section:Int) -> Int {
return (datasourceModel.dataListArray?.count)!
}
func loadData(completion :#escaping (_ isSucess:Bool) -> ()){
loadFromWebserviceData { (newDataSourceModel) in
if(newDataSourceModel != nil)
{
self.datasourceModel = newDataSourceModel!
completion(true)
}
else{
completion(false)
}
}
}
//}
func loadFromWebserviceData(completion :#escaping (SearchDataSourceModel?) -> ()){
//with using Alamofire ..............
Alamofire.request("http://www.example.com").validate(statusCode: 200..<300).validate(contentType: ["application/json"]).responseJSON{ response in
switch response.result{
case .success(let data):
print("success",data)
let result = response.result
if let wholedata = result.value as? [String:Any]{
if let data = wholedata["data"] as? Array<[String:Any]>{
// print(data["name"] as! String)
print(data)
print(response)
let newDataSource:SearchDataSourceModel = SearchDataSourceModel(array: data)
completion(newDataSource)
// }
}
}
// case .failure(let data):
// print("fail",data)
case .failure(let encodingError ):
print(encodingError)
// if response.response?.statusCode == 404{
print(encodingError.localizedDescription)
completion(nil)
// }
}
}}
}
my datasource model:-
class SearchDataSourceModel: NSObject {
var dataListArray:Array<SearchModel>? = []
init(array :Array<[String:Any]>?) {
super.init()
var newArray:Array<[String:Any]> = []
if array == nil{
// newArray = self.getJsonDataStored44()
}
else{
newArray = array!
}
var datalist:Array<SearchModel> = []
for dict in newArray{
let model = SearchModel(dictionary: dict)
datalist.append(model!)
}
self.dataListArray = datalist
}
}
my viewcontroller:-
class SearchViewController: UIViewController,UITableViewDelegate,UITableViewDataSource, UISearchBarDelegate,UITextFieldDelegate {
#IBOutlet private weak var tableView: UITableView!
!
#IBOutlet weak var txt: UITextField!
var filteredSearchArray = NSMutableArray()
private var searchViewModel :SearchViewModel!
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?, withViewModel viewModel:SearchViewModel) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
searchViewModel = viewModel
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var filteredData: [String]!
override func viewDidLoad() {
super.viewDidLoad()
txt.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
searchViewModel.loadData { (isSuccess) in
if(isSuccess == true)
{
self.tableView .reloadData()
}
else{
}
}
}
#objc private func textFieldDidChange(textField: UITextField) {
if textField.text == "" {
self .viewDidLoad()
}else{
filterContentForSearchText(searchText: textField.text!)
}
}
func filterContentForSearchText(searchText: String) {
filteredSearchArray = NSMutableArray(array:(searchViewModel.datasourceModel.dataListArray?.filter({(ele:AnyObject) -> Bool in
return (ele as! SearchModel).restaurantname.lowercased().contains(searchText.lowercased())
}))!)
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchViewModel.numberOfRowsInSection(section: section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "searchcell"
var cell: SearchCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? SearchCell
if cell == nil {
tableView.register(UINib(nibName: "SearchCell", bundle: nil), forCellReuseIdentifier: identifier)
cell = tableView.dequeueReusableCell(withIdentifier: identifier) as?SearchCell
}
cell.setsearchData(search: searchViewModel.datafordisplay(atindex: indexPath))
return cell
}
}
my tableviewcell:-
class SearchCell: UITableViewCell {
#IBOutlet weak var name: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func setsearchData(search:SearchModel)
{
self.name.text = search.restaurantname
}
}
This is my code but in the filteredSearchArray always shows as 0 element.
Here the list of names is displaying but clicking on the textfield it doesn't searching and not reloading. How to solve the problem.
The data is not coming in this code:-
func filterContentForSearchText(searchText: String) {
filteredSearchArray = NSMutableArray(array:(searchViewModel.datasourceModel.dataListArray?.filter({(ele:AnyObject) -> Bool in
return (ele as! SearchModel).restaurantname.lowercased().contains(searchText.lowercased())
}))!)
tableView.reloadData()
}
First of all one important thing don't use NSArray, NSDictionary in swift use Array, Dictionary instead.
I have made a change in your model class:
class SearchModel: NSObject {
var restaurantname :String!
init(dictionary :JSONDictionary) {
self.restaurantname = dictionary["name"] as? String ?? ""
}
}
Now filter your array like this:
filteredSearchArray = searchViewModel.datasourceModel.dataListArray?.filter {$0.restaurantname.lowercased().contains(searchText.lowercased())}
And now pass filteredSearchArray in tableView datasource then reload the table. You can take a bool variable isSearching to check which array you have to pass in datasource the filtered one the original. Toggle this isSearching in textFieldDidBeginEiditng and textFieldDidEndEiditng method.
I have tested it by below example in my playground:
var models: Array<SearchModel> = [SearchModel.init(dictionary: ["name": "Khalsa Dhaba"]), SearchModel.init(dictionary: ["name": "Yo! China"]), SearchModel.init(dictionary: ["name": "Sher-e-punjab"]), SearchModel.init(dictionary: ["name": "Haveli"])]
let result = models.filter {$0.restaurantname.lowercased().contains("o!".lowercased())}
print(result)
It works fine!
Secondly I noticed you're reloading your view by calling self.viewDidLoad() method which you should not call ever this calls itself and it is a function of viewContoller lifecycle. Call tableView.reloadData() instead.
I am doing in mvvm method.my code as below.
foodModel :-
class foodModel: NSObject {
var name :String!
var city :String!
var imageurl :String!
init?(dictionary :JSONDictionary) {
guard let name = dictionary["name"] as? String,
let imageurl = dictionary["imageurl"] as? String,
let city = dictionary["city"] as? String else {
return
}
self.name = name
self.city = city
self.imageurl = imageurl
}
}
foodDataSourceModel:-
class foodDataSourceModel: NSObject {
var dataListArray:Array<foodModel>? = []
init(array :Array<[String:Any]>?) {
super.init()
var newArray:Array<[String:Any]> = []
if array == nil{
newArray = self.getJsonDataStored1()
}
else{
newArray = array!
}
var datalist:Array<foodModel> = []
for dict in newArray{
let model = foodModel(dictionary: dict)
datalist.append(model!)
}
self.dataListArray = datalist
}
}
typealias dummyDataSource1 = foodDataSourceModel
extension dummyDataSource1{
func getJsonDataStored1() ->Array<Dictionary<String,String>>{
let jsonArray = [["name":"Anjapar","imageurl":"","city":"india"],["name":"Anjapar","imageurl":"","city":"india"]] as Array<Dictionary<String,String>>
return jsonArray
}
}
foodViewModel:-
class foodViewModel: NSObject {
var datasourceModel:foodDataSourceModel
init(withdatasource newDatasourceModel:foodDataSourceModel) {
datasourceModel = newDatasourceModel
}
func datafordisplay(atindex index: Int) -> foodModel {
return datasourceModel.dataListArray![index]
}
func numberOfItemsInSection(section:Int) -> Int {
return (datasourceModel.dataListArray?.count)!
}
func loadData(){
loadFromWebserviceData { (newDataSourceModel) in
self.datasourceModel = newDataSourceModel!
}
}
func loadFromWebserviceData(completion :#escaping (foodDataSourceModel?) -> ()){
let url = URL(string :"http://example.com")!
URLSession.shared.dataTask(with: url) { data, _, _ in
if let data = data {
let json = try! JSONSerialization.jsonObject(with: data, options: [])
//let dictionaries = json as! [JSONDictionary]
let array = json as! Array<[String:Any]>
let newDataSource:foodDataSourceModel = foodDataSourceModel(array: array)
// DispatchQueue.main.async {
//
// }
completion(newDataSource)
}
}.resume()
}
}
foodViewController:-
#IBOutlet private weak var tableView: UITableView!
private var foodViewModel :foodViewModel!
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?, withViewModel viewModel:foodViewModel) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
foodViewModel = viewModel
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
// tableView.register(UINib(nibName: "foodCell", bundle: nil), forCellReuseIdentifier: "cell")
foodViewModel.loadData()
self.tableView .reloadData()
// Do any additional setup after loading the view.
}
// func numberOfSections(in tableView: UITableView) -> Int {
// return restaurtantViewModel.numberOfItemsInSection(section: Int)
// }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "cell")! as! foodCell
//cell.setRestaurtantData(restaurtant: foodModel)
cell.textLabel?.text = "rtyuiop"
//
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
}
*/
}
foodCell:-
class foodCell: UITableViewCell {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var city: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func setRestaurtantData(restaurtant:foodModel)
{
self.name.text = restaurtant.name
self.city.text = restaurtant.city
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Here output is not getting .I need to display the values in tableview.Showing an error as failed to obtain a cell from dataSource
{
"query": "sandwich",
"results_count": 12,
"page": 1,
"results": [
{
"name": "Caffè Macs",
"coordinates": {
"lat": 37.330576,
"lng": -122.029739
},
"meals": ["breakfast", "lunch", "dinner"]
},
...
]
}
This is the pattern ,so from this i need to get the values as "name","lng" and "lat".
There is some problem in load the data from Json .In the urlsession task -showing as variable not available. please check .How to solve this?
Please update your viewDidLoad(). You must set delegate and datasource of tableview.
First set in class:-
class yourClass: UIViewController,UITableViewDelegate , UITableViewDataSource
Now
override func viewDidLoad() {
super.viewDidLoad()
// tableView.register(UINib(nibName: "foodCell", bundle: nil), forCellReuseIdentifier: "cell")
foodViewModel.loadData()
self.tableView .delegate = self
self.tableview.dataSource = self
self.tableView .reloadData()
When you fetch data from a web service, this is usually done in a asynchronous manner. In your viewDidLoad method, you're updating your table view immediately after the call which starts fetching the data.
override func viewDidLoad() {
...
// Start fetching the data
foodViewModel.loadData()
// Reloading the table view even though the data might not be ready
self.tableView.reloadData()
...
}
You can fix this by passing a callback to your loadData function in your foodViewModel:
func loadData(completion: (() -> ()) {
loadFromWebserviceData { (newDataSourceModel) in
self.datasourceModel = newDataSourceModel!
completion()
}
}
And in your foodViewController, you would then wait to update your table view until the data source is ready to be displayed:
override func viewDidLoad() {
...
foodViewModel.loadData {
self.tableView.reloadData()
}
...
}
While the code above should work, this is just to highlight the problem you're currently facing and therefore, was kept as simple as possible.
I have a class that handles the user looking for another user via the username. The looking part is done through SearchBar control. Backend is Firebase.
Here is the full code I have:
class AddFriendByUsernameTableViewController: UITableViewController, UISearchBarDelegate, UISearchDisplayDelegate {
/**************************Global Variables************************/
var friendObject = FriendClass()
var friendsArray = [FriendClass]()
var friendsUsernames = [String]()
var isFirstLoading: Bool = true
var utiltiies = Utilities()
var searchActive : Bool = false
var usernames:[String]!
/**************************UI Components************************/
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
self.searchBar.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func search(searchText: String? = nil){
/****************************Get Username by Auth Data****************************/
if(searchText != nil)
{
self.getAllUsersForSearchFilter({(result) -> Void in
if(result.domain == "")
{
let containsResult = self.usernames.contains(searchText!)
if(containsResult == true)
{
/*Query All information for found username*/
let reference = Firebase(url:"https://something.firebaseio.com/users/")
/****************************Get Username by Auth Data****************************/
reference.queryEqualToValue(searchText!).observeEventType(.Value, withBlock: { (snapshot: FDataSnapshot!) -> Void in
for userInstance in snapshot.children.allObjects as! [FDataSnapshot]{
}
})
}else{
print("No matching username to search Text")
}
}
})
}
}
func getAllUsersForSearchFilter(completion: (result: NSError) -> Void)
{
let errorFound:NSError = NSError(domain: "", code: 0, userInfo: nil)
let reference = Firebase(url:"https://something.firebaseio.com/users/")
reference.observeEventType(.Value, withBlock: { (snapshot: FDataSnapshot!) -> Void in
if(snapshot != nil )
{
for userInstance in snapshot.children.allObjects as! [FDataSnapshot]{
//username = (userInstance.value["username"] as? String)!
//self.usernames.append(userInstance.value["username"] as! String)
}
completion(result: errorFound)
}else{
completion(result: errorFound)
}
})
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if(usernames != nil)
{
return usernames.count
}
return 0
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as UITableViewCell
//self.tempObjectHolder = self.data[indexPath.row]
//cell.textLabel!.text = self.tempObjectHolder["appUsername"] as? String
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPathT = tableView.indexPathForSelectedRow;
let currentCell = tableView.cellForRowAtIndexPath(indexPathT!)
/*Get username selected from the search results. */
self.friendObject.username = currentCell?.textLabel!.text
/*self.friendObject.name = self.tempObjectHolder["name"] as? String
self.friendObject.mobile = self.tempObjectHolder["mobile"] as? String
self.friendObject.telephone = self.tempObjectHolder["telephone"] as? String
self.friendObject.username = self.tempObjectHolder["appUsername"] as? String
self.friendObject.email = self.tempObjectHolder["email"] as? String
self.friendObject.workAddressString = self.tempObjectHolder["workAddress"] as! String
self.friendObject.homeAddressString = self.tempObjectHolder["homeAddress"] as! String*/
self.performSegueWithIdentifier("viewUserResultsSegue", sender: self)
}
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
searchActive = true;
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
searchActive = false;
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
searchActive = false;
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
search()
searchActive = false;
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
search(searchText)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "viewUserResultsSegue") {
// pass data to next view
//let destinationVC = segue.destinationViewController as! ViewResultUserProfileViewController
//destinationVC.friendObject = self.friendObject;
}
}
}
When I start typing in the the search bar, it executes and triggers an erro:
fatal error: unexpectedly found nil while unwrapping an Optional value
and this is happening because it took the firt character I entered and searched for it. I want it to search for full work (username) not first letters.
Thanks,
To do some action when the keyboard Enter is tapped, you can create an IBAction method in your view controller, such as:
#IBAction func enterDetected(sender: UITextField) {
print("Saw an 'Enter'")
}
Then connect the UITextField's "Primary Action Triggered" connection to the method in Interface Builder.
I am using this tutorial to auto-complete the google places.
I have installed the alamofire and swiftyJSON through cocoapads.
My issue is When I enter text in searchbar, nothing happens.
On my ViewControlller:
let gpaViewController = GooglePlacesAutocomplete(
apiKey: "MY-API-KEY",
placeType: .Address
)
gpaViewController.placeDelegate = self
presentViewController(gpaViewController, animated: true, completion: nil)
On my GooglePlacesAutoComplete View Controller:
import UIKit
import Alamofire
import SwiftyJSON
enum PlaceType: CustomStringConvertible {
case All
case Geocode
case Address
case Establishment
case Regions
case Cities
var description : String {
switch self {
case .All: return ""
case .Geocode: return "geocode"
case .Address: return "address"
case .Establishment: return "establishment"
case .Regions: return "regions"
case .Cities: return "cities"
}
}
}
struct Place {
let id: String
let description: String
}
protocol GooglePlacesAutocompleteDelegate {
func placeSelected(place: Place)
func placeViewClosed()
}
Only this set of codes is called:
class GooglePlacesAutocomplete: UINavigationController {
var gpaViewController: GooglePlacesAutocompleteContainer?
var placeDelegate: GooglePlacesAutocompleteDelegate? {
get { return gpaViewController?.delegate }
set { gpaViewController?.delegate = newValue }
}
convenience init(apiKey: String, placeType: PlaceType = .All) {
let gpaViewController = GooglePlacesAutocompleteContainer(
apiKey: apiKey,
placeType: placeType
)
self.init(rootViewController: gpaViewController)
self.gpaViewController = gpaViewController
let closeButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Stop, target: self, action: "close")
gpaViewController.navigationItem.leftBarButtonItem = closeButton
gpaViewController.navigationItem.title = "Enter Address"
}
func close() {
placeDelegate?.placeViewClosed()
}
}
No indication of this:
class GooglePlaceSearchDisplayController: UISearchDisplayController {
override func setActive(visible: Bool, animated: Bool) {
if active == visible { return }
searchContentsController.navigationController?.navigationBarHidden = true
super.setActive(visible, animated: animated)
searchContentsController.navigationController?.navigationBarHidden = false
if visible {
searchBar.becomeFirstResponder()
} else {
searchBar.resignFirstResponder()
}
}
}
// MARK: - GooglePlacesAutocompleteContainer
class GooglePlacesAutocompleteContainer: UIViewController {
var delegate: GooglePlacesAutocompleteDelegate?
var apiKey: String?
var places = [Place]()
var placeType: PlaceType = .All
convenience init(apiKey: String, placeType: PlaceType = .All) {
self.init(nibName: "GooglePlacesAutocomplete", bundle: nil)
self.apiKey = apiKey
self.placeType = placeType
}
override func viewDidLoad() {
super.viewDidLoad()
let tv: UITableView? = searchDisplayController?.searchResultsTableView
tv?.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
}
}
Or this:
// MARK: - GooglePlacesAutocompleteContainer (UITableViewDataSource / UITableViewDelegate)
extension GooglePlacesAutocompleteContainer: UITableViewDataSource, UITableViewDelegate {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return places.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.searchDisplayController?.searchResultsTableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell?
// Get the corresponding candy from our candies array
let place = self.places[indexPath.row]
// Configure the cell
cell!.textLabel!.text = place.description
cell!.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
return cell!
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
delegate?.placeSelected(self.places[indexPath.row])
}
}
// MARK: - GooglePlacesAutocompleteContainer (UISearchDisplayDelegate)
extension GooglePlacesAutocompleteContainer: UISearchDisplayDelegate {
func searchDisplayController(controller: UISearchDisplayController, shouldReloadTableForSearchString searchString: String!) -> Bool {
getPlaces(searchString)
return false
}
private func getPlaces(searchString: String) {
let url = "https://maps.googleapis.com/maps/api/place/autocomplete/json"
Alamofire.request(.GET, url).responseJSON { response in
switch response.result {
case .Success(let data):
let json = JSON(data)
let name = json["name"].stringValue
print(name)
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
}
}
Apologies for long code. Am not sure, where I made a mistake.
I also set the delegate for searchBar in xib
I double checked the installation of Cocoapads, Alamofire and SwiftyJson. Can someone please help me? I'm new to these things..
You Have To Make Object Of UISearchBar Like This...
lazy var searchBar : UISearchBar = UISearchBar()
In Did Load
searchBar.delegate = self