【问题标题】:Problems creating a generic MVVM connector to CoreData创建到 CoreData 的通用 MVVM 连接器时出现问题
【发布时间】:2021-11-15 19:57:29
【问题描述】:

首先,很抱歉帖子的长度,但我对 iOS 和 SwiftUI 开发非常陌生,我不想错过任何细节。我在 Android 和 Flutter 上使用 Kotlin 做过一些小项目,所以我有一些应用开发经验。

上下文

我尝试创建一个简单的应用程序,将用户数据保存在 CoreData 上,并且我尝试遵循 MVVM 架构来开发应用程序。我受到了 Medium 上的 post 的启发。我有以下文件:

  • DataSource.swift:抽象NSPersistentContainer的初始化的类。
  • Entity.swift:CoreData 实体类标准化协议。
  • ProductEntity.swift:符合 Entity 协议的特定 CoreData 类定义。
  • Model.swift:具有实体泛型的类,抽象模型实例化和更新过程。
  • ProductModel.swift:继承 Model 的特定 CoreData 实体模型定义(引发异常的地方)。

异常

我在初始化 ProductsModel 类时遇到异常(ProductsModel.swift,请在下面查看),我不知道错误来源及其原因在哪里。

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An instance of NSFetchedResultsController requires a fetch request with sort descriptors'

希望你能给我一些线索! :)

代码

DataSource.swift

import Foundation
import CoreData

let defaultDatabase = "DB"

class DataSource {
    static let shared = DataSource()
    public let container: NSPersistentContainer
    
    init(dbName: String = defaultDatabase) {
        container = NSPersistentContainer(name: dbName)
        container.loadPersistentStores { (_, err) in
            if let error = err as NSError? {
                print("NSError \(error) - \(error.userInfo)")
                return
            }
        }
    }
    
    func save() {
        do {
            print("Saving context")
            try self.container.viewContext.save()
            print("Successfully saved context")
        } catch {
            print("ERROR: \(error as NSObject)")
        }
    }
}

Entity.swift

import CoreData

protocol Entity: NSFetchRequestResult {
    associatedtype CurrentEntity: NSManagedObject
    static var name: String { get }
}

ProductEntity.swift

import os
import CoreData

@objc(ProductEntity)
public class ProductEntity: NSManagedObject, Entity {
    typealias CurrentEntity = ProductEntity
    static let name: String = "Product"
}

extension ProductEntity : Identifiable {
    public var ID: String {
        self.objectID.uriRepresentation().absoluteString
    }
}

extension ProductEntity {
    @NSManaged public var desc: String?
    @NSManaged public var name: String
    @NSManaged public var price: Double
    @NSManaged public var rations: Int16
    @NSManaged public var shoppingList: NSSet?
}

Model.swift

import Combine
import CoreData
import os

class Model<T: Entity>: NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
    var records = CurrentValueSubject<[T.CurrentEntity], Never>([])
    private let controller: NSFetchedResultsController<T.CurrentEntity>
    
    override init() {
        controller = NSFetchedResultsController(
            fetchRequest: NSFetchRequest<T.CurrentEntity>(entityName: T.name),
            managedObjectContext: DataSource.shared.container.viewContext,
            sectionNameKeyPath: nil, cacheName: nil
        )
        
        super.init()
        
        controller.delegate = self
        
        do {
            try controller.performFetch()
            records.value = (controller.fetchedObjects ?? []) as [T.CurrentEntity]
        } catch {
            NSLog("Error: could not fetch objects")
        }
    }
    
    public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        guard let records = controller.fetchedObjects as? [T.CurrentEntity] else { return }
        self.records.value = records
    }
    
    public func save() {
        DataSource.shared.save()
    }
}

ProductModel.swift

import os

class ProductsModel: Model<ProductEntity> {
    static let shared: ProductsModel = ProductsModel() // <-- This line raise the exception
}

【问题讨论】:

  • 正如异常所说,您传递给NSFetchedResultsControllerNSFetchRequest 必须至少有一个排序描述符。 developer.apple.com/documentation/coredata/…。您可以将排序键传递给模型初始化程序。老实说,如果您刚刚开始尝试采用 MVVM,那么您可能会让自己的生活变得艰难,除非您在另一个平台上对此有丰富的经验。 MVVM 并不适合 iOS,尤其是在您使用 UIKit 时,除非您使用 Combine 或类似的绑定框架。

标签: ios swift core-data mvvm combine


【解决方案1】:

NSFetchedResultsController 是一个可以管理来自Core Data 的搜索结果的工具。它至少需要一个排序描述符来维护您的获取请求列表。 所以你应该改进你定义的NSFetchRequest来解决这个异常。

let req = NSFetchRequest<T.CurrentEntity>(entityName: T.name)
req.sortDescriptors = [NSSortDescriptor(key: "someKeyForSort", ascending: true)]

另外,"someKeyForSort"T.CurrentEntity 的属性名称。如果ProductEntity 是类型,假设您希望NSFetchedResultsController 维护按名称升序排序的获取结果,则“名称”可能是键。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-16
    • 2021-06-18
    • 1970-01-01
    • 1970-01-01
    • 2020-05-25
    • 2013-10-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多