根据表达式

时间:2017-03-16 09:51:10

标签: ios swift uitableview core-data nsfetchedresultscontroller

概要

我希望能够基于来自Core Data实体的fetchRequest中的表达式创建新值。

拥有具有此属性的实体(简短版本)

  Name              Attributes
[Course] -> {licenseStart, name, id}

我想在该实体上创建一个fetchResquest并检索一个包含所有属性的字典以及另一个基于此表达式的字典:

licenseStart.doubleValue / 1000 < NSDate().timeIntervalSince1970 ? "Actually" : "Cooming Soon"

结果将是:

[ "licenseStart":1,"id":3, "name":"A", "status":0, ...
  "licenseStart":2,"id":4, "name":"B", "status":0, ...
  "licenseStart":3,"id":6, "name":"B", "status":0, ...
  "licenseStart":4,"id":2, "name":"C", "status":0, ...
  "licenseStart":5,"id":1, "name":"D", "status":1, ...
  "licenseStart":6,"id":7, "name":"E", "status":1, ...
]

发表

我想问一个问题,我现在面对的问题已经过了几天......我尝试的每一种方法最终都会得到一个更复杂的解决方案。

让我向你解释一下情况:

我有一个CoreData模型,其实体具有不同的属性,其中一个是许可证开始这是一个unix时间戳编号,用于确定课程何时开始。

所以,当我想填充数据来创建一个带有我从数据库中获取所有这些信息的课程的UITableView时没有任何问题,我使用这个 licenseStart 属性来计算基于部分的部分在公式上,这是UITableView

的代码

的UITableViewController

let entity = NSEntityDescription.entityForName("Course", inManagedObjectContext: managedObjectContext!)
        let req = NSFetchRequest()
        let predicate = sectionProtocol?.FRCPredicate
        let sort = [NSSortDescriptor(key: "licenseStart", ascending: true), NSSortDescriptor(key: "name", ascending: true), NSSortDescriptor(key: "courseId", ascending: true)]
        req.entity = entity
        req.sortDescriptors = sort
        req.predicate = predicate
        /* NSFetchedResultsController initialization
        a 'nil' 'sectionNameKeyPath' generates a single section */
        var aFetchedResultsController = NSFetchedResultsController(fetchRequest: req, managedObjectContext: managedObjectContext!, sectionNameKeyPath:"dateStatus", cacheName: nil)

在此之后我的结果是(小例子)像这样

[ "licenseStart":1,"id":3, "name":"A",...
  "licenseStart":2,"id":4, "name":"B",...
  "licenseStart":3,"id":6, "name":"B",...
  "licenseStart":4,"id":2, "name":"C",...
  "licenseStart":5,"id":1, "name":"D",...
  "licenseStart":6,"id":7, "name":"E",
]

我在UITableView中将拥有:

[Actually] 
  [A]
  [B]
  [B]
  [C]
[Cooming Soon]
  [D]
  [E]

所以,我现在所做的是使用 licenseStart 创建两个部分,如何?,简单,只使用我的模型属性 dateStatus 并创建两个可能的使用许可证的结果如下:

ModelClass

var dateStatus : Int {
        get {
            return licenseStart.doubleValue / 1000 < NSDate().timeIntervalSince1970 ? "Actually" : "Cooming Soon"
        }
 }

再次在我的UITableView中,我使用dataSource委托方法来填充行和节。

问题

直到现在一切都很完美。但是现在我想添加一个排序选项,让用户对结果进行升序或降序排序,问题是当我排序时,即使用不同的排序选项升序:

let entity = NSEntityDescription.entityForName("Course", inManagedObjectContext: managedObjectContext!)
        let req = NSFetchRequest()
        let predicate = sectionProtocol?.FRCPredicate
        let sort = [NSSortDescriptor(key: "licenseStart", ascending: true), NSSortDescriptor(key: "name", ascending: false), NSSortDescriptor(key: "courseId", ascending: false)]
        req.entity = entity
        req.sortDescriptors = sort
        req.predicate = predicate
        /* NSFetchedResultsController initialization
        a 'nil' 'sectionNameKeyPath' generates a single section */
        var aFetchedResultsController = NSFetchedResultsController(fetchRequest: req, managedObjectContext: managedObjectContext!, sectionNameKeyPath:"dateStatus", cacheName: nil)

由于我基于licenseStart创建部分的方式,此获取的结果将与之前完全相同:

[ "licenseStart":1,"id":3, "name":"A",...
  "licenseStart":2,"id":4, "name":"B",...
  "licenseStart":3,"id":6, "name":"B",...
  "licenseStart":4,"id":2, "name":"C",...
  "licenseStart":5,"id":1, "name":"D",...
  "licenseStart":6,"id":7, "name":"E",
]

我在UITableView中将拥有:

[Actually] 
  [A]
  [B]
  [B]
  [C]
[Coming Soon]
  [D]
  [E]

为什么呢?因为licenseStart都是不同的,它使用NSSortDescriptors中的第一个元素来检索数据..这不是我想要的行为,我想要的是让这些部分与之前的顺序完全相同,但内部的元素排列不同

即:

[Actually] 
  [C]
  [B]
  [B]
  [A]
[Coming Soon]
  [E]
  [D]

可能的解决方案

我想在fetchrequest期间创建一个新列,如果我能够在检索数据时评估此表达式licenseStart.doubleValue / 1000 < NSDate().timeIntervalSince1970 ? 0 : 1,那么我将使用0&#39; s和1& #39;在结果中是这样的:

[ "licenseStart":1,"id":3, "name":"A", "status":0, ...
  "licenseStart":2,"id":4, "name":"B", "status":0, ...
  "licenseStart":3,"id":6, "name":"B", "status":0, ...
  "licenseStart":4,"id":2, "name":"C", "status":0, ...
  "licenseStart":5,"id":1, "name":"D", "status":1, ...
  "licenseStart":6,"id":7, "name":"E", "status":1, ...
]

现在,我可以使用此状态在NSSortDescriptors [NSSortDescriptor(key: "status", ascending: true),NSSortDescriptor(key: "licenseStart", ascending: true), NSSortDescriptor(key: "name", ascending: true), NSSortDescriptor(key: "courseId", ascending: true)]中创建升序部分,并[NSSortDescriptor(key: "status", ascending: true),NSSortDescriptor(key: "licenseStart", ascending: false), NSSortDescriptor(key: "name", ascending: false), NSSortDescriptor(key: "courseId", ascending: false)]升级部分。

这会给我我想要的行为......但我还没有找到一个干净利落的方式来做这件事。我尝试使用NSExpressions执行此操作,但我无法使用小于来获取CoreData ..

此外,一种可能的解决方案是在我的数据模型中创建这个新列并在保存数据时生成此值,但这不是一种有效的方法,因为每次用户填充时,此licenseStart值必须与当前时间戳进行比较这张桌子。

我在问这个问题是否有人有一个简短的想法或一个聪明的解决方案。

在MySQL或SQLite中很容易基于某个值或表达式创建新列,但在coredata中我是新手,所以我没有看到明确的方法来做到这一点。

非常感谢你。

2 个答案:

答案 0 :(得分:0)

我有一种情况,我希望将一些元素分成不在核心数据属性的列表顶部。我使用fetchedResultsController进行了fetch,然后执行了一次运行,将元素放入两个数组(O(n))中,保持按原始排序排序。当元素改变时,我必须在我的数组中找到它们来改变它们,但它并不像我预期的那么难。

更简单的解决方案可能是使用两个fetchedResultsController。

答案 1 :(得分:0)

尽量不要将CoreData视为常规关系型SQL数据库,您可以在其中创建查询中的列。

您需要的只是向您的实体添加另一个属性status。每次更新licenseStart时,只需设置status值。

如果你正在使用codegen,那么

extension Course {
    func updateLicenseStart(date: Date) {
        licenseStart = date
        status = licenseStart.doubleValue / 1000 < Date().timeIntervalSince1970 ? 0 : 1
    } 
}

或重命名基础属性......

extension Course {
    var licenseStart {
        set {
            _licenseStart = newValue 
            status = licenseStart.doubleValue / 1000 < Date().timeIntervalSince1970 ? 0 : 1
        }
        get { return _licenseStart }
    } 
}

如果您要维护自己的模型类,则无法向@NSManaged var添加属性观察器,但您可以删除@NSManaged并覆盖{{ 1}}和set ...

get