Failed to call designated initializer on NSManagedObject - ios

I have a NSManagedObject class which I'm using to save and retrieve from core-data.
class Nickname: NSManagedObject {
func saveData(addressDetails:[String:AnyObject]){
var isDataSaved = false
let address = NSEntityDescription.insertNewObjectForEntityForName("Nickname",
inManagedObjectContext: appDelegate.managedObjectContext) as! Nickname
if let addressString = addressDetails["address"] as? String{
if let addressNickName = addressDetails["nickname"] as? String{
if let addressID = addressDetails["id"] as? String{
address.address = addressString
address.nickname = addressNickName
address.addressID = addressID
do{
try appDelegate.managedObjectContext.save()
isDataSaved = true
}
catch{
print(error)
}
}
}
}
if !isDataSaved{
NSLog("Data not saved in core data. Check Nickname ManagedObject class")
}
}
func getData(nickName:String)->String?{
let fetchRequest = NSFetchRequest(entityName: "Nickname")
fetchRequest.predicate = NSPredicate(format: "nickname = %#", nickName)
fetchRequest.returnsObjectsAsFaults = false
var result : [AnyObject]!
do{ result = try appDelegate.managedObjectContext.executeFetchRequest(fetchRequest)}
catch { print(error)}
let requiredData = result[0] as? Nickname
return requiredData != nil ? (requiredData!.address) : (nil)
}
}
In the viewController that I'm performing save and fetch I'm creating an object like this
let table = Nickname()
let params = ["address":"aasdadad","nickname":"asdadadadad","id":"69"]
table.saveData(params)
let data = table.getData("asdadadadad")
print(data)
But I keep getting the error
CoreData: error: Failed to call designated initializer on NSManagedObject class 'XXXX.Nickname'
I understand that it is happening because I haven't created an object in the right way. I have been searching for a while and haven't been able to find a solution.
NOTE: There are many questions in SO regarding the same error. I tried all the suggested solutions and still haven't found a way to fix this.

Related

How can you get attribute names from an Entity from CoreData at an iOS app

I am reading data with following code from CoreData but instead of that can we read first attribute names "firstName", "lastName", "age" from CoreData into an array and read their values instead of writing all the names in code.
It is repeated work because they are written in DataModel as well.
loadData() {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Entity")
do {
let result = try context.fetch(fetchRequest)
dump(result)
for data in result as! [NSManagedObject] {
fNames = data.value(forKey: "firstName") as! String
lNames = data.value(forKey: "lastName") as! String
age = data.value(forKey: "age") as! Int
print("first \(fNames), last : \(lNames), last : \(age)")
}
} catch {
print("Could not load data: \(error.localizedDescription)")
}
}
Use the class that Xcode has generated for you that has the same name as the entity name
loadData() {
//Declare fetch request to hold the class you want to fetch
let fetchRequest = NSFetchRequest<Entity>(entityName: "Entity")
do {
let result = try context.fetch(fetchRequest)
dump(result)
for data in result {
// result is now [Entity] so you can access properties directly
// and without casting
let firstName = data.firstName
let lastName = data.lastName
let age = data.age
print("first \(firstName), last : \(lastName), age : \(age)")
}
} catch let error as NSError {
print("Could not load data: \(error.localizedDescription)")
}
}
Try this, access your entity name from NSManagedObject
e.g.
For AppDelegate.SharedInstance(), just declare this func in AppDelegate.swift
class func sharedInstance() -> AppDelegate
{
return UIApplication.shared.delegate as! AppDelegate
}
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName:"CallHistory") //Here CallHistory is my entity name which i can access as NSManagedObject
let arr_callhistory = try AppDelegate.sharedInstance().persistentContainer.viewContext.fetch(fetchRequest) as! [CallHistory]
if arr_callhistory.count != 0
{
for callhistory_dict in arr_callhistory
{
let callStatus = callhistory_dict.callStatus
}
}

Core Data Value returns Nil

Hi i have been working with core data to store and retrieve some values(String only) from the core data. Here is how i am storing the values.
The Function :
public func saveStringValue(forKey: String, value: String) -> Bool{
var saved = false
if self.entityName != nil && self.appDelegate != nil{
let context = appDelegate?.persistentContainer.viewContext
if context != nil{
let entity = NSEntityDescription.entity(forEntityName: self.entityName!, in: context!)
let entityHandle = NSManagedObject(entity: entity!, insertInto: context!)
entityHandle.setValue(value, forKey: forKey)
do{
try context?.save()
saved = true
}catch let error as NSError{
saved = false
print("Error : \(error)")
}
}
}
return saved
}
Here is how i call it
let historyManager = HistoryManager(entity: "SearchHistory")
let titleInserted = historyManager.saveStringValue(forKey: "title", value: book.title!)
if(titleInserted == true)
{
print("Title Inserted to Entity")
}
if let image = book.imageUrl{
let imageInserted = historyManager.saveStringValue(forKey: "image", value: image)
if imageInserted == true{
print("Image Url Inserted to Entity")
}
}
I can see in the console printed that
Title inserted into entity
ImageInserted Into entity
Here is the code to retrieve the value from core data store
public func fetchAll() -> [Book]{
var books = [Book]()
let context = self.appDelegate?.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: self.entityName!)
//let fetchRequest: NSFetchRequest<SearchHistory> = SearchHistory.fetchRequest()
do{
let fetchedBooks = try context?.fetch(fetchRequest)
for aBook in fetchedBooks!{
if let title = aBook.value(forKey: "title"){
let book = Book(title: title as! String)
if let im = aBook.value(forKey: "image"){
book.imageUrl = im as! String
print("ImageUrl : \(im) : ")
}
else{
print("No Value for key : image")
}
books.append(book)
}
}
}
catch let error as NSError{
print("Fetch Error: \(error.localizedDescription)")
}
print("Books : \(books.count)")
return books
}
But when i run the code to retrieve the book imageUrl it returns nil and prints
No value for key : image
It retrieves the title but not the imageUrl.
Can you help me through this problem or point me to the right direction. And please do post the reason why i was getting this problem and how to solve it. Thanks.
Your problem is that your saveStringValue creates a new NSManagedObject instance each time you call it.
The first time you call saveStringValue you will create a SearchHistory object that has a title but no image. The second time you call it you will create another SearchHistory object with an image value but no title.
In my opinion, your saveStringValue function is unnecessary. Assuming your code is based on a template that resulted from clicking "use Core Data" in Xcode, you will have a SearchHistory class available and you can use something like this:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let newHistory = SearchHistory(context: context)
newHistory.title = book.title
newHistory.image = book.imageUrl
appDelegate.saveContext()

Update core data object swift 3

I want to update a core data object in swift 3. After some googled I didn't found anything about swift 3.
So my question is: how can I update a core data object in swift 3?
Fetch the existing values using a fetch request with a predicate. Use a unique value in the predicate. Once you've fetched the object, update the object with new values and save the context.
let empId = "001"
let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "EmpDetails")
let predicate = NSPredicate(format: "empId = '\(empId)'")
fetchRequest.predicate = predicate
do {
let result = try persistentContainer.viewContext.fetch(fetchRequest)
if let objectToUpdate = result.first as? NSManagedObject {
objectToUpdate.setValue("newName", forKey: "name")
objectToUpdate.setValue("newDepartment", forKey: "department")
objectToUpdate.setValue("001", forKey: "empID")
try persistentContainer.viewContext.save()
}
} catch {
print(error)
}
Using NSManagedObject subclass
let empId = "001"
let fetchRequest: NSFetchRequest<Employee> = Employee.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "%K = %#", #keyPath(Employee.id), empId)
do {
let results = try persistentContainer.viewContext.fetch(fetchRequest)
if let employee = results.first {
employee.name = "new name"
employee.department = "new department"
}
try persistentContainer.viewContext.save()
} catch let error as NSError {
print(error.localizedDescription)
}
Batch updates
Batch updates help to update multiple Core Data objects without having
to fetch anything into memory.
let batchUpdate = NSBatchUpdateRequest(entityName: "Employee")
batchUpdate.propertiesToUpdate = [#keyPath(Employee.isActive): true]
batchUpdate.affectedStores = persistentContainer.viewContext.persistentStoreCoordinator?.persistentStores
batchUpdate.resultType = .updatedObjectsCountResultType
do {
let batchResult = try coreDataStack.managedContext.execute(batchUpdate) as? NSBatchUpdateResult
print(batchResult?.result)
} catch let error as NSError {
print(error.localizedDescription)
}
Pass unique id in variable "id"(Unique variable created in Core data model) and all the variable as you want to update values:
func context() -> NSManagedObjectContext {
let context=(UIApplication.shared.delegate as!AppDelegate).persistentContainer.viewContext
return context
}
func save() {
(UIApplication.shared.delegate as! AppDelegate).saveContext()
}
func UpdateCartByTestId(id:Int64,name:String) {
let fetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Update")
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.predicate = NSPredicate(format:"id == %d",id)
let result = try? context().fetch(fetchRequest)
if result?.count == 1 {
let dic = result![0]
dic.setValue(id, forKey: "id")
dic.setValue(name, forKey: "name")
save()
}
}

Syncing parse data with coredata, how do I figure out which objects to delete?

I have successfully written the code to download the parse objects, fetch the current objects in my database and then compare.
My algorithm:
I iterate through the parse objects and run a fetchrequest and compare their objectID's. If I get nothing, I make a new object for my database. Otherwise I then look at the modifiedDate I have in my database and the updatedAt from parse and compare to see if I need to set new values. This code works great.
The code:
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
if let objects = objects {
for object in objects {
let object = object as! PFObject
let name = object["name"] as! String
let email = object["email"] as! String
let subjectsTaught = object["subjectsTaught"] as [String: String]
let category = object["category"] as! String
let uniqueID = object.objectId!
let modifiedDate = object.updatedAt!
let fetchRequest2 = NSFetchRequest(entityName: "Teacher")
fetchRequest2.predicate = NSPredicate(format: "uniqueID == %#", uniqueID)
var error2: NSError?
if let foundTeachers = self.managedObjectContext.executeFetchRequest(fetchRequest2, error: &error2) as? [Teacher] {
if foundTeachers.isEmpty == true {
let teacher = NSEntityDescription.insertNewObjectForEntityForName("Teacher", inManagedObjectContext: self.managedObjectContext) as! Teacher
teacher.name = name
teacher.email = email
teacher.subjectsTaught = subjectsTaught
teacher.category = category
teacher.uniqueID = uniqueID
teacher.modifiedDate = modifiedDate
} else {
if let teacher = foundTeachers.first {
let date1 = teacher.modifiedDate
let date2 = modifiedDate
let compareResult = date1.compare(date2)
if compareResult == NSComparisonResult.OrderedAscending {
teacher.setValue(name, forKey: "name")
teacher.setValue(email, forKey: "email")
teacher.setValue(subjectsTaught, forKey: "subjectsTaught")
teacher.setValue(category, forKey: "category")
teacher.setValue(modifiedDate, forKey: "modifiedDate")
}
}
}
}
var error: NSError?
if !self.managedObjectContext.save(&error) {
println("Error \(error)")
abort()
}
}
}
My question is how should I figure out which objects where not in parse? I don't want to query parse for every object in my database as I assume that would be network intensive.
Should I do a fetchrequest for all Teacher objects in the beginning and as I iterate through the parse objects, delete them as I go? If I have objects left, those should be deleted?
Okay, I figured out what to do. I ended up running a fetchrequest first for all teacher and appending their names to an array. During the parse iteration, I deleted teachers from that list as I went through them and at the end, used that list to delete teachers from the database.
let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName("Teacher", inManagedObjectContext: self.managedObjectContext)
var error: NSError?
var foundTeacherNames = [String]()
if let foundTeachers = self.managedObjectContext.executeFetchRequest(fetchRequest, error: &error) as? [Teacher] {
for teacher in foundTeachers {
foundTeacherNames.append(teacher.name)
}
}
//Find teachers in parse database
let query = PFQuery(className: "TeacherList")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
if let objects = objects {
for object in objects {
let object = object as! PFObject
let name = object["name"] as! String
let email = object["email"] as! String
let subjectsTaught = object["subjectsTaught"] as! [String: String]
let category = object["category"] as! String
let uniqueID = object.objectId!
let modifiedDate = object.updatedAt!
let fetchRequest2 = NSFetchRequest(entityName: "Teacher")
fetchRequest2.predicate = NSPredicate(format: "uniqueID == %#", uniqueID)
var error2: NSError?
if let foundTeachers = self.managedObjectContext.executeFetchRequest(fetchRequest2, error: &error2) as? [Teacher] {
if foundTeachers.isEmpty == true {
let teacher = NSEntityDescription.insertNewObjectForEntityForName("Teacher", inManagedObjectContext: self.managedObjectContext) as! Teacher
teacher.name = name
teacher.email = email
teacher.subjectsTaught = subjectsTaught
teacher.category = category
teacher.uniqueID = uniqueID
teacher.modifiedDate = modifiedDate
} else {
if let teacher = foundTeachers.first {
let date1 = teacher.modifiedDate
let date2 = modifiedDate
let compareResult = date1.compare(date2)
if compareResult == NSComparisonResult.OrderedAscending {
teacher.setValue(name, forKey: "name")
teacher.setValue(email, forKey: "email")
teacher.setValue(subjectsTaught, forKey: "subjectsTaught")
teacher.setValue(category, forKey: "category")
teacher.setValue(modifiedDate, forKey: "modifiedDate")
}
}
}
if contains(foundTeacherNames, name) {
let i = find(foundTeacherNames, name)!
foundTeacherNames.removeAtIndex(i)
}
}
var error: NSError?
if !self.managedObjectContext.save(&error) {
println("Error \(error)")
abort()
}
if !foundTeacherNames.isEmpty {
for teacher in foundTeacherNames {
let request = NSFetchRequest(entityName: "Teacher")
request.predicate = NSPredicate(format: "name = %#", teacher)
if let fetchResults = self.managedObjectContext.executeFetchRequest(request, error: nil) as? [NSManagedObject] {
if fetchResults.count != 0 {
self.managedObjectContext.deleteObject(fetchResults[0])
}
}
}
}
Yes, the best way is to fetch all entities and then check for the unique ids. You could use key-value-coding (or its Swift equivalents such as map) to just get the ids you are interested in.
let existingIDs = entitiesFromParse.map() { $0.uniqueID as? String }
You can then check if an ID exists with
let idExists = existingIDs.contains(idToCheck)
This is preferable to multiple fetch requests which are expensive.

How to save and fetch one Object from Database in Swift?

I have a method which will save Object into Database. As per logic, whenever there is internet, it will save object by downloading from server.The method is as follows.
func saveConfiguration (config : ConfigDao){
let entity = NSEntityDescription.entityForName("AppConfig", inManagedObjectContext:self.del.managedObjectContext!)
let configurationContext = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: self.del.managedObjectContext!) as AppConfig
configurationContext.categoriesVer = Int32(config.categoriesVer)
configurationContext.fireBallIp = config.fireBallIP
configurationContext.fireBallPort = Int32(config.fireBallPort)
configurationContext.isAppManagerAvailable = config.isAppManagerAvailable
configurationContext.isFireBallAvailable = config.isFireballAvailable
configurationContext.timePerQuestion = config.timePerQuestion
}
Issue is that this adds all objects, and doesn't replace it, so my first query is
"How to add only one object in DB and replace when next Object comes?"
I also want to retrieve the same object, the only one object, normally in array, I do fetch last index, but how can I save only one and fetch same in DB.
func fetchAppConfig() -> AppConfig {
var fetchRequest = NSFetchRequest (entityName: "AppConfig")
var error : NSError?
let fetchResults = del.managedObjectContext?.executeFetchRequest(fetchRequest, error: &error) as [NSManagedObject]
if error != nil {
println ("Error \(error)")
}
return fetchResults
}
Thanks.
You can do as following.
//For storing only one object,
func saveConfiguration (config : ConfigDao){
let entity = NSEntityDescription.entityForName("AppConfig", inManagedObjectContext:self.del.managedObjectContext!)
var fetchRequest = NSFetchRequest (entityName: "AppConfig")
if let fetchResults = del.managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [AppConfig] {
for ob: AppConfig in fetchResults
{
println("Delete object,,, ")
self.del.managedObjectContext?.deleteObject(ob as AppConfig)
}
self.del.saveContext()
}
let configurationContext = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: self.del.managedObjectContext!) as AppConfig
configurationContext.categoriesVer = Int32(config.categoriesVer)
configurationContext.fireBallIp = config.fireBallIP
configurationContext.fireBallPort = Int32(config.fireBallPort)
configurationContext.isAppManagerAvailable = config.isAppManagerAvailable
configurationContext.isFireBallAvailable = config.isFireballAvailable
configurationContext.timePerQuestion = config.timePerQuestion
del.saveContext()
}// end of the
//To fetch object at zero index. which will be only one, still you can use 0 index to be sure.
func fetchAppConfig() -> AppConfig {
var appConfig:AppConfig = AppConfig()
var fetchRequest = NSFetchRequest (entityName: "AppConfig")
var error : NSError?
if let fetchResults = del.managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [AppConfig] {
if error != nil {
println ("Error \(error)")
}
appConfig = fetchResults[0]
}
return appConfig
}

Resources