Trouble With SearchBar and Search Bar Controller Due to Depreciation of SearchDisplayController - json

I would like some help with the search bar functionality. I am stuck and not sure where to take it from here. I am trying to update the tableview when search text word is contained in a recipe title. I am not sure how to do this because of the depreciated searchDisplay controller. Help would be appreciated.
import UIKit
import SwiftyJSON
class Downloader {
class func downloadImageWithURL(_ url:String) -> UIImage! {
let data = try? Data(contentsOf: URL(string: url)!)
return UIImage(data: data!)
}
}
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate,UISearchDisplayDelegate{
#IBOutlet weak var recipeTable: UITableView!
// search functionality Need help with my search functionality
var filteredRecipes = [Recipe]()
func filterContentForSearch(searchText:String) {
// need help here
self.filteredRecipes = self.recipes.filter({(title:Recipe) -> Bool in
return (title.title!.lowercased().range(of: searchText.lowercased()) != nil)
})
}
private func searchDisplayController(controller: UISearchController!, shouldReloadTableForSearchString searchString: String!) -> Bool {
self.filterContentForSearch(searchText: searchString)
return true
}
//end search parameters
// tableview functionionalitys
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if tableView == searchDisplayController!.searchResultsTableView {
return filteredRecipes.count
}else{
return recipes.count
}
// recipeTable.reloadData()
}
// tableview functionalities
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! RecipeTableViewCell
if tableView == self.searchDisplayController!.searchResultsTableView{
//get images from download
DispatchQueue.main.async { () ->Void in
cell.imageLabel.image = Downloader.downloadImageWithURL(self.filteredRecipes[indexPath.row].imageUrl)
}
cell.recipeLabel.text = self.filteredRecipes[indexPath.row].title
recipeTable.reloadData()
}else{
//get image from download
DispatchQueue.main.async { () ->Void in
cell.imageLabel.image = Downloader.downloadImageWithURL(self.recipes[indexPath.row].imageUrl)
}
cell.recipeLabel.text = recipes[indexPath.row].title
}
//recipeTable.reloadData()
return cell
}
// structs for json
struct Root : Decodable {
let count : Int
let recipes : [Recipe]
}
struct Recipe : Decodable { // It's highly recommended to declare Recipe in singular form
let recipeId : String
let imageUrl, sourceUrl, f2fUrl : String
let title : String?
let publisher : String
let socialRank : Double
let page : Int?
let ingredients : [String]?
}
//recipes is array of Recipes
var recipes = [Recipe]() // array of recipes
//unfiltered recipes to put into search
fileprivate func getRecipes() {
let jsonURL = "https://www.food2fork.com/api/search?key=264045e3ff7b84ee346eb20e1642d9d9"
//.data(using: .utf8)!
guard let url = URL(string: jsonURL) else{return}
URLSession.shared.dataTask(with: url) {(data, response , err) in
if let response = response as? HTTPURLResponse, response.statusCode != 200 {
print(response.statusCode)
return
}
DispatchQueue.main.async {
if let err = err{
print("failed to get data from URL",err)
return
}
guard let data = data else{return}
//print(String(data: data, encoding: .utf8))
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Root.self, from: data)
self.recipes = result.recipes
//print(result.recipes)
self.recipeTable.reloadData()
}catch let jsonERR {
print("Failed to decode",jsonERR)
}
}
}.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
//recipeTable.reloadData()
//search bar
//filteredRecipes = recipes
//call json object
getRecipes()
}
}

You could take this approach:
Add a Boolean variable to indicate whether searching or not
var searching: Bool = false
Use this for numberOfRowsInSection for the tableview
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searching {
return filteredRecipes.count
} else {
return recipes.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! RecipeTableViewCell
var recipe: Recipe
if searching {
recipe = filteredRecipes[indexPath.row]
} else {
recipe = recipes[indexPath.row]
}
DispatchQueue.main.async { () ->Void in
cell.imageLabel.image = Downloader.downloadImageWithURL(recipe.imageUrl)
}
cell.recipeLabel.text = recipe.title
return cell
}
And add this for the searchBar (set searching for other funcs like searchBarCancelButtonClicked)
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
searching = false
filteredRecipes.removeAll()
view.endEditing(true)
} else {
searching = true
filteredRecipes = recipes.filter{$0.title.contains(searchBar.text!)}
}
tableView.reloadData()
}

Related

JSON Parsing swift, Always catch statement executing

I have UITableViewController, i'm trying to parse data from url,
Always catch statement executing, that prints "something" in the console.
in Storyboard i added reuse identifier to the table view cell.
'''
class TableViewController: UITableViewController {
final let url = URL(string: "http://jsonplaceholder.typicode.com/posts")
private var posts = [Post]()
override func viewDidLoad() {
super.viewDidLoad()
downloadJson()
}
func downloadJson() {
guard let downloadURL = url else { return }
URLSession.shared.dataTask(with: downloadURL) { (data, response, error) in
guard let data = data, error == nil, response != nil else {
return
}
do {
let decoder = JSONDecoder()
let tempPosts = try decoder.decode(Posts.self, from: data)
print(tempPosts)
self.posts = tempPosts.posts
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("something")
}
}.resume()
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = posts[indexPath.row].title
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
'''
'''
class Posts: Codable {
let posts: [Post]
init(posts: [Post]) {
self.posts = posts
}
}
class Post: Codable {
let userId: Int
let id: Int
let title: String
let body: String
init(userId: Int, id: Int, title: String, body: String) {
self.userId = userId
self.id = id
self.title = title
self.body = body
}
}
'''
If the Post model is,
struct Post: Codable {
let userId, id: Int
let title, body: String
}
Use [Posts].self instead of Posts.self while parsing the data.
let tempPosts = try decoder.decode([Post].self, from: data)

How to show Json data in Offline using core data

Here i have created table view with labelName and labelUsername,
and i have downloaded Json data and saving it in core data entity called Details which contains attributes name and username..
here table view showing its data in online...
but how can i show fetched data in table view while in offline..
please help me in the code...
import UIKit
import CoreData
struct JsonData {
var nameS: String
var usernameS: String
init(name: String, username: String) {
self.nameS = name
self.usernameS = username
}
}
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var iteamsArray = [JsonData]()
override func viewDidLoad() {
downloadJson()
}
func downloadJson(){
let urlStr = "https://jsonplaceholder.typicode.com/users"
let url = URL(string: urlStr)
URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in
guard let respData = data else {
return
}
guard error == nil else {
print("error")
return
}
do{
let jsonObj = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [[String: Any]]
for items in jsonObj {
let nameJson = items["name"] as? String
let usernameJson = items["username"] as? String
let coreData = NSEntityDescription.insertNewObject(forEntityName: "Details", into: self.context) as! Details
coreData.name = nameJson
coreData.username = usernameJson
self.iteamsArray.append(JsonData(name: nameJson!, username: usernameJson!))
}
try self.context.save()
//fetching from core data
let fetchRequest : NSFetchRequest<Details> = Details.fetchRequest()
let details = try self.context.fetch(fetchRequest)
if details.count > 0 {
for detail in details as [NSManagedObject] {
let nameCore = detail.value(forKey: "name")
let usernameCore = detail.value(forKey: "username")
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch {
print("catch error")
}
}).resume()
}
}
// MARK: - TableView
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return iteamsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "JsonCell", for: indexPath) as! JsonTableViewCell
let aData = iteamsArray[indexPath.row]
cell.labelName.text = aData.nameS
cell.labelUsername.text = aData.usernameS
cell.selectionStyle = .none
return cell
}
}
First of all forget the custom struct. Use the NSManagedObject class as data source array.
var iteamsArray = [Details]()
In viewDidLoad first fetch the data, if the array is empty load it from the web service
override func viewDidLoad() {
let fetchRequest : NSFetchRequest<Details> = Details.fetchRequest()
do {
iteamsArray = try self.context.fetch(fetchRequest)
if iteamsArray.isEmpty {
downloadJson()
} else {
self.tableView.reloadData()
}
} catch { print(error) }
}
In downloadJson() replace
self.iteamsArray.append(JsonData(name: nameJson!, username: usernameJson!))
with
self.iteamsArray.append(coreData)
and remove these lines
//fetching from core data
let fetchRequest : NSFetchRequest<Details> = Details.fetchRequest()
let details = try self.context.fetch(fetchRequest)
if details.count > 0 {
for detail in details as [NSManagedObject] {
let nameCore = detail.value(forKey: "name")
let usernameCore = detail.value(forKey: "username")
}
}
In cellForRow get the values directly from the NSManagedObject objects
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "JsonCell", for: indexPath) as! JsonTableViewCell
let aData = iteamsArray[indexPath.row]
cell.labelName.text = aData.name
cell.labelUsername.text = aData.userName
cell.selectionStyle = .none
return cell
}

data is not filtered by the searchbar

I am trying to filter my fetched JSON data using a searchbar. However, when I type something into the searchbar it does nothing. Data is still in the same place and it is not filtered, whereas it should be dynamically filtered while I am typing something into the searchbar.
The code below shows my TableViewController as well as the function for fetching JSON data into my array. It is then filtered using a searchbar and whenever the data's name is matching the condition in the search bar it is then added to the second array called 'filteredExercise'.
import UIKit
class ExerciseTableViewController: UITableViewController, UISearchBarDelegate {
var fetchedExercise = [Exercise]()
var filteredExercise = [Exercise]()
var inSearchMode = false
#IBOutlet var searchBar: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
parseData()
}
func parseData() {
fetchedExercise.removeAll()
let urlPath = "https://wger.de/api/v2/exercise/?format=json&language=2&status=2&limit=200"
let url = URL(string: urlPath)!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Error while parsing JSON")
}
else {
do {
if let data = data,
let fetchedData = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as? [String:Any],
let exercises = fetchedData["results"] as? [[String: Any]] {
for eachExercise in exercises {
if eachExercise["license_author"] as! String == "wger.de" {
let name = eachExercise["name"] as! String
let description = eachExercise["description"] as! String
let id = eachExercise["id"] as! Int
self.fetchedExercise.append(Exercise(name: name, description: description, id: id))
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
catch {
print("Error while parsing data.")
}
}
}
task.resume()
}
// MARK: - Table view data source
override func numberOfSections(in 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 inSearchMode {
return filteredExercise.count
}
return fetchedExercise.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "ExerciseCell", for: indexPath) as? ExerciseCell {
let exercise: Exercise!
if inSearchMode {
exercise = filteredExercise[indexPath.row]
cell.configureCell(exercise: exercise)
} else {
exercise = fetchedExercise[indexPath.row]
cell.configureCell(exercise: exercise)
}
return cell
} else {
return UITableViewCell()
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var exercise: Exercise!
exercise = fetchedExercise[indexPath.row]
performSegue(withIdentifier: "exerciseDetailVC", sender: exercise)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
inSearchMode = false
self.tableView.reloadData()
} else {
inSearchMode = true
let lower = searchBar.text!.lowercased()
filteredExercise = fetchedExercise.filter({$0.name.range(of: lower) != nil})
self.tableView.reloadData()
}
}
}
Looks like you have an error in?
#IBOutlet var searchBar: UITableView!
I think it should be type of UISearchBarController.
Okay, I have finally figured out what is wrong with it.
Basically first of all I had incorrect type for my searchBar due to the Xcode bug and I did not see that.
Then I had to connect my IBOutlet to the storyboard as well because it was not done.
Finally I started getting wrong results while filtering through the data and it was because I have been filtering through results using a lowercased() function, whereas all my data is capitalized.

UITableView not loading data with SwiftyJSON and Alamofire

I am trying to parse a json and load it in the CustomTable, my json parsed successful but could not load the data in the UI, i tried adding test datas, and it displayed. So i guess it has to do with scoping. I have tried using other answers on the same question, but still it did not for work me
import UIKit
import Alamofire
import SwiftyJSON
class NewsViewController: UIViewController,
UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var newsPosts = [NewsPost]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
getNewsPost()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let post = newsPosts[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "NewsCell") as? NewsPostCell{
cell.configureCell(post: post)
return cell
}else{
let cell = NewsPostCell()
cell.configureCell(post: post)
return cell
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return newsPosts.count
}
func getNewsPost(){
Alamofire.request(URL_BASE + NEWS_URL).responseJSON { response in
let json = JSON(data: response.data!)
if let dict = json.dictionaryObject{
if let result = dict["data"] as? [NSDictionary]{
for data in result{
guard let title = data["title"] as? String else{
return print("title is nil")
}
guard let href = data["href"] as? String else{
return print("href is nil")
}
guard let image = data["image"] as? String else{
return print("image is nil")
}
guard let content = data["content"] as? String else{
return print("content is nil")
}
guard let _ = data["timestamp"] as? String else{
return print("timestamp is nil")
}
guard let type = data["type"] as? String else{
return print("type is nil")
}
print(type)
let post = NewsPost(title: title, href: href,
image: image, timestamp:
"1 day ago", content: content, type: type)
self.newsPosts.append(post)
}
self.tableView.reloadData()
}
}
}
}
}
You should test if your tableview is initialized when before you reload the data :
import UIKit
import Alamofire
import SwiftyJSON
class NewsViewController: UIViewController,
UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var newsPosts = [NewsPost]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
getNewsPost()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let post = newsPosts[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "NewsCell") as? NewsPostCell{
cell.configureCell(post: post)
return cell
}else{
let cell = NewsPostCell()
cell.configureCell(post: post)
return cell
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return newsPosts.count
}
func getNewsPost(){
Alamofire.request(URL_BASE + NEWS_URL).responseJSON { response in
let json = JSON(data: response.data!)
if let dict = json.dictionaryObject{
if let result = dict["data"] as? [NSDictionary]{
for data in result{
guard let title = data["title"] as? String else{
return print("title is nil")
}
guard let href = data["href"] as? String else{
return print("href is nil")
}
guard let image = data["image"] as? String else{
return print("image is nil")
}
guard let content = data["content"] as? String else{
return print("content is nil")
}
guard let _ = data["timestamp"] as? String else{
return print("timestamp is nil")
}
guard let type = data["type"] as? String else{
return print("type is nil")
}
print(type)
let post = NewsPost(title: title, href: href,
image: image, timestamp:
"1 day ago", content: content, type: type)
self.newsPosts.append(post)
}
if self.tableView != nil {
self.tableView.reloadData()
}
}
}
}
}
}
I guess the problem is when you're calling the gettingNewPost function. The reloadData() function is called right after didLoad, maybe you havent even get the data when you call the function to populate the table. Have you tried to log in the cellForRow function to see if isnt trying to access a nil array?
I finally found the issue, one of the guard is return nil, so it stop the execution of others, so i changed from using guard to the below
let title = data["title"] as? String ?? ""
let href = data["href"] as? String ?? ""
let image = data["image"] as? String ?? ""
let content = data["content"] as? String ?? ""
let type = data["type"] as? String ?? ""

append is not working

I have parsed a json string and i want to use it in a tableview. when i try to append the json string into an array the append method is not working. Here is the code that i have used. Can anyone help me with this?
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var userName = [String]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// 1
let urlAsString = "http://demo.codeofaninja.com/tutorials/json-example-with-php/index.php"
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
//2
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
if (error != nil) {
print(error!.localizedDescription)
}
// 3
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
/*let err: NSError!
if (err != nil) {
print("JSON Error \(err.localizedDescription)")
}*/
// 4
//let fname: String! = jsonResult["firstname"] as! String
//let lname: String! = jsonResult["lastname"] as! String
//let usrname: String! = jsonResult["username"] as! String
dispatch_async(dispatch_get_main_queue(), {
if let users = jsonResult.objectForKey("Users") as? [[String:AnyObject]]
{
for user in users
{
print("First Name:")
print(user["firstname"]!)
print("Last Name:")
print(user["lastname"]!)
print("User Name:")
let nameUser = user["username"]! as! String
print(nameUser)
self.userName.append(nameUser)
print("***************")
}
}
//print(jsonResult["Users"]!)
//print(lname)
//print(usrname)
})
}
catch {
print("error");
}
})
jsonQuery.resume()
//self.userName.append("ganesh")
// 5
print(userName)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userName.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell!
cell.textLabel?.text = userName[indexPath.row]
return cell
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
userName.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You are appending right in the array,Just call self.tableView.reloadData() to refresh table view at the end of dispatch to show the data in the tableView.
The append function works fine but you are filling the array in the background and never reloading the tableView when you have the data in the array
for user in users
{
if let nameUser = user["username"] as? String {
self.userName.append(nameUser)
}
}
self.tableView.reloadData()
Try this inside the if let users = jsonResult.objectForKey("Users") as? [[String:AnyObject]] block