I have been working for a project but stuck on how to retrieve CoreData to my Table View. This is my code:
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func loadData(){
let requestData: NSFetchRequest<Person> = Person.fetchRequest()
do {
try
personArr = context.fetch(requestData)
try context.save()
} catch {print ("Error retrieving data request \(error)")
self.tableView.reloadData()
}
}
// let DVC = segue.destination as! addPersonController
// numberOfRowsInSection ... cellForRowAt
// let cell = tableView.dequeueReusableCell(withIdentifier: "personCell", for: indexPath)
// cell.textLabel?.text = itemArray[indexPath.row].title > let person = personArr[indexPath.row]
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if(tableView.cellForRow(at: indexPath)?.accessoryType == .checkmark)
{tableView.cellForRow(at: indexPath)?.accessoryType = .none
personArr[indexPath.row].finish = false
//cell.accessoryType = .checkmark //forRaw
}
tableView.deselectRow(at: indexPath, animated: true) //not selected
savePerson()
// let myPerson = PersonArr[indexPath.row].name
// performSegue(withIdentifier: "personSegue", sender: myPerson)
}
Here is just an example of some function that I applied. my concern is at fetching data to personArr that is not been fetching as expected. Any ideas?
A closer look at the code reveals that you are reloading the table view in the catch scope which is almost never going to happen.
Move the line into the do scope and delete the pointless line to save the context
func loadData(){
let requestData: NSFetchRequest<Person> = Person.fetchRequest()
do {
personArr = try context.fetch(requestData)
self.tableView.reloadData()
} catch {
print ("Error retrieving data request \(error)")
}
}
Related
So I'm trying to implement a swipe delete of my favorite items off the favorites tab. The swipe implementation works but when I go back to the Favorites Tab, the item deleted reappears. I'm using core data and I know it is not deleting it from core data but not sure how to fix it. This is my file for the favorites tab:
import UIKit
import CoreData
class FavAnimeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var tableView: UITableView!
var favorites = [NSManagedObject]()
var numAnime = 0
var managedObjectContext: NSManagedObjectContext!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
// Do any additional setup after loading the view.
}
//loads every time view appears
override func viewDidAppear(_ animated: Bool) {
load()
}
func load() {
//1
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
//2
let fetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Favorite")
//3
do {
favorites = try managedContext.fetch(fetchRequest)
tableView.reloadData()
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return favorites.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "FavTableViewCell") as! FavTableViewCell
let favAnime = favorites[indexPath.row]
cell.favtitleLabel.text = favAnime.value(forKeyPath: "title") as? String
cell.favsumLabel.text = favAnime.value(forKeyPath: "synopsis") as? String
if let poster = favAnime.value(forKey: "poster") as? String{
let posterUrl = URL(string: poster)!
cell.favposterView.af.setImage(withURL: posterUrl)
}
return cell
}
//MARK: Delete favorite items
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
favorites.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
}
if any can help that be great
The changes (in this case Delete) wont be seen affected until you save the state of the NSManagedObjectContext, Helpfully, managed object context has a matching delete() method that will delete any object regardless of its type or location in the object graph. Once an object has been deleted from the context, we can then call saveContext() to write that change back to the persistent store so that the change is permanent.
do {
try managedObjectContext.save()
} catch (let error) {
print(error.localizedDescription)
}
Remember: you must call saveContext() whenever you want your changes to persist.
I Hope I Helped!
Core Data | nsmanagedobjectcontext
Delete from CoreData
func deleteParticularRecord(id:String){
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
// use predicate for delete particular record from coredata.
let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "EnitityName")
let predicate = NSPredicate(format: "id = '\(id)'")
fetchRequest.predicate = predicate
let objects = try! context.fetch(fetchRequest)
for obj in objects {
context.delete(obj as! NSManagedObject)
}
do {
try context.save() // <- remember to put this :)
} catch {
// Do something... fatalerror
}
}
// Call
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
favorites.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .fade)
// use id or object id
deleteParticularRecord(id: "_Id_")
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
// Hope its works :)
I have an image in a custom cell (called "StoryCell"), the reference to which is in a Realm database and which I am loading up in the CellForRowAt with the following code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
....
let story = stories?[indexPath.row]
if story?.storyImage.isEmpty == true {
print("Do nothing")
} else {
let path = getDocumentDirectory().appendingPathComponent(story!.storyImage)
do {
let imageData = try Data(contentsOf: path)
let retrievedImage = UIImage(data: imageData)
cell.storyImage.image = retrievedImage
} catch {
print("Error retrieving image: \(error)")
}
}
cell.delegate = self
return cell
}
However, whenever I am adding a new table item, after the eighth time the display of the images becomes inconsistent i.e. the first picture repeats on the eighth line. I know this is connected with the 'reuse' nature of cells in tableviews and have tried to resolve it using the 'if' statement in my code above, and also by 'reloading' the tableview data when the new item is added:
#IBAction func addStoryButton(_ sender: UIBarButtonItem) {
let newStory = Story()
newStory.dateCreated = Date()
self.save(story: newStory)
self.storiesTableView.reloadData()
}
However, I still get the same behaviour. Thanks in advance for any thoughts on this.
A full explanation of this issue is here: https://fluffy.es/solve-duplicated-cells/. I finally resolved with the following code in the cellForRowAt although the 'reset' code can also be placed in the 'prepareForReuse' function as indicated by Rocky. Setting the 'defaultImage' as seen in code below also helped:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "storyCell", for: indexPath) as! StoryCell
let story = stories?[indexPath.row]
//reset to default image
let defaultURL = Bundle.main.url(forResource: "test", withExtension: "jpg")
do {
let imageData1 = try Data(contentsOf: defaultURL!)
let retrievedImage = UIImage(data: imageData1)
cell.storyImage.image = retrievedImage
} catch {
print("Error retrieving defaultImage: \(error)")
}
//place saved image in storyImage
if story?.storyImage.isEmpty == true {
print("Do nothing")
} else {
let path = getDocumentDirectory().appendingPathComponent(story!.storyImage)
do {
let imageData = try Data(contentsOf: path)
let retrievedImage = UIImage(data: imageData)
cell.storyImage.image = retrievedImage
} catch {
print("Error retrieving image: \(error)")
}
}
cell.delegate = self
return cell
}
Thanks #Rocky for putting me on the right path.
prepareForReuse is the method which you are looking for.
override func prepareForReuse() {
super.prepareForReuse()
// Do your stuff here, like reset image or content of cell for reusing
self.storyImage.image = nil
}
I'm having problems deleting an item from my Core Data, and having looked at lots of other examples and questions - they all say about deleting an NSManagedObject whereas I'm trying to delete the item at the indexPath.row (which is a String).
var itemsArray = [String]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
What would I put in the following function?
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
}
}
Loading items in Core Data
func loadItems() {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Items")
request.returnsObjectsAsFaults = false
do {
let results = try context.fetch(request)
if results.count > 0 {
for result in results as! [NSManagedObject] {
if let product = result.value(forKey: "product") as? String {
self.itemsArray.append(product)
}
}
}
} catch {
print("Error")
}
}
To be able to delete the object you have to use the NSManagedObject as data source
var itemsArray = [Items]()
The loadItems can be reduced to
func loadItems() throws {
let request = NSFetchRequest<Items>(entityName: "Items")
request.returnsObjectsAsFaults = false
itemsArray = try context.fetch(request)
}
Put the do - catch block around the loadItems() call and print the error instance, not a meaningless literal string.
In cellForRow use
let item = itemArray[indexPath.row]
let product = item.product
To delete the item you have to remove it from the data source, then delete the item in the context and then save the context:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
let objectToDelete = itemArray[indexPath.row]
itemArray.remove(at: indexPath.row)
context.delete(objectToDelete)
// here add code to save the context
self.tableView.deleteRows(at: [indexPath], with: .fade) // and you have to update the table view
}
}
I got an array that populates a tableview, it works fine when I run the app.
I created a popover with a PickerView to choose one option to sort the TableView data.
I get the user choise in the popover, pass it to the main ViewController, sorted the data and called tableview.reloadData() but nothing happens.
I printed the array after the sort and the array is sorted but I can't saw the changes.
But if I go to other ViewController and came back the data is changed.
Why the changes are not showing when I call the tableview.reloadData().
Here's the code:
var dataModel = DataModel()
var ordenacao = String()
override func viewWillAppear(_ animated: Bool) {
dataModel.loadData()
tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
ordenadados(ordem: ordenacao)
tableView.reloadData()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell! = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier")
cell.textLabel?.text = dataModel.notas[indexPath.row].titulo
cell.detailTextLabel?.text = dataModel.notas[indexPath.row].datafinal
print("Ordena Tableview")
for nota in dataModel.notas {
print (nota.titulo ?? "")
}
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataModel.notas.count
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
dataModel.notas.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath as IndexPath], with: UITableViewRowAnimation.fade)
dataModel.saveData()
}
EDIT:
func ordenadados(ordem: String){
dataModel.loadData()
if(ordenacao == "Titulo Asc."){
print("Titulo Asc")
dataModel.notas.sort { $0.titulo! < $1.titulo! }
}else if(ordenacao == "Titulo Desc."){
print("Titulo Desc.")
dataModel.notas.sort { $0.titulo! > $1.titulo! }
}
dataModel.saveData()
for nota in dataModel.notas {
print (nota.titulo ?? "")
}
dataModel.loadData()
tableView.reloadData()
}
In the output the array was sorted but in the TableView nothing changed.
Save and Load Data methods:
//save data
func saveData() {
let data = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWith: data)
archiver.encode(notas, forKey: "teste")
archiver.finishEncoding()
data.write(toFile: dataFilePath(), atomically: true)
}
//read data
func loadData() {
let path = self.dataFilePath()
let defaultManager = FileManager()
if defaultManager.fileExists(atPath: path) {
let url = URL(fileURLWithPath: path)
let data = try! Data(contentsOf: url)
let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
notas = unarchiver.decodeObject(forKey: "teste") as! Array
unarchiver.finishDecoding()
}
}
We successfully implemented saving and reading from Core Data but we have problems with removing data from Core Data when random cell is deleted.
How to remove data from Core Data when random cell is deleted from tableView??? We typed some code like removing last index from array but it does not work...
2cd Vc
import UIKit
import CoreData
class TableViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var item = [NSManagedObject] ()
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
}
func tableView(tableView:UITableView, numberOfRowsInSection section:Int) -> Int
{
return item.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let person = item[indexPath.row]
cell.textLabel!.text = person.valueForKey("username") as? String
cell.detailTextLabel?.text = person.valueForKey("passwords") as? String
return cell
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
// WHAT TO TYPE HERE TO REMOVE DATA FROM CORE DATA WHEN RANDOM CELL IS DELETED
let context: NSManagedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
do {
try context.save()
} catch _ {
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Users")
do {
let results =
try managedContext.executeFetchRequest(fetchRequest)
item = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
I found it working like this
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
switch editingStyle {
case .Delete:
// remove the deleted item from the model
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext
context.deleteObject(item[indexPath.row] as NSManagedObject)
item.removeAtIndex(indexPath.row)
do{
try context.save()
}catch{
print("Error, data not saved!")
}
//tableView.reloadData()
// remove the deleted item from the `UITableView`
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
default:
return
}