Rex*_*xha 4 xcode mvvm swift swift5 swiftui
我的视图中有这些过滤器,它们都会更新,FilterViewModel然后负责过滤数据。其中一个视图SearchAddressView需要 aPlacemarkViewModel而不是 a,FilterViewModel因为它在用户开始键入时提供地址下拉列表。那里有很多代码,所以我不想将此代码复制到我的FilterViewModel
但是,我需要阅读@Published var placemark: Placemark从PlacemarkViewModel到FilterViewModel。我正在尝试将 PlacemarkViewModel 导入 FilterViewModel,然后用于didSet { }读取它的值,但它不起作用。
所以想法是..当用户搜索地址时,这会更新 PlacemarkViewModel 但FilterViewModel也需要获取该值。关于如何实现这一目标有什么想法吗?
struct FiltersView: View {
@ObservedObject var filterViewModel: FilterViewModel
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack {
FilterButtonView(title: LocalizedStringKey(stringLiteral: "category"), systemName: "square.grid.2x2.fill") {
CategoryFilterView(filterViewModel: self.filterViewModel)
}
FilterButtonView(title: LocalizedStringKey(stringLiteral: "location"), systemName: "location.fill") {
SearchAddressView(placemarkViewModel: self.filterViewModel.placemarkViewModel)
}
FilterButtonView(title: LocalizedStringKey(stringLiteral: "sort"), systemName: "arrow.up.arrow.down") {
SortFilterView(filterViewModel: self.filterViewModel)
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
过滤视图模型
class FilterViewModel: ObservableObject, LoadProtocol {
@Published var placemarkViewModel: PlacemarkViewModel() {
didSet {
print("ok") // nothing
}
}
}
Run Code Online (Sandbox Code Playgroud)
地标视图模型
class PlacemarkViewModel: ObservableObject {
let localSearchCompleterService = LocalSearchCompleterService()
let locationManagerService = LocationManagerService()
@Published var addresses: [String] = []
// I need this value in my FilterViewMode;
@Published var placemark: Placemark? = nil
@Published var query = "" {
didSet {
localSearchCompleterService.autocomplete(queryFragment: query) { (addresses: [String]) in
self.addresses = addresses
}
}
}
init(placemark: Placemark? = nil) {
self.placemark = placemark
}
var address: String {
if let placemark = placemark {
return "\(placemark.postalCode) \(placemark.locality), \(placemark.country)"
}
return ""
}
func setPlacemark(address: String) {
locationManagerService.getLocationFromAddress(addressString: address) { (coordinate, error) in
let location: CLLocation = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
self.locationManagerService.getAddressFromLocation(location: location) { (placemark: CLPlacemark?) in
if let placemark = placemark {
self.placemark = Placemark(placemark: placemark)
self.query = placemark.name ?? ""
}
}
}
}
func getAddressFromLocation() {
locationManagerService.getLocation { (location: CLLocation) in
self.locationManagerService.getAddressFromLocation(location: location) { (placemark: CLPlacemark?) in
if let placemark = placemark {
self.placemark = Placemark(placemark: placemark)
self.query = placemark.name ?? ""
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
接受的答案有很多代码味道,我觉得有必要在这种污染蔓延到 SwiftUI 开发之前澄清它。
视图模型本身是不明确的(为什么其中允许控制逻辑/副作用?)
嵌套视图模型?那有什么意思?同样,没有什么可以阻止您在其中隐藏副作用,这更难以跟踪和调试。
您还需要考虑维护对象生命周期的成本(初始化、传递嵌套、保留周期、释放)。
例如;init(placemarkViewModel: PlacemarkViewModel) { self.placemarkViewModel = placemarkViewModel }
我看到的关于嵌套视图模型的争论是“这是一种常见的做法”。
不,这是一个常见的错误。当有人写下这句话时,你有何感受?
`vm1.vm2.vm3.modelY.property1.vm.p2`
Run Code Online (Sandbox Code Playgroud)
因为当你鼓励这样做时,这正是会发生的事情。
MVVM 通常将 ViewModel 视为无害的值类型,而实际上它是一个充满控制/业务逻辑/副作用的引用类型。
这就是一个这样的例子。当您创建“模型”时,您会发起具有副作用的网络请求。这会伤害使用你的“模型”的不知情的开发人员。
网络不应该是创建引用类型模型的唯一原因。您可以拥有专用的网络服务对象和值类型模型。
如果您从“ViewModel”中剥离所有网络,并且发现剩余的“ViewModel”是微不足道或愚蠢的,那么您就走在正确的轨道上。
您应该使用@EnvironmentObject,而不是拥有两个视图模型并具有隐式依赖关系。
例如;
final class SharedState: ObservableObject {
@Published var placemark: Placemark?
// other stuff you want to publish
func updatePlacemark() {
// network call to update placemark and trigger view update
}
}
let state = SharedState()
state.updatePlacemark() // explicit call for networking with side effects
// set as environmentObject, e.g.; ContentView().environmentObject(state)
Run Code Online (Sandbox Code Playgroud)
您的SearchAddressView可以删除外部参数并直接访问environmentObject。
因此,您可以删除所有传递的视图模型:
FilterButtonView(title: LocalizedStringKey(stringLiteral: "location"), systemName: "location.fill") {
SearchAddressView()
}
Run Code Online (Sandbox Code Playgroud)
等等,但这让我花哨的视图模型变得毫无用处?
这可能是这一切中最大的收获。你不需要它们。它们引入了额外的复杂性,弊大于利(例如,您被困住了)。
| 归档时间: |
|
| 查看次数: |
3760 次 |
| 最近记录: |