I had a problem to filter realm database. I search movie name and wrote values to realm database from Json. After written, I assign to tableview cell to show results. At the second search, I always get same values because of the getting all result from the database. I need to filter new values from the database and set to tableview. Please help me !
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
self.counter = 0
aranacak_kelime = searchBar.text!
let url = URL(string: "https://www.omdbapi.com/?s=" + aranacak_kelime + "&apikey=c6e----")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if(error != nil)
{
print("error")
}
else{
if let content=data
{
do
{
let json = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableLeaves) as AnyObject
if let search = json["Search"] as? [NSDictionary]
{
DispatchQueue(label: "background").async {
autoreleasepool {
let realm = try! Realm()
print(Realm.Configuration.defaultConfiguration.fileURL)
for result in search
{
let movie = Movie()
movie.name = result["Title"] as! String
movie.type = result["Type"] as! String
movie.year = result["Year"] as! String
movie.poster = result["Poster"] as! String
try! realm.write {
realm.add(movie)
}
self.counter += 1
DispatchQueue.main.async {self.tableView.reloadData()}
}
}
}
}
}
catch{
print("error in JSONSerialization")
}
}
}}task.resume()}
and tableview
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "textCell", for: indexPath) as! TableViewCell
let row = indexPath.row
let realm = try! Realm()
//let results = realm.objects(Movie.self).filter("ANY name.contains('aranacak_kelime')")
let results = realm.objects(Movie.self)
cell.movieName.text = results[row].name
cell.movieYear.text = results[row].year
cell.movieType.text = results[row].type
let imageUrl = results[row].poster
}
and my Movie class
class Movie: Object{
#objc dynamic var name:String = ""
#objc dynamic var type:String = ""
#objc dynamic var year:String = ""
#objc dynamic var poster:String = ""
}
As I understand, you have a couple of issues. If you search for the same movie twice, you will be saving the same result to your realm DB. So maybe use some sort of ID and when saving use realm.add(movie, update: true) this will prevent from storing the same movie twice.
The other issue is that you are not filtering the results you load on the tableview.
You should have an auxiliar method where you use the search bar text to filter your results before reloading your table view, something like:
func reloadContent() {
self.data = realm.objects(Movie.self).filter("ANY name.contains(YOUR_SEARCHBAR.text)")
self.tableView.reloadData()
}
You should change this line DispatchQueue.main.async {self.tableView.reloadData()} for DispatchQueue.main.async {self.reloadContent()}
and finally have a property of var data: Results<Movie>? where you will store the filtered movies. You will have to change your table view datasource delegate methods to use this data instead.
Hope it helps.
Related
my scenario, I am loading JSON Data into CoreData, after that I am fetching into Tableview. Now, Each and every tableview cell have swipe with Delete and Edit button. If I click delete I need to remove data from coredata and tableview both place.
My JSON Structure
class displyDataClass {
var name : String
var username : String
var email : String
init(name : String,username : String,email :String) {
self.name = name
self.username = username
self.email = email
}
}
JSON Load Into CoreData
import UIKit
import CoreData
class ViewController: UIViewController ,UITableViewDelegate,UITableViewDataSource{
var displayDatasssss = [displyDataClass]()
var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
print("hai")
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return displayDatasssss.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell1") as! TableViewCell1
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "User")
cell.label.text = displayDatasssss[indexPath.row].email
let _:AppDelegate = (UIApplication.shared.delegate as! AppDelegate)
let context:NSManagedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let newUser = NSEntityDescription.insertNewObject(forEntityName: "User", into: context) as NSManagedObject
newUser.setValue(cell.label.text, forKey: "name")
do {
try context.save()
} catch {}
print(newUser)
print("Object Saved.")
let myStringValue = cell.label.text
request.predicate = NSPredicate (format: "name == %#", myStringValue!)
do
{
let result = try context.fetch(request)
if result.count > 0
{
let nameData = (result[0] as AnyObject).value(forKey: "name") as! String
print(nameData)
}
}
catch {
//handle error
print(error)
}
return cell
}
#IBAction func tap(_ sender: Any) {
let url = "http://jsonplaceholder.typicode.com/users"
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request){(data, response,error)in
if (error != nil){
print("Error")
}
else{
do{
// Array of Data
let fetchData = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! NSArray
for eachData in fetchData {
let eachdataitem = eachData as! [String : Any]
let name = eachdataitem["name"]as! String
let username = eachdataitem["username"]as! String
let email = eachdataitem["email"]as! String
self.displayDatasssss.append(displyDataClass(name: name, username: username,email : email))
}
self.tableView.reloadData()
}
catch{
print("Error 2")
}
}
}
task.resume()
}
}
class displyDataClass {
var name : String
var username : String
var email : String
init(name : String,username : String,email :String) {
self.name = name
self.username = username
self.email = email
}
}
Below code For delete
// delete action two
let deleteAction = UITableViewRowAction(style: .default, title: "Delete", handler: { (action, indexPath) in
print("Delete tapped")
// remove the deleted item from the model
let appDel:AppDelegate = UIApplication.shared.delegate as! AppDelegate
let managedObjectContext = appDel.persistentContainer.viewContext
managedObjectContext.delete(self.displayDatasssssindexPath.row])
self.milestoneTitles.remove(at: indexPath.row)
do {
try managedObjectContext.save()
} catch _ {
}
self.tableView.deleteRows(at: [indexPath], with: .automatic)
return [editAction, deleteAction]
}
Don't use a custom class. Use only the provided User class.
First of all declare a data source array (replacing displayDatasssss)
var users = [User]()
In the tap method load the data and insert new items in the Core Data stack. Consider that each tap on the button inserts duplicate items into the database. Older entries are not removed.
As User has only name and id properties email is assigned to id.
The items are appended to the data source array and saved in the context.
#IBAction func tap(_ sender: Any) {
let url = "http://jsonplaceholder.typicode.com/users")!
let task = session.dataTask(with: url){ [unowned self] (data, response,error)in
if let error = error { print(error); return }
do {
// Array of Data
let fetchData = try JSONSerialization.jsonObject(with: data!) as! [[String:Any]]
for eachDataItem in fetchData {
let name = eachdataitem["name"] as! String
let email = eachdataitem["email"] as! String
let newUser = User(context: self.context)
newUser.name = name
newUser.id = email
self.users.append(newUser)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
try self.context.save()
} catch{
print("Error 2", error)
}
}
task.resume()
}
In viewDidLoad fetch the data from CoreData and reload the table view
override func viewDidLoad() {
super.viewDidLoad()
do {
let request : NSFetchRequest<User> = User.fetchRequest()
users = try context.fetch(request)
tableView.reloadData()
} catch { print(error) }
}
In cellForRow assign the property value(s) to the labels, nothing else
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell1") as! TableViewCell1
let user = users[indexPath.row]
cell.label.text = user.name
return cell
}
The delete method is quite similar to yours
let deleteAction = UITableViewRowAction(style: .default, title: "Delete", handler: { [unowned self] (action, indexPath) in
print("Delete tapped")
// remove the deleted item from the model
let objectToDelete = self.users.remove(at: indexPath.row)
self.context.delete(objectToDelete)
do {
try self.context.save()
self.tableView.deleteRows(at: [indexPath], with: .automatic)
} catch {
print(error)
}
}
return [editAction, deleteAction]
Note: Print always errors, don't ignore them or print only meaningless literal strings
Swift 5, iOS 12, Xcode 10
I finally implemented a search bar on my ListingsViewController and it's working really well -- but the initial data isn't populating into the table.
I realize that this is a clumsy implementation, but I'm learning as I go and I'm using only code that I can understand. I've been at this for two days -- I've tried creating a Struct and bringing in the data that way, but I can't even get an Array. I've tried bringing it in as an NSArray and an Array and an Object, but either I can't get the initial table to load, or I can't get data to parse out at all, or I can't get the search to work.
I suspect it has something to do with how, when or where I'm calling the loadData() function but I just can't figure it out.
class ListingsViewController: UITableViewController, UISearchResultsUpdating {
var tableData = [String]()
var filteredTableData = [String]()
var resultSearchController = UISearchController()
func updateSearchResults(for searchController: UISearchController) {
filteredTableData.removeAll(keepingCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (tableData as NSArray).filtered(using: searchPredicate)
filteredTableData = array as! [String]
self.tableView.reloadData()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
tableData = [String]()
loadData()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.reloadData()
resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
tableView.tableHeaderView = controller.searchBar
return controller
})()
tableView.reloadData()
}
func loadData() {
guard let url = URL(string: "--------------") else {return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let dataResponse = data, error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do {
let jsonResponse = try JSONSerialization.jsonObject(with: dataResponse, options: [])
guard let jsonArray = jsonResponse as? [[String:Any]] else { return }
for dic in jsonArray {
self.tableData.append(dic["name"] as! String)
// self.feedItems.append(Listing(id: (dic["id"] as! String), city_id: (dic["city_id"] as! String), category: (dic["category"] as! String), sub_category: (dic["sub_category"] as! String), name: (dic["name"] as! String), phone: (dic["phone"] as! String), email: (dic["email"] as! String), website: (dic["website"] as! String), address: (dic["address"] as! String), comment: (dic["comment"] as! String), recommendedby: (dic["recommendedby"] as! String)))
}
} catch let parsingError {
print("Error", parsingError)
}
}
self.tableView.reloadData()
task.resume()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if (resultSearchController.isActive) {
return filteredTableData.count
} else {
return tableData.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.textColor = .white
cell.backgroundColor = .black
cell.tintColor = .lightText
if (resultSearchController.isActive) {
cell.textLabel?.text = filteredTableData[indexPath.row]
return cell
} else {
cell.textLabel?.text = tableData[indexPath.row]
return cell
}
}
}
I'm expecting the entire list of Listings to appear when I switch to the ListingsViewController from the tab bar. Instead, I get a blank table.
HOWEVER, If I tap into the search bar and start typing, though, I get matching results -- and when I cancel the search, I can see all of the results in the table.
(Also, when I tap into the search bar, my navigation bar goes away and doesn't come back, even when I cancel the search. Even if I switch to a different tab and come back. Haven't been able to figure that one out.)
You are missing the delegate and data source for the table view
add this:
class ListingsViewController: UITableViewController, UISearchResultsUpdating,UITableViewDelegate,UITableViewDataSource {
//your class code here
}
and on your viewDidLoad add this:
self.tableView.delegate = self
self.tableView.dataSource = self
that should work, Please notice that after you called the delegate and data source your cell for row and numbers of rows functions will be called so make sure the array you are using is not nil by then
I saw you haven't been missing delegate and data source for the table view.
You must register delegate and data source for viewcontroler. where you will display data.
Please insert this code on your viewDidLoad
self.tableView.delegate = self
self.tableView.dataSource = self
Change your load day to this
func loadData() {
guard let url = URL(string: "--------------") else’s {return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let dataResponse = data, error == nil else {
print(error?.localizedDescription ?? "Response Error")
return
}
do {
let jsonResponse = try JSONSerialization.jsonObject(with: dataResponse, options: [])
guard let jsonArray = jsonResponse as? [[String:Any]] else { return }
for dic in jsonArray {
self.tableData.append(dic[“name”] as! String
}
DispatchQueue.main.async {
print("call to reload data")
self.tableView.reloadData()
}
} catch let parsingError {
print("Error", parsingError)
}
}
task.resume()
}
your are callig to reload data before the completion block, you can check it with the following print("calling to reloadData") then test my solution and see that works, make a print statamente for JsonArray to check where is called, you must reload the tableViewData after the async func is terminated.
I have search module, which connected to the parse.
And i want to make searching from few data table base.
For searching i use next code:
var searchResult = [String]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return searchResult.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = searchResult[indexPath.row]
return cell
}
For searching bar this code:
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
let firstQuery = PFQuery(className: "fried")
firstQuery.whereKey("best", contains: searchBar.text)
let seccondQuery = PFQuery(className: "home")
seccondQuery.whereKey("home", contains: searchBar.text)
let query = PFQuery.orQuery(withSubqueries: [firstQuery,seccondQuery])
query.findObjectsInBackground { (result, error) in
if let objects = result {
self.searchResult.removeAll(keepingCapacity: false)
for object in objects {
let question = object.object(forKey: "question") as! String
let answer = object.object(forKey: "answer1") as! String
let test = question + " " + answer
self.searchResult.append(test)
}
DispatchQueue.main.async {
self.tableView.reloadData()
self.resignFirstResponder()
}
}
}
}
And i got this type of error:
reason: 'All sub queries of an or query should be on the same class.'
It's possible to make searching at the few data base?
Search one class at a time, orQuery is done on subqueries of the same class
self.searchResult.removeAll(keepingCapacity: false) // Start by emptying the search resulys array
firstQuery.findObjectsInBackground { (result, error) in
if let objects = result {
for object in objects {
let question = object.object(forKey: "question") as! String
let answer = object.object(forKey: "answer1") as! String
let test = question + " " + answer
self.searchResult.append(test)
}
DispatchQueue.main.async {
self.tableView.reloadData()
self.resignFirstResponder()
}
}
}
seccondQuery.findObjectsInBackground { (result, error) in
if let objects = result {
for object in objects {
let question = object.object(forKey: "question") as! String
let answer = object.object(forKey: "answer1") as! String
let test = question + " " + answer
self.searchResult.append(test)
}
DispatchQueue.main.async {
self.tableView.reloadData()
self.resignFirstResponder()
}
}
}
Or more consizely:
let queryArry = [firstQuery, secondQuery]
for query in queryArray {
query.findObjectsInBackground { (result, error) in
if let objects = result {
for object in objects {
let question = object.object(forKey: "question") as! String
let answer = object.object(forKey: "answer1") as! String
let test = question + " " + answer
self.searchResult.append(test)
}
DispatchQueue.main.async {
self.tableView.reloadData()
self.resignFirstResponder()
}
}
}
}
I've been struggling with user-entered values showing up correctly in a table view in a project I'm working on.
The way I get the user entered values is by getting the user to enter information (company name, stock symbol, and a URL for the logo) into text fields, then calling handleSave() when the done button is pressed:
func handleSave() {
let newCompanyName = nameTextField.text
guard let newCompanyStockSymbol = stockTextField.text else {
// handle the error how you see fit
print("error getting text from field")
return
}
let newCompanyLogo = logoTextField.text
var newCompanyStockPrice = ""
// Fetch stock price from symbol provided by user for new company
let url = URL(string: "https://query.yahooapis.com/v1/public/yql?q=select%20symbol%2C%20Ask%2C%20YearHigh%2C%20YearLow%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(%22\(newCompanyStockSymbol)%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
} else if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
let json = JSON(data: data!)
if let quotes = json["query"]["results"]["quote"].array {
for quote in quotes {
let ask = quote["Ask"].stringValue
newCompanyStockPrice = ask
}
}
}
self.viewController?.save(name: newCompanyName!, logo: newCompanyLogo!, stockPrice: newCompanyStockPrice)
//self.viewController?.tableView.reloadData()
}
task.resume()
let cc = UINavigationController()
let companyController = CompanyController()
cc.viewControllers = [companyController]
present(cc, animated: true, completion: nil)
}
Which in turn calls this save function, which saves the values into the managed context.
func save(name: String, logo: String, stockPrice: String) {
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
let entity =
NSEntityDescription.entity(forEntityName: "Company",
in: managedContext)!
let company = NSManagedObject(entity: entity,
insertInto: managedContext)
company.setValue(stockPrice, forKey: "stockPrice")
company.setValue(name, forKey: "name")
company.setValue(logo, forKey: "logo")
do {
try managedContext.save()
companies.append(company)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
tableView.reloadData()
}
If I put a breakpoint when I call self.viewController?.save(name: newCompanyName!, logo: newCompanyLogo!, stockPrice: newCompanyStockPrice) in my handleSave() function, I can see that all three things (newCompanyName, newCompanyLogo, and newCompanyStockPrice) have values. But the new company does not appear on my table view when I try to set it in cellForRow:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return companies.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! Cell
let company = companies[indexPath.row]
let stock = company.value(forKey: "stockPrice") as? String
// Company name labels
cell.textLabel?.text = company.value(forKey: "name") as? String
// Stock price underneath
if let stock = stock {
cell.detailTextLabel?.text = "Current stock price: \(stock)"
}
// Logos
DispatchQueue.main.async {
if let url = NSURL(string: (company.value(forKey: "logo") as? String)!) {
if let data = NSData(contentsOf: url as URL) {
cell.logoView.image = UIImage(data: data as Data)
} else {
cell.logoView.image = UIImage(named: "noImage")
}
}
}
return cell
}
EDIT: viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//1
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
//2
let fetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Company")
//3
do {
companies = try managedContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
I would advise placing the call to reload data explicitly on the main queue. It appears to me you are calling func save(name: String, logo: String, stockPrice: String) from within the dataTask completion handler, which then calls reloadData on that thread.
Therefore, within the save function, wrap it like this:
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
I need help on how to solve this problem.
I tried to fetch data using json but when I tried to view in Table View its not showing.
I used the code below to test if table view is working and it works!
// self.clientList = ["Mango", "Banana", "Orange", "Guava", "Grapes"]
I used the code below to test if there's data returned from json. Still it works.
for item in jsonClientList {
let firstName = item["firstName"]
//Printing is working
print(firstName as! String)
}
Line not working! I dont know why. Its inside of loop but to data upon loading the table view.
Thanks in advance.
self.clientList.append(firstName as! String)
//---------------------------------
var clientList:[String] = []
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.clientList.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "tblClientList")
cell.textLabel?.text = self.clientList[indexPath.row]
return cell
}
internal func jsonParser() -> Void{
//-------------------------
let postEndpoint: String = "http://domain.com/client"
let url = NSURL(string: postEndpoint)
let session = NSURLSession.sharedSession()
session.dataTaskWithURL(url!, completionHandler:
{
(data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
do{
let ipString = NSString(data:data!, encoding: NSUTF8StringEncoding)
if (ipString != nil) {
let jsonClientList = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
for item in jsonClientList {
let firstName = item["firstName"]
//I tried to print the data from json and its working!
print(firstName as! String)
//Line not working
//I tried to insert the firstName to clientList array
self.clientList.append(firstName as! String)
}
}
//If I use this its working
// self.clientList = ["Mango", "Banana", "Orange", "Guava", "Grapes"]
}
} catch{
print("Something bad happed!")
}
}
).resume()
//--------------
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
jsonParser()
}
//---------------------------------
you forget to refresh the new data to show in table, do like
self.clientList.append(firstName as! String)
}
dispatch_async(dispatch_get_main_queue())
self.yourtableViewname.reloadData()
}
As mentioned in the other answer the issue is that the table view needs to be reloaded.
In Swift there is a more convenient way to populate the data source array without repeat loop using the map function.
It assumes – like in the question – that all dictionaries in jsonClientList contain a key firstName.
tableView is the name of the UITableView instance.
...
let jsonClientList = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as! [[String:AnyObject]]
self.clientList = jsonClientList.map{ $0["firstName"] as! String }
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
} catch {
...
Reading the JSON with mutable containers is not needed in this case.