Getting nil after delete an object in Coredata Swift - ios

I tried to store some data using CoreData.
I created Entity Name Users and some attributes namely age, username, password.
Successfully loading, retrieving.
When I delete some object in that entity using context.delete(user), It will be deleted but show nil after I retrieving again.
Showing nil for deleted object
Note: I write the code for predicate which format is "Username!=nil".
Is this the right way? or how to overcome without nil in core data?
import UIKit
import CoreData
class CoreDataVC: UIViewController {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
var context:NSManagedObjectContext!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addAction(_ sender: Any){
if let newUser = openDatabse(){
saveData(UserDBObj:newUser)
}
_ = fetchData()
}
#IBAction func deleteAction(_ sender: Any){
if let newUser = openDatabse(){
deleteData(UserObj: newUser)
}
_ = fetchData()
}
// MARK: Methods to Open, Store and Fetch data
func openDatabse() -> NSManagedObject?{
context = appDelegate.persistentContainer.viewContext
guard let entity = NSEntityDescription.entity(forEntityName: "Users", in: context) else {
print("Invalid Entity")
return nil
}
let newUser = NSManagedObject(entity: entity, insertInto: context)
return newUser
}
func deleteData(UserObj: NSManagedObject){
let result = self.fetchData()
if let data = result.last{
context.delete(data)
}
do {
try context.save()
}
catch{
print("Error on saving context")
}
_ = fetchData()
}
func saveData(UserDBObj:NSManagedObject)
{
UserDBObj.setValue("RDC1", forKey: "username")
UserDBObj.setValue("12341", forKey: "password")
UserDBObj.setValue("22", forKey: "age")
print("Storing Data..")
do {
try context.save()
} catch {
print("Storing data Failed")
}
_ = fetchData()
}
func fetchData() -> [Users]
{
var users : [Users] = []
print("Fetching Data..")
let request = Users.fetchRequest()
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
users = result
for data in result {//as! [NSManagedObject] {
print("Username: ",data.username ?? "nil",", Age : ",data.age ?? "nil")
}
} catch {
print("Fetching data Failed")
}
return users
}
}
Thanks in Advance.

Related

Core data not being updated after managedContext.save but no errors thrown

I'm trying to update an already saved entry in Core data, objectContext isn't nil neither is the managedObject. The new values get set to the managedObject with said Id. No errors are thrown when calling managedContext.save() but no changes are reflected in the coreData
I have tried using setValue() no avail and I have searched far and wide on google and stack overflow for a similar problem and most of the time it says to say that the managedObject is nil.
var taskName:String!
var resultsController: NSFetchedResultsController<Tasks>!
// MARK: - Properties
// Manage objects and update tasks
//var resultsController: NSFetchedResultsController<Tasks>!
var managedContext: NSManagedObjectContext!
var tasksArray = [NSManagedObject]()
var fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Tasks")
func settextfields() {
let res = resultsController.fetchedObjects!
print(res)
for r in res {
if r.name == taskName {
txt_name.text = "\(r.name ?? "Task name" )"
txt_date.text = "\(r.date ?? "Task date")"
segmentedBtn.selectedSegmentIndex = Int(r.priority)
}
}
}
func loadTable() {
let request: NSFetchRequest<Tasks> = Tasks.fetchRequest()
// Sort by date
let sortDescriptor = NSSortDescriptor(key: "date", ascending: true)
request.sortDescriptors = [sortDescriptor]
resultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: coreData.managedContext, sectionNameKeyPath: nil, cacheName: nil)
// Fetch data
do {
try resultsController.performFetch()
print("Fetch successful")
} catch {
print("Error performing fetch: \(error)")
}
}
#IBAction func saveEdit(_ sender: Any) {
guard let name = txt_name.text, !name.isEmpty else {
return
}
guard let date = txt_date.text, !date.isEmpty else {
return
}
do {
loadTable()
let res = resultsController.fetchedObjects!
for r in res {
i += 1
if r.name == taskName {
print(r)
guard let name = txt_name.text, !name.isEmpty else {
return
}
guard let date = txt_date.text, !date.isEmpty else {
return
}
do {
r.name = name
r.date = date
r.priority = Int16(segmentedBtn.selectedSegmentIndex)
}
do {
try managedContext.save()
print(managedContext, r)
dismiss(animated: true)
print("Edit Successful!")
} catch {
print("Error saving task: \(error)")
}
}
}
}
I actually rewrote the save function using predicate like the person above suggested and it worked. Then I added a viewWillLoad to the mainViewController to reload the tableView once the entry is updated
#IBAction func saveEdit(_ sender: Any) {
guard let name = txt_name.text, !name.isEmpty else {
return
}
guard let date = txt_date.text, !date.isEmpty else {
return
}
do {
let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "Tasks")
fetchRequest.predicate = NSPredicate(format: "name = %#", taskName)
let test = try managedContext.fetch(fetchRequest)
let obj = test[0] as! NSManagedObject
obj.setValue(name, forKey: "name")
obj.setValue(date, forKey: "date")
obj.setValue(Int16(segmentedBtn.selectedSegmentIndex), forKey: "priority")
do {
try managedContext.save()
dismiss(animated: true)
print("Edit Successful!")
} catch {
print("Error saving task: \(error)")
}
} catch {
print(error)
}
}

Saving into multiple entities - Core Data - Swift

Hopefully this will make sense.
I am trying to store data into 2 different entities from one function but not having much luck with storing data into the second entity.
My 2 entities are called - Job & ShootKitChecklist.
I have created a core data manager, I have stripped this back to just show you the section I am using:
struct CoreDataManager {
static let shared = CoreDataManager()
let persistentContainer: NSPersistentContainer = {
// initialization of core data stack
let container = NSPersistentContainer(name: "TBAShootCoreData")
container.loadPersistentStores { (storeDescription, error) in
if let error = error {
fatalError("loading of store failed: \(error)")
}
}
return container
}()
func createSKCItem(item: String, used: Bool, visible: Bool, job: Job) -> (ShootKitChecklist?, Error?) {
let context = persistentContainer.viewContext
// create Shoot kit item
let SKC = NSEntityDescription.insertNewObject(forEntityName: "ShootKitChecklist", into: context) as! ShootKitChecklist
SKC.job = job
SKC.setValue(item, forKey: "item")
SKC.setValue(used, forKey: "used")
SKC.setValue(visible, forKey: "visible")
do {
try context.save()
return (SKC, nil)
} catch let error {
print ("Failed to add Shoot Kit Item:", error)
return (nil, error)
}
}
}
When I try to save the data, the Job entity (First Entity)writes to the context and I can fetch it in another class.
The data I am trying to save to the ShootKitChecklist is from an array so I put my setValues into a for look. However, it seems to ignore saving any data to the entity.
var SKCequipment = ["A","B","C","D","E"]
#IBAction private func HandleSave(sender : UIButton) {
let context = CoreDataManager.shared.persistentContainer.viewContext
let job = NSEntityDescription.insertNewObject(forEntityName: "Job", into: context)
let SKC = NSEntityDescription.insertNewObject(forEntityName: "ShootKitChecklist", into: context)
job.setValue(jobBrandTextField.text, forKey: "jobBrand")
job.setValue(jobNameTextField.text, forKey: "jobName")
job.setValue(directorTextField.text, forKey: "directorName")
job.setValue(agencyTextField.text, forKey: "agencyName")
job.setValue(prodCoTextField.text, forKey: "prodCoName")
for item in SKCequipment {
print(item)
SKC.setValue(item, forKey: "item")
SKC.setValue(true, forKey: "used")
SKC.setValue(true, forKey: "visible")
}
do {
try context.save()
dismiss(animated: true) {
self.delegate?.didAddJob(job: job as! Job)
}
} catch let saveError {
print("Failed to save company:", saveError)
}
}
To test to see if the items have been added to the core data I am fetching the items like this:
guard let SKCitem = job?.skc?.allObjects as? [ShootKitChecklist] else { return}
self.skcitems = SKCitem
print(skcitems)
Thank you in advance, huge help!
Okay I have fixed it by adding another function to my protocol and appending the Entity in the delegate(I realise that I didn't share this before (apologies). Here is my new protocol:
protocol NewJobControllerDelegate {
func didAddJob(job : Job)
func didAddSKC(SKC : ShootKitChecklist)
}
And then in my HandleSave action I changed my for loop to this:
for item in SKCequipment {
let tuple = CoreDataManager.shared.createSKCItem(item: item, used: true, visible: true, job: job as! Job)
if let error = tuple.1 {
print("Cannot save items", error)
} else {
self.delegate?.didAddSKC(SKC: tuple.0!)
}
And finally my new function from my delegate:
func didAddSKC(SKC: ShootKitChecklist) {
ShootKit.append(SKC)
}
Thank you for your help!

Core data entity manager

I want to write class for manage all entity that i have in my application.
there is my code fore managerClass for one custom entity, but I have a problem to set manageObject type in this context.
public func clearEntityContex() {
let fetchRequest: NSFetchRequest<Shop> = Shop.fetchRequest()
if let fetchResult = try? self.stack.mainQueueContext.fetch(fetchRequest){
for user : Shop in fetchResult {
self.stack.mainQueueContext.delete(user as Shop)
}
do {
try stack.mainQueueContext.save()
}
catch {
debugPrint(error.localizedDescription)
}
}
}
//MARK: _fetch Contex
public func fetchsShopEntity() -> [Shop]? {
var shops : [Shop]?
let fetchRequest: NSFetchRequest<Shop> = Shop.fetchRequest()
do {
let fetchResult =
try self.stack.mainQueueContext.fetch(fetchRequest)
if fetchResult.count > 0 {
shops = fetchResult
}
}
catch {
fatalError("Failed to fetch Account: \(error)")
}
return shops
}
//MARK: _save entity
public func saveEntityInModel(entityItem : Shop){
if let entity = NSEntityDescription.entity(forEntityName: "Shop", in: self.stack.mainQueueContext) {
if let contex = NSManagedObject(entity: entity, insertInto: self.stack.mainQueueContext) as? Shop {
contex.packID = entityItem.packID
contex.packName = entityItem.packName
contex.packImage = entityItem.packImage
contex.priceDolar = entityItem.priceDolar
contex.packDescription = entityItem.packDescription
do {
try stack.mainQueueContext.save()
}
catch {
debugPrint(error.localizedDescription)
}
}
}
}
for example i want to write method that can clear any entityContext. but i cant pass manageObject to this method.
public func clearEntityContex(entityObject: NSManagedObject) {
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = entityObject.fetchRequest()
if let fetchResult = try? self.stack.mainQueueContext.fetch(fetchRequest){
for entity in fetchResult {
self.stack.mainQueueContext.delete(entity as entityObject)
}
do {
try stack.mainQueueContext.save()
}
catch {
debugPrint(error.localizedDescription)
}
}
}
how can solve pass NSManagedObject to this method?
thanks for all reply
Here's a more generic implementation that we use in our projects.
import CoreData
class ACSwiftCoreData: ACCoreDataPlugin {
let managedObjectModelName: String
let databasePath: URL
init(managedObjectModelName: String, databasePath: URL) {
self.managedObjectModelName = managedObjectModelName
self.databasePath = databasePath
}
// MARK: Managed Object Contexts
private var sharedContext: NSManagedObjectContext?
func getSharedManagedObjectContext() throws -> NSManagedObjectContext {
if let sharedContext = self.sharedContext {
return sharedContext
}
let context = try self.createManagedObjectContext()
self.sharedContext = context
return context
}
func createManagedObjectContext() throws -> NSManagedObjectContext {
let storeCoordinator = try self.getPersistentStoreCoordinator()
let managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = storeCoordinator
managedObjectContext.mergePolicy = NSMergePolicy(merge: NSMergePolicyType.mergeByPropertyObjectTrumpMergePolicyType)
return managedObjectContext
}
// MARK: Creating Entities
func createEntityInSharedContext<EntityType>(_ entityName: String) throws -> EntityType {
let context = try self.getSharedManagedObjectContext()
return try self.createEntity(entityName, context: context)
}
func createEntity<EntityType>(_ entityName: String, context: NSManagedObjectContext) throws -> EntityType {
let entity = NSEntityDescription.insertNewObject(forEntityName: entityName, into: context)
guard let expectedEntity = entity as? EntityType else {
throw self.errorWithMessage("ACSwiftCoreData: Entity for name \(entityName) does not match class \(EntityType.self).")
}
return expectedEntity
}
// MARK: Saving Entity
func saveEntity(_ entity: NSManagedObject) throws {
guard let context = entity.managedObjectContext else {
throw errorWithMessage("ACSwiftCoreData: Cannot save Entity. ManagedObjectContext is missing.")
}
if context.hasChanges {
try context.save()
}
}
// MARK: Delete Entity
func deleteEntity(_ entity: NSManagedObject) throws {
guard let context = entity.managedObjectContext else {
throw errorWithMessage("ACSwiftCoreData: Cannot delete Entity. ManagedObjectContext is missing.")
}
context.delete(entity)
try context.save()
}
// MARK: Fetch Requests
func fetchEntitiesInSharedContext<EntityType: AnyObject>(_ entityName: String, predicate: NSPredicate?) -> [EntityType] {
guard let context = try? self.getSharedManagedObjectContext() else {
return [EntityType]()
}
return self .fetchEntities(entityName, context: context, predicate: predicate)
}
func fetchEntities<EntityType: AnyObject>(_ entityName: String, context: NSManagedObjectContext, predicate: NSPredicate?) -> [EntityType] {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
fetchRequest.predicate = predicate
let results = try? context.fetch(fetchRequest)
guard let resultEntitys = results as? [EntityType] else {
return [EntityType]()
}
return resultEntitys
}
// MARK: Technical Details
private var storeCoordinator: NSPersistentStoreCoordinator?
private func getPersistentStoreCoordinator() throws -> NSPersistentStoreCoordinator {
if let storeCoordinator = self.storeCoordinator {
return storeCoordinator
}
let model = try self.getManagedObjectModel()
let storeCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
var options = [AnyHashable: Any]()
options[NSMigratePersistentStoresAutomaticallyOption] = true
options[NSInferMappingModelAutomaticallyOption] = true
try storeCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: self.databasePath, options: options)
self.storeCoordinator = storeCoordinator
return storeCoordinator
}
private var objectModel: NSManagedObjectModel?
private func getManagedObjectModel() throws -> NSManagedObjectModel {
if let objectModel = self.objectModel {
return objectModel
}
let momName = self.managedObjectModelName
guard let modelUrl = Bundle.main.url(forResource: momName, withExtension:"momd") else {
throw self.errorWithMessage("ACSwiftCoreData: DataModel Url could not be created.")
}
guard let objectModel = NSManagedObjectModel(contentsOf: modelUrl) else {
throw self.errorWithMessage("ACSwiftCoreData: DataModel could not be loaded.")
}
self.objectModel = objectModel
return objectModel
}
// MARK: Error handling
private func errorWithMessage(_ message: String) -> NSError {
let userInfo = [NSLocalizedDescriptionKey: message]
let error = NSError(domain: "com.appcron.accomponents", code: 0, userInfo: userInfo)
return error
}
}
In some project that I made and when I used CoreData, I usually create a Singleton with function to fetch, save and delete a CoreData object.
This is my CoreDataController:
import Foundation
import CoreData
import UIKit
final class CoreDataController {
static let sharedInstances = CoreDataController()
private var context: NSManagedObjectContext
private init(){
let application = UIApplication.shared.delegate as! AppDelegate
self.context = application.persistentContainer.viewContext
}
func loadAll() {
print("Fetch from CoreData")
let fetchRequest: NSFetchRequest<YourEntity> = YourEntity.fetchRequest()
do {
let entityArray = try self.context.fetch(fetchRequest)
guard entityArray.count > 0 else {
print("There aren't element in CoreData "); return}
} catch let error {
print("FetchRequest error")
print(" Print error: \n \(error) \n")
}
}
func save(entityToSave: String, item: String){
let entity = NSEntityDescription.entity(forEntityName: entityToSave, in: self.context)
let newItem = YourEntity(entity: entity!, insertInto: self.context)
newItem.name = item
do {
try self.context.save()
} catch let error {
print("Problem with \(newItem)")
print(" Print error: \n \(error) \n")
}
print("Element \(newItem) saved in CoreData")
}
func loadFromName(entityName:String, name: String) -> Any {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
request.returnsObjectsAsFaults = false
let predicate = NSPredicate(format: "yourEntityAttribute = %#", yourEntityAttribute)
request.predicate = predicate
let items = self.loadFromFetchRequest(request: request)
return items[0]
}
private func loadFromFetchRequest(request: NSFetchRequest<NSFetchRequestResult>) -> [Any] {
var array = [Any]()
do {
array = try self.context.fetch(request)
guard array.count > 0 else {print("There aren't element in CoreData"); return []}
for item in array {
print("Item: \(item)")
}
} catch let error {
print("FetchRequest Error")
print(" Print Error: \n \(error) \n")
}
return array
}
func delete(entityName: String, name: String) {
let item = self.loadFromName(entityName: entityName, name: name)
self.context.delete(item as! NSManagedObject)
do {
try self.context.save()
} catch let error {
print("Deleting problem")
print("Print Error: \n \(error) \n")
}
}
func loadData(entity: String) -> [YourEntity] {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
fetchRequest.returnsObjectsAsFaults = false
var data = [YourEntity]()
do {
data = try self.context.fetch(fetchRequest) as! [YourEntity]
} catch let error {
print("Print Error: \n \(error) \n")
}
return data
}
}
When you have to call, just write:
CoreDataController.sharedInstances.save(entityToSave: "Profile", item: textfield.text!)
or other functions!!!
I hope this is useful for you.
For use this Class with every Entity you can write like that:
let entity = NSEntityDescription.entity(forEntityName: entityToSave, in: self.context)
let newItem: Any
switch entityToSave {
case "YourEntity":
newItem = YourEntity(entity: entity!, insertInto: self.context)
(newItem as! YourEntity).entityAttribute = firstItem
(newItem as! YourEntity).entityAttribute = secondItem
case "YourEntity2":
newItem = YourEntity2(entity: entity!, insertInto: self.context)
(newItem as! YourEntity2).entityAttribute = firstItem
(newItem as! YourEntity2).entityAttribute = secondItem
case "YourEntity3":
newItem = YourEntity3(entity: entity!, insertInto: self.context)
(newItem as! YourEntity3).entityAttribute = firstItem
(newItem as! YourEntity3).entityAttribute = secondItem
case "YourEntity4":
newItem = YourEntity4(entity: entity!, insertInto: self.context)
(newItem as! YourEntity4).entityAttribute = firstItem
(newItem as! YourEntity4).entityAttribute = secondItem
default:
fatalError("Error in entityToSave function")
}
do {
try self.context.save()
} catch let error {
print("Problem to save \(newItem)")
print("Print Error: \n \(error) \n")
}
print("Element \(newItem) saved correctly")
}

How to insert new data to existing coredata in swift3

I am trying to insert new data to existed core data .But the new data is repeating . what is the problem with my code ?
func getContext(){
let container: NSPersistentContainer? = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer
let delegate = UIApplication.shared.delegate as? AppDelegate
if let context = container?.viewContext{
let request = NSFetchRequest<Mydata>(entityName: "Mydata")
do {
let searchResults = try context.fetch(request)
let person1 = searchResults[0]
if person1.value(forKey: "iD") as! String != self.myiddddddd {
let person = NSEntityDescription.insertNewObject(forEntityName: "Mydata",
person.setValue(nameFunc, forKey: "name")
person.setValue(imageArray, forKeyPath: "profimg")
person.setValue(postImageArray, forKeyPath: "mid_img")
person.setValue(myUId, forKey: "iD")
do{
try (context.save())
DispatchQueue.main.async(execute: { () -> Void in
self.myTable.reloadData()
})
}
catch
{
print(error)
}
catch {
}
}
} catch {
print("Error with request: \(error)")
}
}
}
"myiddddddd" is the newdata(post id ) from the json .Please help

Swift 3 Core Data

I am getting the following error while trying to do NSFetchRequest in Swift 3
Generic parameter 'ResultType' could not be inferred
i checked lots of links and i have not been able to figure how to solve it.
this is what am doing
ViewController.swift
func loadData(){
let request = NSFetchRequest(entityName: "Grocery") //Error occurs here(Generic parameter 'ResultType' could not be inferred)
do{
let results = try manageObjectContext.execute(request)
groceries = results as! [NSManagedObject]
tableView.reloadData()
}catch{
fatalError("Error is retriving Gorcery items")
}
}
Try this:
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Grocery")
It should work :)
Therefore your code should Look like:
func loadData(){
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Grocery")
do{
let results = try manageObjectContext.execute(request)
groceries = results as! [NSManagedObject]
tableView.reloadData()
}catch{
fatalError("Error is retriving Gorcery items")
}
}
Credits: Nitesh Patil https://medium.com/#imnitpa/swift-3-core-data-7b00b50f5782
import UIKit
import CoreData
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let appDelegate = UIApplication.shared.delegate as!AppDelegate
let context = appDelegate.persistentContainer.viewContext
//adding new user and saving in database...
let newUser = NSEntityDescription.insertNewObject(forEntityName: "Users", into: context)
newUser.setValue("Batman", forKey: "username")
newUser.setValue("Robin", forKey: "password")
do {
try context.save()
print("saved")
} catch {
print("Error occured...")
}
// restoring data back from database
let request = NSFetchRequest < NSFetchRequestResult > (entityName: "Users")
request.returnsObjectsAsFaults = false
do {
let results =
try context.fetch(request)
if results.count > 0 {
for result in results as![NSManagedObject] {
if let username = result.value(forKey: "username") as ? String {
print(username)
}
}
}
else {
print("No results")
}
} catch {
print("Couldn't fetch results")
}
}
}
you can use this too:
let request : NSFetchRequest<Grocery> = Grocery.fetchRequest()
and you get results of the type [Grocery] when you execute the request:
let results : [Grocery] = try manageObjectContext.execute(request)
or
let results = try manageObjectContext.execute(request)

Resources