1

I'm having problems implementing custom section in App using Core Data with iCloud sync.

I've made a sample App to illustrate my problem : it has a list of events within CoreData (fetching using FRC)

Event enitity :

@objc(Event)
class Event: NSManagedObject {

@NSManaged var timeStamp: NSDate?
@NSManaged var name: String?
@NSManaged var sectionIdentifier :Int32

}

I have implemented custom sections based on timeStamp of item :

  • In Past
  • Today
  • Tomorrow
  • Next 7 Days
  • Future
  • No Date

    enum SectionType:Int32{
    case inPast = 9
    case Today = 10
    case Tomorrow
    case Next7Days
    case InFuture
    case NotSet = 14
    
    func title()->String{
        switch self {
        case .inPast:
            return "In Past"
        case .Today:
            return "Today"
        case .Tomorrow:
            return "Tomorrow"
        case .Next7Days:
            return "Next 7 Days"
        case .InFuture:
            return "In Future"
        default:
            return "No due date"
        }
    }
    

    }

Code for FRC

   private var _fetchedResultsController: NSFetchedResultsController? = nil
var fetchedResultsController: NSFetchedResultsController {
    if _fetchedResultsController != nil {
        return _fetchedResultsController!
    }

    let fetchRequest = NSFetchRequest()
    let entity = NSEntityDescription.entityForName("Event", inManagedObjectContext: self.managedObjectContext!)
    fetchRequest.entity = entity
    fetchRequest.fetchBatchSize = 20

    let sortDescriptors = [
        NSSortDescriptor(key: "sectionIdentifier", ascending: true),
        NSSortDescriptor(key: "timeStamp", ascending: true)
    ]

    fetchRequest.sortDescriptors = sortDescriptors

    let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest,
        managedObjectContext: self.managedObjectContext!,
        sectionNameKeyPath: "sectionIdentifier",
        cacheName:nil)

    aFetchedResultsController.delegate = self

    _fetchedResultsController = aFetchedResultsController

    var error: NSError? = nil
    if !_fetchedResultsController!.performFetch(&error) {
         abort()
    }

    return _fetchedResultsController!
}    

Everything seems to be working, Events being grouped by sectionIdentifier. But if it's now synced with device in other timezone, the events will be grouped incorrectly because of time diff.

Using transient property would help, but then I cannot use NSSortDescriptor to sort sections.

Are there any solutions ? I really don't want to populate arrays per section and so on.

Kind Regards

CryingHippo
  • 5,026
  • 1
  • 28
  • 32
  • You should use transient properties when fetching core data objects by time because then the section can update on the fly because time is constantly changing so it is difficult to continue to reset a stored property http://stackoverflow.com/questions/25960555/coredata-swift-and-transient-attribute-getters/26614161#26614161 – Ian Mar 27 '15 at 16:48
  • Thanks for reply ! I've actually solved my problem using transient. I was not using them correctly first time. Apples DateSectionTitles project helped. – CryingHippo Mar 27 '15 at 17:33

1 Answers1

0

So I've solved my problem by implementing a transient property.

The only problem left Events without date are placed on top of list. But I've solved it by adding another property hasDate (Bool) and adding second NSSortDescriptor

ManagedObject :

import Foundation
import CoreData

enum SectionType:String{
    case inPast = "10"
    case Today = "11"
    case Tomorrow = "12"
    case Next7Days = "13"
    case InFuture = "14"
    case NotSet = "15"

func title()->String{
    switch self {
    case .inPast:
        return "In Past"
    case .Today:
        return "Today"
    case .Tomorrow:
        return "Tomorrow"
    case .Next7Days:
        return "Next 7 Days"
    case .InFuture:
        return "In Future"
    default:
        return "No due date"
    }
}
}



@objc(Event)

class Event: NSManagedObject {

@NSManaged var timeStamp: NSDate?
@NSManaged var noDate: Bool
@NSManaged var name: String?

var sectionIdentifier :String? {
    get {
        var str : String

            if let aDate = self.timeStamp {
                if aDate.isToday() || aDate.isYesterday() {
                   str = SectionType.Today.rawValue
                } else if aDate.isTommorow() {
                     str = SectionType.Tomorrow.rawValue
                } else if aDate.isNext7Days() {
                     str = SectionType.Next7Days.rawValue
                } else if aDate.inPast(){
                     str = SectionType.inPast.rawValue
                } else {
                     str = SectionType.InFuture.rawValue
                }

            }else {
                 str = SectionType.NotSet.rawValue
            }

        return str
    }
    set {
        self.sectionIdentifier = newValue
    }
}



func setTime(date : NSDate?){
    self.willChangeValueForKey("timeStamp")
    self.setValue(date, forKey: "timeStamp")
    self.didChangeValueForKey("timeStamp")

    if let date = date {
        self.noDate = false
    }
}

class func keyPathsForValuesAffectingSectionIdentifier() -> NSSet {
    return  NSSet(object: "timeStamp")
}

}

FRC :

       private var _fetchedResultsController: NSFetchedResultsController? = nil
    var fetchedResultsController: NSFetchedResultsController {
        if _fetchedResultsController != nil {
            return _fetchedResultsController!
        }

        let fetchRequest = NSFetchRequest()
        let entity = NSEntityDescription.entityForName("Event", inManagedObjectContext: self.managedObjectContext!)
        fetchRequest.entity = entity
        fetchRequest.fetchBatchSize = 20

        let sortDescriptors = [
//            NSSortDescriptor(key: "sectionIdentifier", ascending: true),
            NSSortDescriptor(key: "noDate", ascending: true),
            NSSortDescriptor(key: "timeStamp", ascending: true)
        ]

        fetchRequest.sortDescriptors = sortDescriptors

        let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest,
            managedObjectContext: self.managedObjectContext!,
            sectionNameKeyPath: "sectionIdentifier",
            cacheName:nil)

        aFetchedResultsController.delegate = self

        _fetchedResultsController = aFetchedResultsController

        var error: NSError? = nil
        if !_fetchedResultsController!.performFetch(&error) {
             abort()
        }

        return _fetchedResultsController!
    }  
CryingHippo
  • 5,026
  • 1
  • 28
  • 32