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)
Related
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{
}
}
}
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)
}
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'm having trouble fetching my object from core data. Object looks like this:
class cilj: NSObject {
var imeCilja: String
var slikaCilja: String }
My entity is called Entity and has two Attributes "tekst" and "slika", both of type String. My save func is this:
func saveImage(goalName:String, imageName:String) {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entityDescription = NSEntityDescription.entityForName("Entity", inManagedObjectContext:managedContext)
let thingToSaveToCD = NSManagedObject(entity: entityDescription!, insertIntoManagedObjectContext: managedContext)
thingToSaveToCD.setValue(globalGoalTitle, forKey: "tekst")
thingToSaveToCD.setValue(globalGoalImagePath, forKey: "slika")
do {
try managedContext.save()
print("managed to save to core data")
//5
// listaObjekata.append(cilj5) as! [cilj]
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
I use an alertController to pick up the text, and imagePicker to pick up image that I then store in documents and get the path. I store both of these in a global variables visible in the code above.
My fetch function is :
func coreDataFetch(){
//core data fetch
//1
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
//2
let fetchRequest = NSFetchRequest(entityName: "Entity")
do {
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as! [cilj]
listaObjekata = fetchedResults
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
I have been through ray wenderlich's article on Core Data, Apple's documentation, a couple of YT videos but I still seem to be missing something. I would greatly appreciate any help. Thanks !
EDIT - here is my cellForRowAtIndexPath
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myCell") as! cellController
let ciljZaPrikaz = listaObjekata[indexPath.item]
cell.labelText.text = ciljZaPrikaz.imeCilja ?? "no text found"
let path = getDocumentsDirectory().stringByAppendingPathComponent(ciljZaPrikaz.slikaCilja)
cell.imageToShow.image = UIImage(contentsOfFile: path)
return cell
}
You are doing correct but just missing type casting to your model. Try below:
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest)
if let results = fetchResults where results.count > 0 {
let entityModel = results[0] as? Entity
let tekst = entityModel.tekst
}
I would also like to add how I iterate through my return objects from core data:
do {
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest)
listaObjekata.removeAll()
for i in 0...(fetchedResults.count-1) {
let enityModel = fetchedResults[i] as? Entity
let thisToArray = cilj(imeCilja: (enityModel?.tekst!)!, slikaCilja: (enityModel?.slika!)!)
listaObjekata.append(thisToArray)
}
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
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