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

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
}

Related

Failed to call designated initializer on NSManagedObject

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.

iOS Optional wrapping in Swift

I'm saving an array into a model, when saving the data is not wrapped with Optional (...) however when the data is being read I get the Optional(...) wrapping around it.
Appreciate your help.
saveOperativesInModel: Test Name
saveOperativesInModel: test team 999
getOperativesFromModel: Optional(Test Name)
getOperativesFromModel: Optional(test team 999)
func saveOperativesInModel() {
if (self.operativesResult?.operativesList.count > 0) {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let entity = NSEntityDescription.entityForName("Operatives", inManagedObjectContext: managedContext)
// Remove all records in entity
let fetchRequest = NSFetchRequest()
fetchRequest.entity = entity
fetchRequest.includesPropertyValues = false
var error:NSError?
if let results = managedContext.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObject] {
for result in results {
managedContext.deleteObject(result)
}
}
var item: NSManagedObject!
//var itemInArray: Operative!
if let operativesList = self.operativesResult?.operativesList {
self.operativesTable.removeAll()
for itemInArray in operativesList {
item = NSManagedObject(entity: entity!, insertIntoManagedObjectContext:managedContext)
item.setValue(itemInArray.id, forKey: "id")
item.setValue(itemInArray.firstName, forKey: "firstName")
item.setValue(itemInArray.lastName, forKey: "lastName")
item.setValue(itemInArray.mobile, forKey: "mobile")
var error: NSError?
if !managedContext.save(&error) {
println("saveOperativesInModel - Could not save \(error), \(error?.userInfo)")
} else {
self.operativesTable.append(item!)
println("saveOperativesInModel: \(itemInArray.firstName)")
}
}
}
//println("saveOperativesInModel : \(operativesTable.count) items")
let fetchRequest1: NSFetchRequest! = NSFetchRequest(entityName:"Operatives")
var error1: NSError?
let fetchedResults = managedContext.executeFetchRequest(fetchRequest1, error: &error1) as? [NSManagedObject]
if let operativesTable = fetchedResults {
if operativesTable.count > 0 {
println("getOperativesFromModel : \(operativesTable.count)")
}
if operativesTable.count > 0 {
for item in operativesTable {
let operative: Operative! = Operative()
operative.id = String (stringInterpolationSegment: item.valueForKey("id"))
operative.firstName = String (stringInterpolationSegment: item.valueForKey("firstName"))
operative.lastName = String (stringInterpolationSegment: item.valueForKey("lastName"))
println("getOperativesFromModel: \(operative.firstName)")
}
}
} else {
println("Could not fetch \(error1), \(error1!.userInfo)")
}
}
}
The firstName attribute from Operative class should be an optional (probably declared like this var firstName: String?. So if you're sure you have a string inside firstName, you can replace your print line with this:
println("getOperativesFromModel: \(operative.firstName!)")

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 do I check if core data class is nil. in SWIFT

I have created an Entity called Status with an attribute called statusUpdate. I want the user to be able to create a status for the first time which would then be saved in core data, then when the status is next updated to save over the old status in core data. Here is what i've done:
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext!
let newUser = NSEntityDescription.insertNewObjectForEntityForName("Status", inManagedObjectContext: context) as NSManagedObject
var request = NSFetchRequest(entityName: "Status")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: &self.error)
if results?.count > 0 {
for result: AnyObject in results! {
if var oldStatus = result.valueForKey("statusUpdate") as? String {
println("Current User fetched status from Core Data")
var newStatus = self.updateStatusText.text
result.setValue(newStatus, forKey: "statusUpdate")
} else if result.valueForKey("statusUpdate") == nil {
var newStatus = self.updateStatusText.text
newUser.setValue(newStatus, forKey: "statusUpdate")
println("Status uploaded for the first time")
}
}
}
context.save(&self.error)
The problem is the else if seems to always run. I want it to basically only run of the Status entity is nil (so it will only run for the first time somebody updates a status) and then when the status is updated after that it will run just the regular if statement.
I'm not sure what i'm doing wrong???
Thanks in advance.
Line 3 of your code inserts a new Status object, so each time this code is executed, you are adding an extra object. The newly inserted object will have a statusUpdate of nil (most likely).
When you execute the fetch, in line 6, this new Status object will be included in the results, together with any objects you have saved previously. So when you iterate through all the results, there will always be one for which statusUpdate is nil.
I would restructure your code to do the fetch first, establish whether there are any results. If so, update the statusUpdate; if not, create the new object and set the statusUpdate field. Something like this:
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext!
var request = NSFetchRequest(entityName: "Status")
request.returnsObjectsAsFaults = false
var results = context.executeFetchRequest(request, error: &self.error)
if let resultsArray = results { // explicitly test for nil results
if resultsArray.count > 0 {
// count should be at most one, but just in case we will iterate through:
for result: AnyObject in resultsArray {
println("Current User fetched status from Core Data")
var newStatus = self.updateStatusText.text
result.setValue(newStatus, forKey: "statusUpdate")
}
else { // resultsArray is empty, so insert a new object
var newStatus = self.updateStatusText.text
let newUser = NSEntityDescription.insertNewObjectForEntityForName("Status", inManagedObjectContext: context) as NSManagedObject
newUser.setValue(newStatus, forKey: "statusUpdate")
println("Status uploaded for the first time")
}
} else { // fetch returned nil, so log an error:
println("Nil returned by fetch")
}
context.save(&self.error)
if (var oldStatus = result.valueForKey("statusUpdate") as? String) != nil{
println("Current User fetched status from Core Data")
var newStatus = self.updateStatusText.text
result.setValue(newStatus, forKey: "statusUpdate")
}else {
var newStatus = self.updateStatusText.text
newUser.setValue(newStatus, forKey: "statusUpdate")
println("Status uploaded for the first time")
}

How to update Objects in core date through NSManagedObjectContext in swift?

I get objects array from core data:
lazy var managedObjectContext : NSManagedObjectContext? = {
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
if let managedObjectContext = appDelegate.managedObjectContext {
return managedObjectContext
}
else {
return nil}
}()
var tests=[Test]()
func fetchLog() {
let fetchRequest = NSFetchRequest(entityName: "Test")
if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [Test] {
tests = fetchResults
}
And then tray to update it:
managedObjectContext?.updatedObjects(tests[atRow] as NSManagedObject)
But get error: '(NSManagedObject) -> $T5' is not identical to 'NSSet'
Whats wrong?
NSManagedObject is registered with NSManagedObjectContext(MOC). To update it, call MOC's save().
like this:
let aTest = test[atRow] as Test
// update through properties like aTest.id = 2
...
// and save
var error: NSError?
if managedObjectContext!.save(&error) {
println("saved successfully")
} else {
println("failed to save")
if let saveError = error {
println("error=\(saveError.localizedDescription)")
}
}

Resources