繁体   English   中英

使用 SwiftUI MVVM 反馈的核心数据

[英]Core Data with SwiftUI MVVM Feedback

我正在寻找一种使用 MVVM 使用 CoreData 对象的方法(放弃@FetchRequest )。 经过试验,我得到了这个实现:

数据表.swift:

protocol Datable {
    associatedtype Object: NSManagedObject
//MARK: - Mapping
    static func map(from object: Object) -> Self
    func map(from object: Object) -> Self
//MARK: - Entity
    var object: Object {get}
//MARK: - Fetching
    static var modelData: ModelData<Self> {get}
//MARK: - Writing
    func save()
}

extension Datable {
    static var modelData: ModelData<Self> {
        return ModelData()
    }
    func map(from object: Object) -> Self {
        return Self.map(from: object)
    }
    func save() {
        _ = object
        let viewContext = PersistenceController.shared.container.viewContext
        do {
            try viewContext.save()
        }catch {
            print(String(describing: error))
        }
    }
}

extension Array {
    func model<T: Datable>() -> [T] {
        return self.map({T.map(from: $0 as! T.Object)})
    }
}

模型数据.swift:

 class ModelData<T: Datable>: NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
    var publishedData = CurrentValueSubject<[T], Never>([])
    private let fetchController: NSFetchedResultsController<NSFetchRequestResult>
    override init() {
        let fetchRequest = T.Object.fetchRequest()
        fetchRequest.sortDescriptors = []
        fetchController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: PersistenceController.shared.container.viewContext, sectionNameKeyPath: nil, cacheName: nil)
        super.init()
        fetchController.delegate = self
        do {
            try fetchController.performFetch()
            publishedData.value = (fetchController.fetchedObjects as? [T.Object] ?? []).model()
        }catch {
            print(String(describing: error))
        }
    }
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        guard let data = controller.fetchedObjects as? [T.Object] else {return}
        self.publishedData.value = data.model()
    }
}

尝试.swift:

struct Attempt: Identifiable, Hashable {
    var id: UUID?
    var password: String
    var timestamp: Date
    var image: Data?
}

//MARK: - Datable
extension Attempt: Datable {
    var object: AttemptData {
        let viewContext = PersistenceController.shared.container.viewContext
        let newAttemptData = AttemptData(context: viewContext)
        newAttemptData.password = password
        newAttemptData.timestamp = timestamp
        newAttemptData.image = image
        return newAttemptData
    }
    static func map(from object: AttemptData) -> Attempt {
        return Attempt(id: object.aid ?? UUID(), password: object.password ?? "", timestamp: object.timestamp ?? Date(), image: object.image)
    }
}

ViewModel.swift:

class HomeViewModel: BaseViewModel {
    @Published var attempts = [Attempt]()
    required init() {
        super.init()
        Attempt.modelData.publishedData.eraseToAnyPublisher()
            .sink { [weak self] attempts in
                self?.attempts = attempts
            }.store(in: &cancellables)
    }
}

到目前为止,这就像一个魅力,但是我想检查这是否是最好的方法并尽可能改进它。 请注意,我已经在 SwiftUI 中使用@FetchRequest一年多了,并决定迁移到 mvvm,因为我在所有 Storyboard 项目中都使用它。

对于将 NSFetchedResultsController 包装在NSFetchedResultsController兼容代码中的前沿方法,您可能需要查看AsyncStream

但是, @FetchRequest当前是作为DynamicProperty实现的,因此如果您也这样做,它将允许从update func 中的@Environment访问托管对象上下文,该更新函数在DynamicProperty上调用body之前在View上调用。 您可以在内部使用@StateObject作为 FRC 委托。

小心 MVVM,因为它使用对象,因为 SwiftUI 旨在使用值类型来消除对象可能遇到的各种一致性错误。 请参阅文档选择结构和类 如果您在 SwiftUI 之上构建 MVVM 对象层,则可能会重新引入这些错误。 你最好使用View数据结构,因为它的设计和留下 MVVM 编码遗留视图控制器时。 但老实说,如果您学习了子视图控制器模式并了解了响应者链,那么实际上根本不需要 MVVM 视图模型对象。

仅供参考,当使用 Combine 的ObservableObject时,我们不会sink管道或使用cancellables 相反, assign管道的输出分配给@Published 但是,如果您不使用CombineLatest ,那么也许要重新考虑您是否真的应该使用 Combine。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM