I have an app that keeps track of monthly expenses. I have an Expense entity with a month attribute that keeps track of current month expense is created on. I would then display the expenses for each month in a table view as shown here. The user can only switch left and right only if there are expenses within the next or the last month
#IBAction func backMonthButtonPressed(sender: AnyObject) {
print("Back Button Pressed")
currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: -1, toDate: currentMonth, options: [])!
if checkMonth(currentMonth) {
updateFetch()
setMonth()
} else {
currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: 1, toDate: currentMonth, options: [])!
}
tableView.reloadData()
}
#IBAction func nextMonthButtonPressed(sender: AnyObject) {
print("Next Button Pressed")
currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: 1, toDate: currentMonth, options: [])!
if checkMonth(currentMonth) {
updateFetch()
setMonth()
} else {
currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: -1, toDate: currentMonth, options: [])!
}
tableView.reloadData()
}
func checkMonth(month : NSDate) -> Bool {
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let context = app.managedObjectContext //scratch pad
let fetchRequest = NSFetchRequest(entityName: "Expense")
fetchRequest.predicate = NSPredicate(format: "month == %#", month.MonthYearDateFormatter())
let count = context.countForFetchRequest(fetchRequest, error: nil)
if count > 0 {
print("There are expenses for this month \(month.MonthYearDateFormatter()). Show expenses")
return true
} else {
print("There are no expenses for this month \(month.MonthYearDateFormatter()). Do Nothing")
return false
}
}
My problem is this, in the unlikely scenario that the user created an expense back in June and didn't create an expense in August. How can I let the user still see his/her expense back in August without skipping it. Any ideas?
I made some optimisation before elaboration:
#IBAction func backMonthButtonPressed(sender: AnyObject) {
self.processMonth(step: -1)
}
#IBAction func nextMonthButtonPressed(sender: AnyObject) {
self.processMonth(step: 1)
}
// a method uses almost the same code for both cases, so it was merged
func processMonth(step: Int) {
let direction = (step < 1 ? "Back" : "Next")
print("\(direction) Button Pressed")
currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: step, toDate: currentMonth, options: [])!
//if checkMonth(currentMonth) {
// I wouldn't test this because it locks you out from seeing empty month.
updateFetch()
setMonth()
//}
tableView.reloadData()
}
An answer to what you've exactly asked:
If your data source for your UITableView is set properly, you should be able to go through empty months though
// changed return type from `Bool` to `void` as I suggested in the method not to test empty month, as it could be useless
func checkMonth(month : NSDate) {
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let context = app.managedObjectContext //scratch pad
let fetchRequest = NSFetchRequest(entityName: "Expense")
fetchRequest.predicate = NSPredicate(format: "month == %#", month.MonthYearDateFormatter())
let count = context.countForFetchRequest(fetchRequest, error: nil)
if count > 0 {
print("There are expenses for this month \(month.MonthYearDateFormatter()). Show expenses")
} else {
print("There are no expenses for this month \(month.MonthYearDateFormatter()). Do Nothing")
// here you can make some additional actions, like seeting the empty table with "no expenses this month"
}
}
Anyway, as #Paulw11 noted, if your data source is not size-exhausting, you could rather fetch the data from your data-model at viewDidLoad/viewDidAppear for example and then to render each month according to the currentMonth variable (regarding what month a user currently see).
So as a result, you would call setMonth() method only in the load of your controller and each time a user changes a current month view.
With the help of #Paulw11 and #pedrouan, I was able to do what I wanted.
Fetch all expenses
func fetchExpenses() {
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let context = app.managedObjectContext //scratch pad
let fetchRequest = NSFetchRequest(entityName: "Expense")
//Always Sort Budget by the date it's created
let sortDescriptor = NSSortDescriptor(key: "created", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
do {
let results = try context.executeFetchRequest(fetchRequest)
self.expenses = results as! [Expense]
} catch let err as NSError {
print(err.debugDescription)
}
}
Set constraints to first and last month to determine how far a user can navigate through months in viewDidLoad
//If array is not empty, set first and last month
if expenses.count > 0 {
guard let first = expenses.first?.month else {
return
}
firstMonth = first
guard let last = expenses.last?.month else {
return
}
lastMonth = last
}else {
//Set current month to be first and last
firstMonth = currentMonth.MonthYearDateFormatter()
lastMonth = currentMonth.MonthYearDateFormatter()
}
Limit user from going past first and last month
#IBAction func backMonthButtonPressed(sender: AnyObject) {
print("Back Button Pressed")
if currentMonth.MonthYearDateFormatter() != firstMonth {
self.processMonth(step: -1)
}
}
#IBAction func nextMonthButtonPressed(sender: AnyObject) {
print("Next Button Pressed")
if currentMonth.MonthYearDateFormatter() != lastMonth {
self.processMonth(step: 1)
}
}
Return all the expenses in current month and if there's none clear fetch and return empty table.
func updateFetch() {
setFetchResults()
do {
try self.fetchedResultsController.performFetch()
} catch {
let error = error as NSError
print("\(error), \(error.userInfo)")
}
}
func setFetchResults() {
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let context = app.managedObjectContext //scratch pad
//Fetch Request
let fetchRequest = NSFetchRequest(entityName: "Expense")
let sortDescriptor = NSSortDescriptor(key: "created", ascending: false)
fetchRequest.predicate = NSPredicate(format: "month == %#", currentMonth.MonthYearDateFormatter())
fetchRequest.sortDescriptors = [sortDescriptor]
let count = context.countForFetchRequest(fetchRequest, error: nil)
let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: ad.managedObjectContext , sectionNameKeyPath: section, cacheName: nil)
//If there is none, empty fetch request
if count == 0 {
print("There are no expenses for this month. Show nothing")
controller.fetchRequest.predicate = NSPredicate(value: false)
} else {
print("There are expenses for this month. Show expenses")
}
fetchedResultsController = controller
controller.delegate = self
}
With pedroruan's processMonth(), I was able to navigate through empty tableViews so that user can see that August is empty while still being able to go to June's month. Thanks guys.
Related
There isn't a whole of sample code on the internet for querying for sleep data. The below code will return all sleep samples from a given day which if you look at the Apple Health App include "Asleep" samples from the Apple Watch which are sleep intervals, but there is also an "In Bed" sample from the iPhone which contains the total range from getting in bed to getting out of bed. How can I query HealthKit for only this In Bed sample?
func sleepTime() {
let healthStore = HKHealthStore()
// startDate and endDate are NSDate objects
// first, we define the object type we want
if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
// You may want to use a predicate to filter the data... startDate and endDate are NSDate objects corresponding to the time range that you want to retrieve
//let predicate = HKQuery.predicateForSamplesWithStartDate(startDate,endDate: endDate ,options: .None)
// Get the recent data first
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
// the block completion to execute
let query = HKSampleQuery(sampleType: sleepType, predicate: nil, limit: 100000, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in
if error != nil {
// Handle the error in your app gracefully
return
}
if let result = tmpResult {
for item in result {
if let sample = item as? HKCategorySample {
let startDate = sample.startDate
let endDate = sample.endDate
print()
let sleepTimeForOneDay = sample.endDate.timeIntervalSince(sample.startDate)
}
}
}
}
}
HKCategorySample contains the value of type Int which is the enumeration value for the sample.
It has three values:
0 -> inBed
1 -> asleep
2 -> awake
So, the proposed change if you only want inBed data is:
if let result = tmpResult {
for item in result {
if let sample = item as? HKCategorySample {
if sample.value == 0 {
let startDate = sample.startDate
let endDate = sample.endDate
print()
let sleepTimeForOneDay = sample.endDate.timeIntervalSince(sample.startDate)
}
}
}
}
Better way is to go with switch case.
if let result = tmpResult {
for item in result {
if let sample = item as? HKCategorySample {
switch sample.value {
case 0:
// inBed, write logic here
print("inBed")
case 1:
// asleep, write logic here
print("asleep")
default:
// awake, write logic here
print("awake")
}
}
}
}
I currently have two managed objects for Core Data that has one-to-many relationship.
Goal
extension Goal {
#nonobjc public class func createFetchRequest() -> NSFetchRequest<Goal> {
return NSFetchRequest<Goal>(entityName: "Goal")
}
#NSManaged public var title: String
#NSManaged public var date: Date
#NSManaged public var progress: NSSet?
}
Progress
extension Progress {
#nonobjc public class func createFetchRequest() -> NSFetchRequest<Progress> {
return NSFetchRequest<Progress>(entityName: "Progress")
}
#NSManaged public var date: Date
#NSManaged public var comment: String?
#NSManaged public var goal: Goal
}
For every goal, you can have multiple Progress objects. The problem is when I request a fetch for Progress with a particular Goal as the predicate, nothing is being returned. I have a suspicion that I'm not using the predicate properly.
This is how I request them.
First, I fetch Goal for a table view controller:
var fetchedResultsController: NSFetchedResultsController<Goal>!
if fetchedResultsController == nil {
let request = Goal.createFetchRequest()
let sort = NSSortDescriptor(key: "date", ascending: false)
request.sortDescriptors = [sort]
request.fetchBatchSize = 20
fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: self.context, sectionNameKeyPath: "title", cacheName: nil)
fetchedResultsController.delegate = self
}
fetchedResultsController.fetchRequest.predicate = goalPredicate
do {
try fetchedResultsController.performFetch()
} catch {
print("Fetch failed")
}
And pass the result to the next screen, Detail view controller:
if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController {
vc.goal = fetchedResultsController.object(at: indexPath)
navigationController?.pushViewController(vc, animated: true)
}
Finally, I fetch Progress using the Goal as the predicate from Detail view controller:
var goal: Goal!
let progressRequest = Progress.createFetchRequest()
progressRequest.predicate = NSPredicate(format: "goal == %#", goal)
if let progress = try? self.context.fetch(progressRequest) {
print("progress: \(progress)")
if progress.count > 0 {
fetchedResult = progress[0]
print("fetchedResult: \(fetchedResult)")
}
}
Goal is being returned properly, but I get nothing back for Progress. I've tried:
progressRequest.predicate = NSPredicate(format: "goal.title == %#", goal.title)
or
progressRequest.predicate = NSPredicate(format: "ANY goal == %#", goal)
but still the same result.
Following is how I set up the relationship:
// input for Progress from the user
let progress = Progress(context: self.context)
progress.date = Date()
progress.comment = commentTextView.text
// fetch the related Goal
var goalForProgress: Goal!
let goalRequest = Goal.createFetchRequest()
goalRequest.predicate = NSPredicate(format: "title == %#", titleLabel.text!)
if let goal = try? self.context.fetch(goalRequest) {
if goal.count > 0 {
goalForProgress = goal[0]
}
}
// establish the relationship between Goal and Progress
goalForProgress.progress.insert(progress)
// save
if self.context.hasChanges {
do {
try self.context.save()
} catch {
print("An error occurred while saving: \(error.localizedDescription)")
}
}
Actually you don't need to refetch the data. You can get the progress from the relationship
Declare progress as native Set
#NSManaged public var progress: Set<Progress>
In DetailViewController delete the fetch code in viewDidLoad and declare
var progress: Progress!
In the first view controller filter the progress
let goal = fetchedResultsController.object(at: indexPath)
if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController,
let progress = goal.progress.first(where: {$0.goal.title == goal.title}) {
vc.progress = progress
navigationController?.pushViewController(vc, animated: true)
}
And consider to name the to-many relationship in plural form (progresses)
I figured out that it's due to Core Data Fault where Core Data lazy loads the data and unless you explicitly access the data, the value will not be displayed.
You can either do something like the following:
let goal = fetchedResultsController.object(at: indexPath)
if let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController,
let progress = goal.progress.first(where: {$0.goal.title == goal.title}) {
vc.goalTitle = goal.title
vc.date = progress.date
if let comment = progress.comment {
vc.comment = comment
}
navigationController?.pushViewController(vc, animated: true)
}
or setreturnsObjectsAsFaults to false.
Here's a good article on the topic.
I am trying to read all calendar events from the EventStore. The routine I use, works sometimes but not always.
func getCalendarEvents(_ anfangOpt: Date?, _ endeOpt: Date?) -> [EKEvent]? {
guard let anfang = anfangOpt, let ende = endeOpt else { return nil }
var events: [EKEvent]? = nil
let eventStore = EKEventStore()
eventStore.requestAccess( to: EKEntityType.event, completion: { _,_ in })
if EKEventStore.authorizationStatus(for: EKEntityType.event) == EKAuthorizationStatus.authorized {
var predicate: NSPredicate? = nil
predicate = eventStore.predicateForEvents(withStart: anfang, end: ende, calendars: nil)
if let aPredicate = predicate {
events = eventStore.events(matching: aPredicate)
}
}
return events
}
This function always returns the events. But they are sometimes incomplete. So that
for event in bereinigteEvents {
if dateInInterval(prüfdatum, start: event.startDate, ende: event.endDate) {
istimurlaub = true
if let zwischenname = event.title {
eventname = zwischenname
} else {
eventname = "n/a"
}
eventcalendar = event.calendar.title
trigger.append ("Auslöser: „" + eventname + "“ im Kalender „" + eventcalendar + "“")
}
}
sometimes crashes at the line "eventcalendar = event.calendar.title" and the error message that "nil" was unexpectedly found.
Thank you!
After the first answer I have changed the function, which gets the events to:
func getCalendarEvents(_ anfangOpt: Date?, _ endeOpt: Date?) -> [EKEvent]? {
guard let anfang = anfangOpt, let ende = endeOpt else { return nil }
var events: [EKEvent]? = nil
let eventStore = EKEventStore()
func fetchEvents() {
var predicate: NSPredicate? = nil
predicate = eventStore.predicateForEvents(withStart: anfang, end: ende, calendars: nil)
if let aPredicate = predicate {
events = eventStore.events(matching: aPredicate)
}
}
if EKEventStore.authorizationStatus(for: EKEntityType.event) == EKAuthorizationStatus.authorized {
fetchEvents()
} else {
eventStore.requestAccess( to: EKEntityType.event, completion: {(granted, error) in
if (granted) && (error == nil) {
fetchEvents()
}
})
}
return events
}
But it still crashes with "unexpectedly found nil" in "event.calendar.title".
I ended up using this
Swift 4 How to get all events from calendar?
routine to fetch the events.
The problem still occurs sometimes (!!): Occasionally "nil" is found in "event.calender.title", although it shouldn't be "nil"
The line
eventStore.requestAccess( to: EKEntityType.event, completion: { _,_ in })
is pointless because it works asynchronously. The result of the request is returned after the authorizationStatus check in the next line.
I recommend to first check the status. If the access is not granted ask for permission and perform the fetch. If it's granted perform the fetch directly. This can be accomplished by moving the code to fetch the events into a method.
Note:
It seems that you want to fetch the events when calling the method. Why do you declare start and end date as optional and check for nil?
Declare
func getCalendarEvents(_ anfang: Date, _ ende: Date) -> [EKEvent]? { ...
then you get notified at compile time whether a parameter is nil.
PS: Deutsche Parameternamen mit Umlauten sehen sehr lustig aus. (German parameter names with umlauts look pretty funny)
Problem was, that event.calendar is actually an optional (which I was not aware of).
if let eventZwischenCal = event.calendar {
eventcalendar = eventZwischenCal.title
} else {
eventcalendar = "n/a"
}
fixes the problem.
I am using GCD in swift
like this :
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
//all background task
dispatch_async(dispatch_get_main_queue()) {
self.second()
}
}
In this code second function is getting called before completing all background task that's why I am not able to take some data which I am using in second function. I want to second method after completing all background task. Can anyone tell me how to achieve this task?
***************In background I am taking healthkit data like******
let healthKitTypesToRead =
Set(
arrayLiteral: HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth)!,
HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBiologicalSex)!,
HKObjectType.workoutType()
)
let newCompletion: ((Bool, NSError?) -> Void) = {
(success, error) -> Void in
if !success {
print("You didn't allow HealthKit to access these write data types.\nThe error was:\n \(error!.description).")
return
}
else
{
let stepCount = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
// Our search predicate which will fetch data from now until a day ago
// (Note, 1.day comes from an extension
// You'll want to change that to your own NSDate
//let date = NSDate()
//let predicate = HKQuery.predicateForSamplesWithStartDate(date, endDate: NSDate(), options: .None)
// The actual HealthKit Query which will fetch all of the steps and sub them up for us.
let stepCountQuery = HKSampleQuery(sampleType: stepCount!, predicate:.None, limit: 0, sortDescriptors: nil) { query, results, error in
var steps: Double = 0
if results?.count > 0
{
for result in results as! [HKQuantitySample]
{
steps += result.quantity.doubleValueForUnit(HKUnit.countUnit())
}
testClass.HK_stepCount = String(steps)
}
//completion(steps, error)
}
self.healthKitStore.executeQuery(stepCountQuery)
//EDIT.....
let tHeartRate = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)
let tHeartRateQuery = HKSampleQuery(sampleType: tHeartRate!, predicate:.None, limit: 0, sortDescriptors: nil) { query, results, error in
if results?.count > 0
{
var string:String = ""
for result in results as! [HKQuantitySample]
{
let HeartRate = result.quantity
string = "\(HeartRate)"
print(string)
}
testClass.HK_HeartRate = string
finalCompletion(Success: true)
}
}
self.healthKitStore.executeQuery(tHeartRateQuery)
}
}
healthKitStore.requestAuthorizationToShareTypes(healthKitTypesToWrite, readTypes: healthKitTypesToRead, completion: newCompletion)
I am not able to take value of step count, It get executed after calling second(method) called, plz suggest me what to do?
You can create a separate function to execute your task on other thread
func someFunction(finalCompletion: (Success: Bool)->()) {
let healthKitTypesToRead =
Set(
arrayLiteral: HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth)!,
HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBiologicalSex)!,
HKObjectType.workoutType()
)
let newCompletion: ((Bool, NSError?) -> Void) = {
(success, error) -> Void in
if !success {
print("You didn't allow HealthKit to access these write data types.\nThe error was:\n \(error!.description).")
return
}
else
{
let stepCount = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
// Our search predicate which will fetch data from now until a day ago
// (Note, 1.day comes from an extension
// You'll want to change that to your own NSDate
//let date = NSDate()
//let predicate = HKQuery.predicateForSamplesWithStartDate(date, endDate: NSDate(), options: .None)
// The actual HealthKit Query which will fetch all of the steps and sub them up for us.
let stepCountQuery = HKSampleQuery(sampleType: stepCount!, predicate:.None, limit: 0, sortDescriptors: nil) { query, results, error in
var steps: Double = 0
if results?.count > 0
{
// Edit--
for result in results as! [HKQuantitySample]
{
steps += result.quantity.doubleValueForUnit(HKUnit.countUnit())
// heartBeat += ....
}
testClass.HK_stepCount = String(steps)
finalCompletion(Success: true)
}
//completion(steps, error)
}
self.healthKitStore.executeQuery(stepCountQuery)
}
}
healthKitStore.requestAuthorizationToShareTypes(healthKitTypesToWrite, readTypes: healthKitTypesToRead, completion: newCompletion)
}
Another function?
I will edit this answer in some time to tell you about a better technique to deal with async request. In general you should have a separate singleton class for such background tasks. (RESTful API service class.. but for now you can use the below method)
func getHeartBeatInfo(finalCompletionHeart: (Success: Bool)->()) {
let tHeartRate = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)
let tHeartRateQuery = HKSampleQuery(sampleType: tHeartRate!, predicate:.None, limit: 0, sortDescriptors: nil) { query, results, error in
if results?.count > 0
{
var string:String = ""
for result in results as! [HKQuantitySample]
{
let HeartRate = result.quantity
string = "\(HeartRate)"
print(string)
}
testClass.HK_HeartRate = string
finalCompletionHeart(Success: true)
}
}
self.healthKitStore.executeQuery(tHeartRateQuery)
}
And then you can call this method like so:
override func viewDidLoad() {
someFunction( { (finalCompletion) in
if finalCompletion == true {
getHeartBeatInfo( { finalCompletionHeart in
if finalCompletionHeart == true {
self.second()
}
})
}
})
}
I'm doing a chat app, where I'm storing chat time for each chat, now I need to display latest chats first, grouped in section, for eg: If I have 10 chats of today it should be under section named July 10, 2016 with last send chat at last. For this I'm sorting the list by chatTimestamp and I have another sectionDate field which stores the corresponding date. Below is a sample format of data that displays in core data.
<MyChat.ChatData: 0x7f8b71cdd190> (entity: ChatData; id: 0xd000000000140002 <x-coredata:….> ; data: {
chatDeviceType = ipad;
chatId = 3557;
chatOwnerName = “John Mathew”;
chatReadStatus = 1;
chatStatus = Received;
chatText = “Hi how are you?“;
chatTimestamp = "2015-09-21 10:41:37 +0000";
chatType = Mine;
imageData = nil;
imageUrl = nil;
sectionDate = "Sep 21, 2015";
users = "0xd000000000080000 <x-coredata:…>”;
})
This is a portion of my code so far
var fetchedResultsController: NSFetchedResultsController = NSFetchedResultsController()
override func viewDidLoad() {
super.viewDidLoad()
setupFRC(limit: LIMIT)
......
}
func setupFRC(limit limit:Int) {
messageMaxLimit += limit
let objectsCount = self.stack.mainContext.countForFetchRequest(fetchRequest(self.stack.mainContext), error: nil)
NSFetchedResultsController.deleteCacheWithName("Root")
let request = self.fetchRequest(self.stack.mainContext)
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: request,
managedObjectContext: self.stack.mainContext,
sectionNameKeyPath: "sectionDate",
cacheName: "Root")
self.fetchedResultsController.delegate = self
self.messageMaxLimit = self.messageMaxLimit > objectsCount ? objectsCount : self.messageMaxLimit
if objectsCount > self.messageMaxLimit
{
self.fetchedResultsController.fetchRequest.fetchOffset = objectsCount - self.messageMaxLimit
}
self.fetchData()
}
//To fetch data in FRC
func fetchData() {
do {
try self.fetchedResultsController.performFetch()
chatCollectionView.reloadData()
} catch {
assertionFailure("Failed to fetch: \(error)")
}
}
func fetchRequest(context: NSManagedObjectContext) -> FetchRequest<ChatData> {
let e = entity(name: "ChatData", context: context)
let fetch = FetchRequest<ChatData>(entity: e)
fetch.predicate = NSPredicate(format: "(SELF.users == %#)", currentUser!)
//Sort by chatTimeStamp
let sortDescriptor = NSSortDescriptor(key: "chatTimestamp", ascending: true)
fetch.sortDescriptors = [sortDescriptor]
return fetch
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
do {
try self.fetchedResultsController.performFetch()
} catch {
assertionFailure("Failed to fetch: \(error)")
}
chatCollectionView.reloadData()
}
Now the problem is that the chats seems to have order correctly, but the sections are in alphabetic order. But If I enter a chat and send it and when it reloads from controllerDidChangeContent it gets corrected.
I can't figure out why it doesn't load in correct order initially.I'm using a collection view for this. Am I doing anything wrong here?
I fixed the issue by removing the NSManaged attribute for sectionDate and added it like below and made the property transient in the core data model and it worked.
var sectionDate: String {
get {
self.willAccessValueForKey("sectionDate")
var ddtmp = self.primitiveValueForKey("sectionDate") as! String?
self.didAccessValueForKey("sectionDate")
if (ddtmp == nil)
{
ddtmp = Utilities.stringFromDate(self.chatTimestamp!, dateFormat: "MMM dd, yyyy")
self.setPrimitiveValue(ddtmp, forKey: "sectionDate")
}
return ddtmp!
}
}