Bookmark Button with core data in a TableView - ios

Question:- I am having problem while deleting a row from core data. Whenever I perform the delete function, The values in the row are changed to 0 but the row still exist in Core database. How can I completely remove the row in below written code?
I have a tableView showing some list, each cell has a button with grey bookmark image as default.
When I click on bookmarkBtn, the button image is changed to blue image and the data is stored by coreData. After clicking it again, bookmarkBtn again changes to grey image and the entry in core data is supposed to be deleted.
//Code:- **when bookmarkBtn is clicked.**
#IBAction func btnClicked(_ sender: UIButton)
{
let position: CGPoint = sender.convert(CGPoint.zero, to: self.tableView)
let indexPath = self.tableView.indexPathForRow(at: position)!
sender.tag = indexPath.row
let cell: TableViewCell = self.tableView.cellForRow(at: indexPath) as! TableViewCell
// print("Favorite button tapped")
if sender.isSelected != true
{
cell.bookmarkBtn.setImage(UIImage(named: "bookmark_blue"), for: UIControlState.normal);
sender.isSelected = true;
saveItem(itemToSave:sender.tag)
self.bookmarkArrState.replaceObject(at: (indexPath.row), with:1);
}
else
{
cell.bookmarkBtn.setImage(UIImage(named: "bookmark_grey" ), for: UIControlState.normal);
sender.isSelected = false;
removeData(itemToDelete: sender.tag)
self.bookmarkArrState.replaceObject(at: (indexPath.row), with:0)
}
}
//Code:- **Saving values in CoreData**
func saveItem(itemToSave: Int){
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
//**Note:** Here we are providing the entityName **`Entity`** that we have added in the model
let entity = NSEntityDescription.entity(forEntityName: "AddedBookmark", in: context)
let myItem = NSManagedObject(entity: entity!, insertInto: context)
myItem.setValue(itemToSave, forKey: "indexNo")
do {
try context.save()
}
catch{
print("There was an error in saving data")
}
}
//**Code:- Deleting Data when bookmarkBtn is clicked again**
func removeData(itemToDelete: Int)
{
let mngdCntxt = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
//**Note:** Here we are providing the entityName **`Entity`** that we have added in the model
let entity = NSEntityDescription.entity(forEntityName: "AddedBookmark", in: mngdCntxt)
let myItem = NSManagedObject(entity: entity!, insertInto: mngdCntxt)
if let dataAppDelegatde = UIApplication.shared.delegate as? AppDelegate {
let mngdCntxt = dataAppDelegatde.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "AddedBookmark")
let predicate = NSPredicate(format: "indexNo = %i", Int(itemToDelete))
print("item selected :-\(itemToDelete)")
fetchRequest.predicate = predicate
do{
var result = try mngdCntxt.fetch(fetchRequest)
print(result.count)
print(fetchRequest)
if result.count > 0{
for object in result
{
print(object)
mngdCntxt.performAndWait {
mngdCntxt.delete(result[0] as! NSManagedObject)
mngdCntxt.refreshAllObjects()
}
}
}
do {
try mngdCntxt.save()
}
catch{
print("There was an error in saving data")
}
}
catch{
}
}
}

Related

Setting an image to coredata

I have a list of tableviewcells loaded. And beside each of those cells is an 'Add To favorite' button. When that 'Add To favorite' is clicked the image on it will change and that changed image should be stored into coredata so that when the app is run again I can know which cell was favorited. For that this is what has been tried...
func favoriteBtnTapped(cell: HistoryTableViewCell) {
if segmentControl.selectedSegmentIndex == 2 {
favBtnTapFlag = true
if let indexPath = tableview?.indexPath(for: cell) {
let myFavMsg = messages1[indexPath.row]
let likedMsg = myFavMsg.historyData
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "FavoritedMessages", in: managedContext)
let category = NSManagedObject(entity: entity!, insertInto: managedContext)
category.setValue(likedMsg, forKeyPath: "favData")
//New image is set on the cell and it is saved to coredata here...
cell.favoriteButton.setImage(UIImage(named: "pin"), for: .normal)
let imageData = UIImageJPEGRepresentation((cell.favoriteButton.imageView?.image)!, 1)
category.setValue(imageData, forKey: "favImage")
do {
try managedContext.save()
self.favMessages.append(category as! FavoritedMessages)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
}
And it is fetched in viewWillAppear like so...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
let fetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Messages")
let fetchRequest1 = NSFetchRequest<NSManagedObject>(entityName: "FavoritedMessages")
do {
messages1 = try managedContext.fetch(fetchRequest as! NSFetchRequest<NSFetchRequestResult>) as! [Messages]
favMessages = try managedContext.fetch(fetchRequest1 as! NSFetchRequest<NSFetchRequestResult>) as! [FavoritedMessages]
for result in favMessages as [FavoritedMessages] {
if let imageData = result.value(forKey: "favImage") as? NSData {
if let image = UIImage(data:imageData as Data) as? UIImage {
favoritedImage.image = image
}
}
}
tableview.reloadData()
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}}
Here favoritedImage.image = image I'm trying to assign the fetched image to an imageview variable and then assign it at cellForRow. But how that can be done I'm not sure...
You should not save image in coredata.
Save the model with favourite_status as 1. (0 => unfavourited and 1 => favourited ) in coredata. (Basically boolean value)
Load the image from app bundle based on favourite_status.
When loading cells also use favourite_status to load image.
/// Edited
func favoriteBtnTapped(cell: HistoryTableViewCell) {
//1. Get Entity from datasource(Array) using indexpath.row
//2. Favorite the entity by setting favaorite_status = 1
//3. Save to coredata also
//4. Reload Table view
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//1. Get Entity from datasource(Array) using indexpath= Array
//2. Set cell image based on favaorite_status. Use UIImage.init(name:””) (if favaorite_status == 0, imagename will be different)
}

swift - NSPredicate and Context Save is not working well

I have a function that checking core data. if doesn't exist it should write to in it(but its not working in that way) SoundItems is an array that contains favorited objects and mainArray is the array that contains real objects;
#IBAction func Favori(_ sender: Any) {
// save core data
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
let newSound = NSEntityDescription.entity(forEntityName: "Sounds", in: context)
let sound = NSManagedObject(entity: newSound!, insertInto: context)
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Sounds")
let predicate = NSPredicate(format: "soundName = %#", soundArray[soundIndex].deletingPathExtension().lastPathComponent)
fetchRequest.predicate = predicate
do {
let fetchResults = try context.fetch(fetchRequest) as? [Sounds]
if fetchResults!.count > 0 {
print("already favd")
}
else {
sound.setValue(soundArray[soundIndex].deletingPathExtension().lastPathComponent, forKey: "soundName")
try context.save()
soundItems.append(sound)
print(sound)
}
}
catch {
print(error)
}
}
and here is the code that listing the core data;
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")! as UITableViewCell
let sound = soundItems[indexPath.row]
cell.textLabel?.text = sound.value(forKey: "soundName") as? String
return cell
}
I tried to run the code in debug mode and it works fine when there is no duplicated core data and adds into the tableView list. But here is the situation; when core data exist(fetchResults!.count > 0) still adding in the tableview as nil label.text but always adds the item of mainArray[0]
If you want to check if the sound is in the favorites and if not add a new sound you have to change the order of the steps
#IBAction func Favori(_ sender: Any) {
// save core data
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
let fetchRequest : NSFetchRequest<Sounds> = Sounds.fetchRequest()
let favName = soundArray[soundIndex].deletingPathExtension().lastPathComponent
let predicate = NSPredicate(format: "soundName = %#", favName)
fetchRequest.predicate = predicate
do {
let fetchResults = try context.fetch(fetchRequest)
if fetchResults.isEmpty {
let newSound = NSEntityDescription.insertNewObject(forEntityName: "Sounds", into:context) as! Sounds
newSound.soundName = favName
try context.save()
soundItems.append(newSound)
print(newSound)
}
else {
print("already favd")
}
}
catch {
print(error)
}
}
It's recommended to name entities in singular form (Sound).
#vadian had the point. Why did you insert a new sound before fetching the data?
let newSound = NSEntityDescription.entity(forEntityName: "Sounds", in: context)
let sound = NSManagedObject(entity: newSound!, insertInto: context)

User-entered values saved to Core Data (?) but not appearing in table view

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()
}

Can't load data from CoreData into tableview

I'm learning how to use CoreData. I have a tableView with an add button, which segues to a view with just a textfield and a save button. When the button is pressed, the string in the textfield is saved in a CoreData database:
#IBOutlet weak var name: UITextField!
#IBAction func onSave(sender: UIBarButtonItem) {
saveItem(name.text!)
self.dismissViewControllerAnimated(true, completion: nil)
}
private func saveItem(itemName: String)
{
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entity = NSEntityDescription.entityForName("Item", inManagedObjectContext: managedContext)
let item = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedContext)
item.setPrimitiveValue(itemName, forKey: "name")
do {
try managedContext.save()
listItems.append(item)
}catch let error {
print(error)
}
The tableView file has the Controller class and a global array of NSManagedObject:
Here is the relevant code:
var listItems = [NSManagedObject]()//outside the class
override func viewWillAppear(animated: Bool) { //in the tableviewcontroller class
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetch = NSFetchRequest(entityName: "Item")
do {
let results = try managedContext.executeFetchRequest(fetch)
listItems = results as! [NSManagedObject]
//EDIT
for item in listItems {
print(item)
}
} catch let error {
print(error)
}
self.tableView.reloadData()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
let item = listItems[indexPath.row]
cell.textLabel?.text = item.valueForKey("name") as? String
return cell
}
The database is very simple: an Entity named Item with a name attribute
This, however, doesn't work. When I press the save button, no new rows are added. If I remove all the code in viewWillAppear but self.tableView.reloadData(), the rows are created but when I relaunch the app they aren't there.
Any help would be much appreciated.

TableView/CoreData Subtitles and Title

I am making a list app and have a Coredata model with two attributes in it (One for item and one for details)
I can save and input data and it will be displayed correctly but when i close and reopen the app, everything has its own cell.
Any help guys?
Code For CellAtIndex
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")! as UITableViewCell
let item = listItems[indexPath.row]
let detailed = detailedItems[indexPath.row]
cell.textLabel?.text = item.valueForKey("item") as? String
cell.detailTextLabel?.text = detailed.valueForKey("detail") as? String
return cell
}
Code For ViewWillAppear
verride func viewWillAppear(animated: Bool) {
//Load First Item
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchrequest = NSFetchRequest(entityName: "ListEnt")
do{
let results = try managedContext.executeFetchRequest(fetchrequest)
listItems = results as! [NSManagedObject]
detailedItems = results as! [NSManagedObject]
}catch{
print("Error")
}
}
Here is the code for saving, I have a dialog box appear, with to text field, and each text field has its own save function
//Saving Items//
func saveItem(itmToSave: String){
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entity = NSEntityDescription.entityForName("ListEnt", inManagedObjectContext: managedContext)
let item = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedContext)
item.setValue(itmToSave, forKey: "item")
do{
try managedContext.save()
listItems.append(item)
}catch{
print("Saving Main Item")
}
}
//Saving Details
func saveItem2(itmToSave2: String){
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entity = NSEntityDescription.entityForName("ListEnt", inManagedObjectContext: managedContext)
let item2 = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedContext)
item2.setValue(itmToSave2, forKey: "detail")
do{
try managedContext.save()
detailedItems.append(item2)
}catch{
print("Saving deatil Item")
}
}
Pictures of the problem
How it looks when you input data (Correct way)
How it looks when you close and reopen the app
Cheers Guys

Resources