Batch Update request puts NSNumber fields to nil [Swift] - ios

I'm working with web app. I have UITableViewController which deals with NSFetchedResultsController. I store tableView's objects through CoreData. When a user refreshes UI I perform server request and then call batch update for each new entity, my CoreData class looks like:
extension DBOrder {
#NSManaged var comment: String
#NSManaged var date: NSNumber
#NSManaged var id: NSNumber
#NSManaged var maturity_date: NSNumber
#NSManaged var number_of_tasks: NSNumber
#NSManaged var price: NSNumber
#NSManaged var status: String
#NSManaged var subject: String
#NSManaged var taskImages: [String]
#NSManaged var theme: String
}
"id" is unique for each object. "propertiesToUpdate" consists of some fields like "maturity_date": 1470427641000, "status": "some status" and etc. "entityName" is "DBOrder". And privateContext is NSManagedObjectContext type to update entities in background
func updateCoreData(id: NSNumber, entityName: String, propertiesToUpdate: [String: AnyObject], privateContext: NSManagedObjectContext) -> Bool {
let batchRequest = NSBatchUpdateRequest(entityName: entityName)
batchRequest.predicate = NSPredicate(format: "id == %#", id)
if !doesOrderExists(entityName, id: id, context: privateContext) {
return false
}
batchRequest.propertiesToUpdate = propertiesToUpdate
batchRequest.resultType = .UpdatedObjectIDsResultType
do {
let res = try privateContext.executeRequest(batchRequest) as! NSBatchUpdateResult
let orderIDs = res.result as! [NSManagedObjectID]
return (orderIDs.count != 0) ? true : false
} catch {
print(error)
}
return false
}
This function is called for each object that has been loaded from server. If object is already existed then I update it else create the new one.
Finally, the problem: when I use batch update it works incorrect with NSNumber. It always puts NSNumber fields to nil and works as it should with String fields. So, what I'm doing wrong?

Related

returnsObjectsAsFaults not working as expected

I created a Core Data object as follows:
#objc(Gates)
public class Gates : NSManagedObject {
public class func getFetchRequest() -> NSFetchRequest<Gates> {
let request = NSFetchRequest<Gates>(entityName: "Gates")
request.returnsObjectsAsFaults = false
return request
}
#NSManaged var updatedAt: String
#NSManaged var objectId: String
#NSManaged var identifier: String
#NSManaged var name: String
#NSManaged var address: String
#NSManaged var dueDate: String
#NSManaged var productionCode: String
#NSManaged var locationCountry: String
#NSManaged var locationCity: String
#NSManaged var locationBuilding: String
#NSManaged var locationLevel: String
#NSManaged var locationRoom: String
#NSManaged var locationRange: String
#NSManaged var isFavorite: Bool
public func setGateData(gateDict: [String: Any]) {
updatedAt = gateDict["updatedAt"] as? String ?? ""
objectId = gateDict["objectId"] as? String ?? ""
identifier = gateDict["identifier"] as? String ?? ""
name = gateDict["name"] as? String ?? ""
isFavorite = gateDict["isFavorite"] as? Bool ?? false
address = gateDict["address"] as? String ?? ""
dueDate = gateDict["dueDate"] as? String ?? ""
productionCode = gateDict["productionCode"] as? String ?? ""
locationCountry = gateDict["locationCountry"] as? String ?? ""
locationCity = gateDict["locationCity"] as? String ?? ""
locationBuilding = gateDict["locationBuilding"] as? String ?? ""
locationLevel = gateDict["locationLevel"] as? String ?? ""
locationRoom = gateDict["locationRoom"] as? String ?? ""
locationRange = gateDict["locationRange"] as? String ?? ""
}
}
I also set this up in the xcdatamodeld:
Now, after I have saved the object in core data and I'm using the getFetchRequest() method that is part of the class which sets
request.returnsObjectsAsFaults = false on the request but I still getting the following result when I try to print the fetched objects:
<Gates: 0x60c0000959a0> (entity: Gates; id: 0xd000000005e40000 <x-
coredata://B9C33A5D-BF96-433A-9186-F51AA253F488/Gates/p377> ; data: <fault>)
As you can see in this case the data is still data: <fault>.
Why is the object parameters are not retrieved even though I set request.returnsObjectsAsFaults = false? What am I missing?
I'm having this issue and I found in my case instead of using the objects value in line, I initialize a variable with it first and then use that variable.
I would love to know if this is a Core Data bug or if I'm doing something wrong.
public class Person: NSManagedObject, Identifiable {
#NSManaged public var firstName: String
#NSManaged public var lastName: String
#NSManaged public var emailAddress: String
}
This does not work all the time:
CoreDataManager.shared.persistentContainer.performBackgroundTask{ context in
context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
do {
let email = "123Tester#gmail.com"
let request = Person.getPersonWith(email: email)
request.returnsObjectsAsFaults = false //DOES NOT WORK
if let person = try context.fetch(request).first{
print(person.fullName)
}
} catch{
fatalError()
}
}
However this does
CoreDataManager.shared.persistentContainer.performBackgroundTask{ context in
context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
do {
let email = "123Tester#gmail.com"
let request = Person.getPersonWith(email: email)
if let person = try context.fetch(request).first{
let fullName = person.fullName
print(fullName)
}
} catch{
fatalError()
}
}

Core data Relationship in swift

I am working on iPhone application where I need to implement one to many relationship from one entity to another entity.I have export NSManagedObject subclass from coredata database but could not find relation accessors in core data model file like Objective-C.
Although I'm able to set data in relationship NSSet but this only persist while iPhone app is running. Once I kill and restart the application I didn't get entity relationship for in fetch request from core data.
I am not sure what I am doing wrong. It will be great if anyone can tell me how to set data in NSSet relationship object. Any sample example will be a great help
Here is my core data model files. One folder can content multiple content as folder detail
extension FolderContent {
#NSManaged var contentID: NSNumber?
#NSManaged var contentTitle: String?
#NSManaged var contentType: String?
#NSManaged var publishDate: String?
#NSManaged var folderList: NSSet?
}
extension FolderList {
#NSManaged var folderID: NSNumber?
#NSManaged var folderName: String?
#NSManaged var folderDetail: NSSet?
}
func updateFolderList()
{
// Initialize Fetch Request
let fetchRequest = NSFetchRequest()
// Create Entity Description
let entityDescription = NSEntityDescription.entityForName(FOLDER_LIST, inManagedObjectContext: self.managedObjectContext)
// Configure Fetch Request
fetchRequest.entity = entityDescription
do {
let result = try self.managedObjectContext.executeFetchRequest(fetchRequest).last as! FolderList
let content = result.mutableSetValueForKey("folderDetail")
content.addObject(self.getContent())
var folderContent:FolderContent = result.folderDetail?.allObjects.first as! FolderContent
print(folderContent.contentTitle)
self.save()
print(result)
} catch {
let fetchError = error as NSError
print(fetchError)
}
}
func getContent()->FolderContent
{
let folderContent = NSEntityDescription.insertNewObjectForEntityForName(FOLDER_CONTENT, inManagedObjectContext: self.managedObjectContext) as! FolderContent
folderContent.contentID = 1
folderContent.contentTitle = "Sandeep"
folderContent.contentType = "Product"
return folderContent
}
If the relationship of FolderContent and FolderList is defined as
A FolderContent have many FolderList(s)
A FolderList only belongs to a FolderContent
FolderContent
extension FolderContent {
#NSManaged var contentID: NSNumber?
#NSManaged var contentTitle: String?
#NSManaged var contentType: String?
#NSManaged var publishDate: String?
#NSManaged var folderList: Set<FolderList>?
}
FolderList
extension FolderList {
#NSManaged var folderID: NSNumber?
#NSManaged var folderName: String?
#NSManaged var folderDetail: FolderContent?
}
Let say you want to persist the record and its relationship
func persistRecords() {
// Insert the new records
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let folderContentEntity = NSEntityDescription.entityForName("FolderContent", inManagedObjectContext: managedContext)
let folderListEntity = NSEntityDescription.entityForName("FolderList", inManagedObjectContext: managedContext)
//Create FolderContent record
let folderContentObject = FolderContent(entity: folderContentEntity!, insertIntoManagedObjectContext: managedContext)
folderContentObject.setValue(CONTENTID, forKeyPath: "contentID")
...
//Create FolderList record
let folderListObject = FolderList(entity: folderListEntity!, insertIntoManagedObjectContext: managedContext)
folderListObject.setValue(FOLDERID, forKeyPath: "folderID")
...
//Set relationship here
folderListObject.folderDetail = folderContentObject
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
I am guessing you did something like folder.folderDetail.addObject. With core data that won't work because core data behind the scenes is doing lots of things to maintain graph integrity. You have to use mutableSetValueForKey to get the set and have core data work its magic.

Converting NSSet to NSMutableArray

Currently I have a subclass of NSManaged object called Folder with property called item that is of type NSSet.
class Folder: NSManagedObject {
#NSManaged var title: String
#NSManaged var date: NSDate
#NSManaged var item: NSSet
func itemMutableArray() -> NSMutableArray {
var mutableArray: NSMutableArray!
mutableArray = [item.allObjects]
return mutableArray
}
Item class:
class Item: NSManagedObject {
#NSManaged var title: String
#NSManaged var date: NSDate
#NSManaged var completed: Bool
Does anybody have any suggestions with where I am currently going wrong ?
Here is my previous function I was using which now I want to convert my NSSet to an NSMutableArray.
func itemArray() -> [Item] {
let sortDescriptor = NSSortDescriptor(key: "date", ascending: true)
return item.sortedArrayUsingDescriptors([sortDescriptor]) as! [Item]
}
func itemMutableArray() -> NSMutableArray {
return NSMutableArray(array: (item.allObjects as! [Item]).sorted{ $0.date.compare($1.date) == NSComparisonResult.OrderedAscending } )
}
use Swift types, they are much more versatile
This puts the NSSet into an Array (var is mutable) and sorts it ascending by property date
#NSManaged var title: String
#NSManaged var date: NSDate
#NSManaged var item: NSSet
var sortedItemArray : Array<Item> {
var array = item.allObjects as! [Item]
return sorted(array) {$0.date < $1.date }
}

Swift core data removing entries in one to many relationships

I'm using Xcode 6.4 and Swift in iOS 8.4 project
I have a model with one to many relationship
class Account: NSManagedObject {
#NSManaged var userId: Int
#NSManaged var accessToken: String?
#NSManaged var userName: String
#NSManaged var sex: String
#NSManaged var email: String
#NSManaged var avatarUrl: String?
#NSManaged var friends: NSSet
}
class User: NSManagedObject {
#NSManaged var id: Int
#NSManaged var userName: String
#NSManaged var sex: String
#NSManaged var email: String
#NSManaged var avatarUrl: String
}
When I'm trying to remove all friends it doesn't work:
extension Account {
func replaceFriends(friends:[User]) {
var friendsList = self.mutableSetValueForKey("friends")
friendsList.removeAllObjects()
friendsList.addObjectsFromArray(friends)
}
func getFriends() -> [User] {
return self.mutableSetValueForKey("friends").allObjects as! [User]
}
}
class AccountDao: BaseDao {
private class func findAccount() -> Account? {
if let result = moc.executeFetchRequest(NSFetchRequest(entityName: "Account"), error: nil) as? [Account] {
if (!result.isEmpty) {
return result[0]
}
}
return nil
}
class func getAccount() -> Account {
return findAccount() ??
NSEntityDescription.insertNewObjectForEntityForName("Account", inManagedObjectContext: moc) as! Account;
}
}
During saving I'm setting empty array:
class func saveObjectContext() -> Bool {
var error: NSError?
var account = AccountDao.getAccount()
account.replaceFriends([User]())
if !moc.save(&error) {
self.log.error("Error during saving context, \(error)")
return false
} else {
println("Count in log \(AccountDao.getAccount().getFriends().count)")
self.log.info("Info save context \(error)")
return true
}
}
In log it returns 0:
Count in log 0
But in ControllerView still I'm getting AccountDao.getAccount().getFriends().count == 1. Method replaceFriends works only first time when I fetch and save data. And it save changes for simple properties like userName but not for friends. Do you have any ideas?
I found that in relation one to many Account -> User without inverse relationship in User, after changing list in Account every user in list have to be marked that has changed (property updated == true). But it always was set to false.
I added additional property to User, and after every change of list I had to change this property like user.toUpdate = true. After this change everything is working fine.

Core Data with Swift two entities

I am trying to use Core Data to save some of my application data. I have following classes. Basically I want to store the properties of each job, and use it later on.
Following is the class I currently use in my application.
class Job {
var name:String?
var count = 1
var id:String
var startDate:NSDate?
var finishDate:NSDate?
var expected:NSDate?
var detail:Array<JobDetail> = []
var isFinished:Bool?
var sender:String?
var receiver:String?
init(name:String?, id:String) {
self.name = name
self.id = id
self.isFinished = false
self.startDate = NSDate()
}
func addDetail (message:String?, date:NSDate?, location:String?, status: DetailStatus) {
detail.append(JobDetail(message: message, date: date, location: location, status: status))
if status == DetailStatus.OK {
self.isFinished = true
self.finishDate = date
}
}
}
enum DetailStatus {
case OK
case Error
case Exception
case Unknown
}
class JobDetail {
var message:String?
var date:NSDate?
var location:String?
var status:DetailStatus
init(message:String?, date:NSDate?, location:String?, status: DetailStatus) {
self.message = message
self.date = date
self.location = location
self.status = status
}
}
NSManagedObject sub class I created with Xcode after I create the data model.
class Job: NSManagedObject {
#NSManaged var name: String
#NSManaged var count: NSNumber
#NSManaged var id: String
#NSManaged var startDate: NSDate
#NSManaged var finishDate: NSDate
#NSManaged var expected: NSDate
#NSManaged var isFinished: NSNumber
#NSManaged var sender: String
#NSManaged var receiver: String
#NSManaged var details: NSSet
}
class JobDetail: NSManagedObject {
#NSManaged var message: String
#NSManaged var date: NSDate
#NSManaged var location: String
#NSManaged var status: NSNumber
#NSManaged var parent: Job
}
Here are the screenshots of my data model.
Basically I want to CRUD Job in my application so that I can show them in my tableview. I have everything setup, but because I couldn’t setup Core Data I don’t have persistence. I will appreciate if you can help me to setup Core Data.
Refer this. May be it's useful to you...
http://www.raywenderlich.com/85578/first-core-data-app-using-swift
It seems from the screenshots that your setup is correct. Link details with jobs like this.
detail1.parent = job
detail2.parent = job
context.save(nil)
Get all details for a job like this
job.details
This is unordered, but you can sort them using sortedArrayUsingDescriptors.
let sortedDetails = job.details.sortedArrayUsingDescriptors(
[NSSortDescriptor(key:"date" ascending: false)])

Resources