带有SwiftUI MVVM反馈的核心数据
我正在寻找一种使用 coredata
对象使用 mvvm (Ditching @fetchrequest
)的方法。实验后,我到达了以下实现:
软件包url: https://github.com/timmmysapp/timmmysapp/dataSpstruct/dataSubstruct < /a>
datable.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)})
}
}
modeldata.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()
}
}
frib.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)
}
}
到目前为止,这就像魅力一样工作,但是我想检查这是否是最佳方法,并在可能的情况下进行改进。请注意,我一直在使用 @fetchrequest
swiftui
现在已经一年多了,因此决定移至 mvvm ,因为我在所有人中都在使用它我的故事板
项目。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
对于尖端的方式,将
nsfetchedResultScontroller
包裹在SwiftUI兼容代码中,您可能想看 asyncstream 。但是,
@fetchrequest
当前被实现为dynamicproperty
,因此,如果您也这样做,它将允许从@environment
中访问托管对象上下文更新
func在dynamiCproperty
上body
在view> view
上调用。您可以在内部使用@StateObject
作为FRC委托。请谨慎使用MVVM,因为它使用的对象是SwiftUI设计用于使用价值类型的对象,以消除您可以使用对象获得的一致性错误。请参阅doc 在结构和类之间选择。如果您在Swiftui顶部构建MVVM对象层,则可能会重新引入这些错误。最好使用
View
数据结构来设计,并在编码Legacy View Controller时留下MVVM。但是,说实话,如果您学习了子视图控制器模式并了解响应者链,那么MVVM查看模型对象确实不需要。仅供参考,当使用combine的
observableObject
时,我们不sink
管道或使用concellables
。相反, @publed 的管道的管道。但是,如果您不使用combineLatest
,然后也许重新考虑您是否真的应该使用联合收割机。
For a cutting edge way to wrap the
NSFetchedResultsController
in SwiftUI compatible code you might want to take a look at AsyncStream.However,
@FetchRequest
currently is implemented as aDynamicProperty
so if you did that too it would allow access the managed object context from the@Environment
in theupdate
func which is called on theDynamicProperty
beforebody
is called on theView
. You can use an@StateObject
internally as the FRC delegate.Be careful with MVVM because it uses objects where as SwiftUI is designed to work with value types to eliminate the kinds of consistency bugs you can get with objects. See the doc Choosing Between Structures and Classes. If you build an MVVM object layer on top of SwiftUI you risk reintroducing those bugs. You're better off using the
View
data struct as it's designed and leave MVVM for when coding legacy view controllers. But to be perfectly honest, if you learn the child view controller pattern and understand the responder chain then there is really no need for MVVM view model objects at all.And FYI, when using Combine's
ObservableObject
we don'tsink
the pipeline or usecancellables
. Instead,assign
the output of the pipeline to an@Published
. However, if you aren't usingCombineLatest
, then perhaps reconsider if you should really be using Combine at all.