CoreData and Swift: contents gets swapped - swift

I am new to swift and have a problem with CoreData: I can save and load to and from my CoreData store. But when I load the saved data from CoreData the data has been rearranged. I don't want this to happen. How do I solve this?
My code is this:
Meetings.swift
import Foundation
import CoreData
class Meetings: NSManagedObject {
#NSManaged var meetingTimeLeft: NSNumber
#NSManaged var title: String
#NSManaged var date: NSDate
#NSManaged var timeFrom: NSDate
#NSManaged var timeTo: NSDate
#NSManaged var timeFromActual: NSDate
#NSManaged var timeToActual: NSDate
#NSManaged var location: String
#NSManaged var locationRoom: String
#NSManaged var meetingGoals: String
#NSManaged var purpose: String
#NSManaged var averageHourlyCost: NSNumber
#NSManaged var completed: NSNumber
#NSManaged var durationActual: NSNumber
#NSManaged var durationPlanned: NSNumber
#NSManaged var persons: NSSet
#NSManaged var agendas: NSSet
#NSManaged var minutes: NSSet
#NSManaged var relationship: NSManagedObject
#NSManaged var startSetting: StartSetting?
}
extension Meetings {
func addPer(value: Person) {
self.mutableSetValueForKey("persons").addObject(value)
}
func removePer(value: Person) {
self.mutableSetValueForKey("persons").removeObject(Person)
//self.mutableSetValueForKey("persons").addObject(value)
}
func getPer() -> [Person] {
var persons: [Person]
persons = self.persons.allObjects as! [Person]
return persons
}
func addAgenda(value: AgendaItem) {
self.mutableSetValueForKey("agendas").addObject(value)
}
func removeAgenda(value: AgendaItem) {
self.mutableSetValueForKey("agendas").removeObject(AgendaItem)
//self.mutableSetValueForKey("persons").addObject(value)
}
func getAgenda() -> [AgendaItem] {
var agendas: [AgendaItem]
agendas = self.agendas.allObjects as! [AgendaItem]
return agendas
}
func getAgendaItem(i: Int) -> AgendaItem {
var agendas: [AgendaItem]
agendas = self.agendas.allObjects as! [AgendaItem]
let agendaItem = agendas[i]
return agendaItem
}
}
person.swift
import Foundation
import CoreData
class Person: NSManagedObject {
#NSManaged var name: String
#NSManaged var email: String
#NSManaged var phone: NSNumber
#NSManaged var title: String
#NSManaged var company: String
#NSManaged var photo: NSData
#NSManaged var meeting: Meetings
}
AgendaItem.swift
import Foundation
import CoreData
class AgendaItem: NSManagedObject {
#NSManaged var agendaItemTitle: String
#NSManaged var presenter: String
#NSManaged var durationPlanned: NSNumber
#NSManaged var durationActual: NSNumber
#NSManaged var filePresentation: NSData
#NSManaged var fileSupporting: NSData
#NSManaged var agendaTimeStart: NSDate
#NSManaged var meeting: Meetings
}
when saving i do like this:
internal var meetingsModels = [Meetings]()
public let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
managedObjectContext?.deleteObject(meetingsModels[meetingsIndexPassed.row]) meetingsModels.removeAtIndex(meetingsIndexPassed.row)
let entityDescription = NSEntityDescription.entityForName("Meetings", inManagedObjectContext: managedObjectContext!)
let entityDescription2 = NSEntityDescription.entityForName("Person", inManagedObjectContext: managedObjectContext!)
let entityDescription3 = NSEntityDescription.entityForName("AgendaItem", inManagedObjectContext: managedObjectContext!)
let meetings = Meetings(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext)
meetings.title = meetingTitle_txt.text!
meetings.date = lDate
meetings.timeFrom = lStartTime
meetings.timeTo = lEndTime
meetings.location = address_txt.text!
meetings.purpose = details[0]
meetings.meetingGoals = details[1]
meetings.locationRoom = details[2]
meetings.durationPlanned = meetingsEditPassed.durationPlanned
meetings.completed = false
for var i = 0; i < personsEdit.count; i++ {
let person = Person(entity: entityDescription2!, insertIntoManagedObjectContext: managedObjectContext)
person.name = personsEdit[i].name
person.email = personsEdit[i].email
person.title = personsEdit[i].title
person.company = personsEdit[i].company
person.phone = personsEdit[i].phone
meetings.addPer(person)
}
for var i = 0; i < agendaEdit.count; i++ {
let agenda = AgendaItem(entity: entityDescription3!, insertIntoManagedObjectContext: managedObjectContext)
agenda.agendaItemTitle = agendaEdit[i].agendaItemTitle
//print(agendaEdit[i].agendaItemTitle)
agenda.presenter = agendaEdit[i].presenter
agenda.durationPlanned = agendaEdit[i].durationPlanned
meetings.addAgenda(agenda)
}
do {
try managedObjectContext?.save()
} catch let error1 as NSError {
print("\(error1)")
}
meetingsModels.append(meetings)
when loading i do this:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//1
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
//2
let fetchRequest = NSFetchRequest(entityName:"Meetings")
//fetchRequest.sortDescriptors = nil
//3
let fetchedResults: [Meetings]
do {
try
fetchedResults = (managedContext.executeFetchRequest(fetchRequest) as? [Meetings])!
if let results: [Meetings] = fetchedResults {
var temp = [Meetings]()
var temp2 = [Meetings]()
for tmp: Meetings in results {
temp.append(tmp)
if tmp.completed.boolValue {
temp2.append(tmp)
}else {
}
}
meetingsModels = temp
meetingsModels2 = temp2
} else {
}
} catch let error1 as NSError {
print("could not fetch meetings: \(error1), \(error1.userInfo)")
}
}
i hope that some of you can help me.

Your meetings data is being "rearranged" just because your Meetings don't belong to any ordered relationship. I'm not able to run your code on my side. However, I can recommend you one of the following:
Use ordered relations any time you want to make sure the order of
data is preserved any time you fetch it.
Use the
sortDescriptors property on your fetch requests. When you
create one, you can add a sort descriptor to it in order to
arrange data.

Related

How do I update tableview cell when It had changed

When I scroll up&down then tableview cell has set overtimes so It affect lagging.
I want to update only when the data is updated.
Post.swift (It is model)
import Foundation
import Parse
class Post: PFObject, PFSubclassing {
#NSManaged var postBy: PFUser?
#NSManaged var postUser: String?
#NSManaged var postUserImg: PFFile?
#NSManaged var postText: String?
#NSManaged var postImg: PFFile?
#NSManaged var sell: NSNumber?
#NSManaged var commentPointer: PFObject?
#NSManaged var commentBy: String?
#NSManaged var comment: String?
#NSManaged var liked: NSArray?
#NSManaged var likeCount: NSNumber?
#NSManaged var mention: NSArray?
#NSManaged var hashtag: NSArray?
static func parseClassName() -> String {
return "Post"
}
override class func query() -> PFQuery<PFObject>? {
let query = PFQuery(className: Post.parseClassName())
return query
}
}
extension Post: FeedCellSupport {
var username:String?{
return postUser
}
var userImg:PFFile?{
return postUserImg
}
var commentText:String?{
guard let commentTxt = comment else {
return ""
}
return commentTxt
}
TableView.swift
protocol FeedCellSupport {
var postDate: String? { get }
var postId: String? { get }
var postObj: PFObject? { get }
var img: PFFile? { get }
var username:String?{ get }
var userImg:PFFile?{ get }
var commentText:String?{ get }
var commentFrom:String?{ get }
var postText: String? { get }
var likes: Int? { get }
var isLiked: Bool? { get }
var isSell: Bool? { get }
var hashtags: NSArray? { get }
var mentions: NSArray? { get }
}
func fetchObjects() {
let postQuery = Post.query()?
.whereKeyExists("objectId")
.includeKeys(["postBy", "commentPointer", "commentPointer.commentBy", "commentBy"])
.order(byDescending: "createdAt")
as! PFQuery<Post>
postQuery.limit = self.page
postQuery.cachePolicy = .networkElseCache
postQuery.findObjectsInBackground { (object:[Post]?, error:Error?) in
if error == nil {
self.results = object!
}else {
print(error?.localizedDescription as Any)
}
self.tableView.reloadData()
self.refresher.endRefreshing()
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ShopDetailCell", for: indexPath) as! ShopDetailCell
if let object = results[indexPath.row] as? FeedCellSupport{
cell.postID = object.postObj!
cell.userNameLabel.text = object.username!
cell.userNameLabel.sizeToFit()
cell.descriptionLabel.text = object.postText!
cell.descriptionLabel.sizeToFit()
cell.commentByLabel.text = object.commentFrom!
cell.commentByLabel.sizeToFit()
cell.commentLabel.text = object.commentText!
cell.commentLabel.sizeToFit()
cell.delegate = self
}
return cell
}
How can I ensure that data is updated only when it changes?
What are your App functionalities? Your problem is associated with the way suitable to accomplish your task, that is: to update app contents.
So it is about business logic: when should the information be updated?
The solution to your problem is using user toggled events to refresh the contents or other possible solutions such as Application Delegate.
Except for these answers, the contents cannot update tableview automatically as your expectation.
Update
I considered this line of code is lagging:
if let object = results[indexPath.row] as? FeedCellSupport{
//other code
}
Because code inside these braces is dealing with data setting. You could try to mock up local data instead of this results array to say whether this is the problem.

How to execute a fetchRequest count for each object from array?

I have 2 Entities:
Attendace:
import Foundation
import CoreData
extension Attendance {
#NSManaged var date: NSDate?
#NSManaged var id: NSNumber?
#NSManaged var id_consultant: String?
#NSManaged var id_store: NSNumber?
#NSManaged var name: String?
#NSManaged var status: String?
}
and Product :
import Foundation
import CoreData
extension Product {
#NSManaged var id: String?
#NSManaged var name: String?
#NSManaged var preco: String?
#NSManaged var attendance: Attendance?
}
I'm fetching all Attendances and need to execute a fetch to count how many products are there in a Attendance:
There's my method to fetch the Attendances. How could I get the count of products with the Attendance ID?
func fetchAttendances() {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Attendance")
let attendancePredicate = NSPredicate(format: "id_store == \(Int(Helper.userDefaults().objectForKey(ConstantHelper.kIdStore)! as! NSNumber))")
fetchRequest.predicate = attendancePredicate
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
myAttendances = results as! [NSManagedObject]
self.collectionAttendances.reloadData()
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
}
Assuming that you have set up your data model so that Attendance and Product have a one-to-many relationship...
Attendance <---->> Product
... you can leverage the map syntax to accomplish this in a very succinct way:
let countArray = attendanceArray.map { ($0.id, $.products?.count ?? 0) }
This will return an array of tuples with the id and the count of products.

Core Data Fetch Relationship not working

I have a problem with fetching data from relationships.
I have a survey and i want to save the answers, the userIds and the results to core data and fetch the data at the next start of the app.
They are in arrays.
Each of it will be turned to a NSManagedObject "Wert", into the attribute "valueInt" if it is a result and "valueString" if it is an answer or an userId.
extension Wert {
#NSManaged var valueInt: NSNumber?
#NSManaged var valueString: String?
}
Afterwards it will be saved on the NSManagedObject "Message", as a NSSet on answers, results or userIds.
extension Message {
#NSManaged var absenderId: String?
#NSManaged var absenderName: String?
#NSManaged var datum: NSDate?
#NSManaged var gruppenId: String?
#NSManaged var image: NSData?
#NSManaged var latitude: NSNumber?
#NSManaged var longitude: NSNumber?
#NSManaged var messageId: String?
#NSManaged var question: String?
#NSManaged var sound: NSData?
#NSManaged var text: String?
#NSManaged var answers: NSSet?
#NSManaged var results: NSSet?
#NSManaged var userIDs: NSSet?
}
I think that this is working, because after the "addObjectsFromArray" AnswersSet contains some values.
func saveCoreData(AbsenderName: String, AbsenderID: String, Text: String?, Image: NSData?, Datum: NSDate, Latitude: Double?, Longitude: Double?, MessageId: String, Sound: NSData?, question: String?, answers : [String]?, results : [Int]?, userIDs: [String]?) {
let newMessage = NSEntityDescription.insertNewObjectForEntityForName("Message", inManagedObjectContext: context) as NSManagedObject
newMessage.setValue(AbsenderID, forKey: "absenderId")
newMessage.setValue(AbsenderName, forKey: "absenderName")
newMessage.setValue(Text, forKey: "text")
newMessage.setValue(Image, forKey: "image")
newMessage.setValue(Latitude, forKey: "latitude")
newMessage.setValue(Longitude, forKey: "longitude")
newMessage.setValue(Datum, forKey: "datum")
newMessage.setValue(GroupId, forKey: "gruppenId")
newMessage.setValue(MessageId, forKey: "messageId")
newMessage.setValue(Sound, forKey: "sound")
if question != nil && answers != nil && results != nil && userIDs != nil {
newMessage.setValue(question, forKey: "question")
var AnswersArray = [NSManagedObject]()
var ResultsArray = [NSManagedObject]()
var userIDsArray = [NSManagedObject]()
for var index = 0; index < answers?.count ; ++index {
let newWert = NSEntityDescription.insertNewObjectForEntityForName("Wert", inManagedObjectContext: context) as NSManagedObject
newWert.setValue(answers![index], forKey: "valueString")
AnswersArray.append(newWert)
}
for var index = 0; index < results?.count ; ++index {
let newWert = NSEntityDescription.insertNewObjectForEntityForName("Wert", inManagedObjectContext: context) as NSManagedObject
newWert.setValue(results![index], forKey: "valueInt")
ResultsArray.append(newWert)
}
for var index = 0; index < userIDs?.count ; ++index {
let newWert = NSEntityDescription.insertNewObjectForEntityForName("Wert", inManagedObjectContext: context) as NSManagedObject
newWert.setValue(userIDs![index], forKey: "valueString")
userIDsArray.append(newWert)
}
let answersSet = newMessage.mutableSetValueForKey("answers")
let resultsSet = newMessage.mutableSetValueForKey("results")
let userIdsSet = newMessage.mutableSetValueForKey("userIDs")
answersSet.addObjectsFromArray(AnswersArray)
resultsSet.addObjectsFromArray(ResultsArray)
userIdsSet.addObjectsFromArray(userIDsArray)
}
do {
try context.save()
}
catch _ {
print("Error")
}
}
But when i try to fetch the saved values with mutableSetValueForKey(), they contain 0 objects. Everything else is working.
func loadCoreData() -> Int {
var x : [AnyObject] = [AnyObject]()
let request = NSFetchRequest(entityName: "Message")
request.resultType = NSFetchRequestResultType.DictionaryResultType
request.predicate = NSPredicate(format: "gruppenId = %#", GroupId)
let sort1 = NSSortDescriptor(key: "datum", ascending: true)
request.sortDescriptors = [sort1]
do {
x = try context.executeFetchRequest(request)
for (var i = 0 ; i < x.count; ++i ) {
let Absender = x[i].valueForKey("absenderName") as! String
let AbsenderID = x[i].valueForKey("absenderId") as! String
let Text : String? = x[i].valueForKey("text") as? String
let Image : NSData? = x[i].valueForKey("image") as? NSData
let Sound : NSData? = x[i].valueForKey("sound") as? NSData
let Date : NSDate = x[i].valueForKey("datum") as! NSDate
let GruppenID : String = x[i].valueForKey("gruppenId") as! String
let MessageID : String = x[i].valueForKey("messageId") as! String
let longitude : Double? = x[i].valueForKey("longitude") as? Double
let latitude : Double? = x[i].valueForKey("latitude") as? Double
let question : String? = x[i].valueForKey("question") as? String
let answers = x[i].mutableSetValueForKey("answers") as? NSMutableSet
let results = x[i].mutableSetValueForKey("results") as? NSMutableSet
let userIds = x[i].mutableSetValueForKey("userIDs") as? NSMutableSet
}
}
}
It seems that the answers, results etc. are one-to-many relationships. In this case, it is always safer (and more concise) to simply set the to-one relationship.
Thus, rather than maintaining a clumsy array and dealing with mutableSetValueForKey, you could simply use the inverse relationship:
newWert.message = newMessage
NB: Please adopt the convention of using lowerCase variable names to avoid confusion with class names.
NB2: I strongly advise using NSManagedObject subclasses and using dot.notation to set and get values. With one stroke, all the variables you define in your many lines of code above would become utterly superfluous.

One-to-Many Relationship in Swift Core Data

I'm building a journal app where you create a FocusArea object, which then contains 30 journal Entry objects.
So I created a one-to-many relationship in my data model.
Then I implemented two NSManagedObject Classes:
#objc(FocusArea)
class FocusArea: NSManagedObject {
#NSManaged var focusName: String
#NSManaged var focusStart: NSDate
#NSManaged var focusEnd: NSDate
#NSManaged var completeFlag: Bool
//#NSManaged var entries: NSSet
var entries: NSMutableOrderedSet {
return self.mutableOrderedSetValueForKey("entries")
}
}
#objc(Entry)
class Entry: NSManagedObject {
#NSManaged var entryJournal: String
#NSManaged var entryDate: NSDate
#NSManaged var completeFlag: Bool
#NSManaged var entryStatus: Bool
#NSManaged var focus: FocusArea
}
I'm able to add a FocusArea with no problems, but when I try to create 30 Entry objects and store in FocusArea.entries, everything keeps crashing.
What's the correct way to add multiple Entry objects to the FocusArea object?
Am I way off here?
#IBAction func saveFocusArea(sender: AnyObject) {
if txtFocusName.text != nil {
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext!
//create new focus area
let entFocus = NSEntityDescription.entityForName("FocusArea", inManagedObjectContext: context)
let newFocus = FocusArea(entity:entFocus!, insertIntoManagedObjectContext: context)
newFocus.focusName = txtFocusName.text!
newFocus.focusStart = NSDate()
newFocus.focusEnd = NSDate().dateByAddingTimeInterval(60*60*24*30)
newFocus.completeFlag = false
//initialize 30 new entries into core data
let entEntry = NSEntityDescription.entityForName("Entry", inManagedObjectContext: context)
for var i = 0; i < 30; i++ {
let newEntry = Entry(entity:entEntry!, insertIntoManagedObjectContext: context)
let c = Double(i)
newEntry.entryDate = NSDate().dateByAddingTimeInterval(60*60*24*c)
newEntry.entryJournal = ""
newEntry.entryStatus = false
newEntry.completeFlag = false
//THIS IS THE PROBLEM AREA
newFocus.entries.append(newEntry)
}
do {
try context.save()
} catch {
print("There's a problem!")
}
}
}
I figured it out. All I had to do was:
newEntry.focus = newFocus.
So instead of setting the relationship between the parent to the child, set it from the child to the parent.

NSSet and Core Data

I am trying to save two lists of objects in core data. I have a protocol method which adds my tags as (Tag) and puts them into an array of tags. I am not able to save these objects as I am using an NSSet and the extension (for swift) as:
extension CurrentTransaction {
func addTagObject(value:Tag) {
var items = self.mutableSetValueForKey("tagMembers");
items.addObject(value)
}
func removeTagObject(value:Tag) {
var items = self.mutableSetValueForKey("tagMembers");
items.removeObject(value)
}
}
The two objects from core data I am trying to save are:
class Pic: NSManagedObject {
#NSManaged var picName: String!
#NSManaged var picNumber: String!
#NSManaged var currentTransaction: CurrentTransaction
class func createInManagedObjectContext(moc: NSManagedObjectContext, name: String, number: String) -> Pic {
let newPic = NSEntityDescription.insertNewObjectForEntityForName("Pic", inManagedObjectContext: moc) as! Pic
newPic.picName = name
newPic.picNumber = number
return newPic
}
}
class Tag:
class Tag: NSManagedObject {
#NSManaged var tagName: String!
#NSManaged var tagNumber: String!
#NSManaged var currentTransaction: CurrentTransaction
class func createInManagedObjectContext(moc: NSManagedObjectContext, name: String, number: String) -> Tag {
let newTag = NSEntityDescription.insertNewObjectForEntityForName("Tag", inManagedObjectContext: moc) as! Tag
newTag.tagName = name
newTag.tagNumber = number
return newTag
}
}
class currentTransaction:
class CurrentTransaction: NSManagedObject {
#NSManaged var destinationPIC: String
#NSManaged var sourcePIC: String
#NSManaged var nvdNumber: String
#NSManaged var numberOfAnimals: NSNumber
#NSManaged var requestType: String
#NSManaged var date: NSDate
#NSManaged var tagMembers: NSSet
#NSManaged var picMembers: NSSet?
class func createInManagedObjectContext(moc: NSManagedObjectContext, toPic: String, fromPic: String, nvd: String, numAnimals: NSNumber, request: String, date: NSDate, tags: NSSet, pics: NSSet) -> CurrentTransaction {
let newTransaction = NSEntityDescription.insertNewObjectForEntityForName("CurrentTransaction", inManagedObjectContext: moc) as! CurrentTransaction
newTransaction.destinationPIC = toPic
newTransaction.sourcePIC = fromPic
newTransaction.nvdNumber = nvd
newTransaction.numberOfAnimals = numAnimals
newTransaction.requestType = request
newTransaction.tagMembers = tags
newTransaction.picMembers = pics
newTransaction.date = date
return newTransaction
}
}
When I call the CurrentTransaction.addTagObject(tag:Tag) I get "Cannot invoke 'addTagObject' with an argument list of type '(Tag)'
Here is my addTags protocol method:
func addTags(tag: Tag) {
//println("IN ADD TAGS")
//println(tag.tagNumber)
self.tagList.append(tag)
CurrentTransaction.addTagObject(tag) //Error on this line
}
Reason: addTagObject() is not defined as class function and cannot be invoked on class CurrentTransaction. If you intended to use it as class fucntion, change it to:
class func addTagObject(value:Tag) {
var items = self.mutableSetValueForKey("tagMembers");
items.addObject(value)
}