所有分类
  • 所有分类
  • 实时新闻

七爪源码:使用面向协议的编程处理SwiftUI视图

swiftui 展示了一种新的、更快、更高效的视图构建方式。 声明式编程是一项了不起的技术,SwiftUI 以及 Android 上的 Jetpack Compose 和 Flutter 的 Widget 正在使 w 视图构建变得愉快和主动。 但是,构建视图并不是移动开发人员生活的唯一部分。 在瞬息万变的环境中设计一个良好的、可扩展的和有效的 UI 可以很好地与任何类型的模型配合使用是一个相当大的挑战。

一般是怎么处理的?

在最常见和最简单的情况下,有两个对象:

  • 模型:从存储库中获取,可以是来自 API 的数据、来自数据库的记录或来自多种服务的其他类型的数据。 模型通常公开公开字段,很少公开方法。
  • view:实现 View 协议的结构,在最好的情况下只有一个依赖。

依赖关系图如下所示:

七爪源码:使用面向协议的编程处理SwiftUI视图

和代码:

structUserListView:View{varviewModel:UserViewModelvarbody: someView{switchviewModel.state {case.loading:LoaderView()case.loaded(letusers):List(users) {UserRow(user: $0)
            }case.failure(leterror):Text(error.localzableDescriptioin)
        }
    }
}structUserRow:View{letuser:Uservarbody: someView{VStack(alignment: .leading) {Text("\(user.id): \(user.name)")Text(user.email)
        }
    }
}

将模型直接注入视图是一个……有效的想法。 不要理解我的错误,我并不是说这是错误或错误的方法。 我相信当模型是原始类型时,这些解决方案运行良好,例如

  • 细绳
  • 布尔值
  • 数字

将上述类型注入视图可以避免多余的样板代码,如额外的抽象层或仅为满足多余需求而创建的 ViewModel 类。 此外,当一个大视图被分成多个小视图时,更改特定的视图很容易,特别是当它们不依赖于模型,而是依赖于原始类型时。

第二个,也是非常流行的方法来自 MVVM 架构。 在这种架构中,大多数视图都有自己的 ViewModel。

七爪源码:使用面向协议的编程处理SwiftUI视图

MVVM 在模型和视图之间实施了一个额外的层,称为 ViewModel。 模型和视图对象与前面的例子没有什么不同,一个重要的区别是如何将可显示的数据传递给视图。

structUserListView:View{
    @ObservedObjectvarviewModel:UserViewModelvarbody: someView{VStack{List(viewModel.elements) {UserRow(viewModel: $0)
            }
        }
        .onAppear {
            viewModel.fetchData()
        }
    }
}classUserViewModel:ObservableObject{

    @Publishedvarelements: [UserRowViewModel] = []privateletuserRepository:UserRepositoryinit(userRepository:UserRepository) {self.userRepository = userRepository
    }funcfetchData(){
        elements = userRepository.fetch().map{UserRowViewModel(user: $0) }
    }
}structUserRow:View{letviewModel:UserRowViewModelvarbody: someView{VStack(alignment: .leading) {Text(viewModel.header)Text(viewModel.description)
        }
    }
}structUserRowViewModel:Identifiable{varid:String{
        user.id
    }varheader:String{"\(user.id): \(user.name)"}vardescription:String{
        user.email
    }letuser:User}

这通常是解决视图层和模型层之间的依赖问题的好方法。在开发人员中广为人知。这种方法的最大优点是

  • 层分离:抽象负责什么一目了然,领域模型和UI没有依赖关系
  • 可测试性:视图模型通常很容易测试
  • 视图模型通信:发布者或委托可以从父视图模型传递到子视图模型并在上层处理

但我也看到了一些缺点:

  • 不同类型的来源:ViewModel 只接受一个模型。意味着一个类不可扩展,如果出现新的业务案例,则必须重写它。例如。

出现了附加要求,在用户列表中,它不仅必须是用户,还必须是组,这是一个完全不同的模型。

  • 样板代码和 ViewModel 实际上只是模型的包装。除了公开一个字段以供查看外,别无他法。
  • 在大多数情况下,为每个视图创建一个新的视图模型似乎是一种过度工程

知道了这些问题,我们可以顺利地继续:

面向协议的方法

我推荐介于 MVVM 和原始模型之间的东西——视图依赖。为了避免:

  • 很多视图模型文件,通常只为私有 MVVM 架构而创建,什么都不做,只描述如何将模型转换为视图。

并确保:

  • 良好的可测试性水平
  • 轻松采用不同类型的资源
  • 在需要时轻松从协议转换为视图模型

协议方法的主要目标是创建一个易于适应和灵活的环境,以适应任何类型的业务需求。

它的关键部分是一个通常称为 DisplayableModel 的协议,它描述了应该在单个视图上确切显示的内容。

七爪源码:使用面向协议的编程处理SwiftUI视图

它可能看起来像这样

protocolUserRowDisplayableModel{varname:String{get}varavatar:ImageSource{get}
}

可显示协议定义了视图所需的所有数据。 在这里,它是一个名称变量,描述每个用户行视图都有一个名称文本元素和图像源,以便在屏幕上显示用户头像。 可显示模型内部没有模型,适用于任何类型的业务逻辑。 其中的属性对于正确显示视图至关重要。 里面没有更多的处理。 DisplayableModel 的目的是尽可能的清晰和小巧。

视图接受 DisplayableModel 协议作为入口点。

structUserRow:View{letmodel:UserRowDisplayableModelvarbody: someView{// Body}
}

由 UserRow 处理的每个模型都必须实现 DisplayableModel 协议,例如

接下来,必须在 UserView 上显示的每个模型都必须实现 DisplayableModel 协议,例如

extensionUser:UserRowDisplayableModel{varname:String{"\(firstName), \(lastName)"}varavatar:ImageSource{ImageSource(url: avatarUrl)
    }
}extensionGroup:UserRowDisplayableModel{varname:String{"\(groupName)"}varavatar:ImageSource{MixedAvatarImageSource(urls: users.map{ $0.avatar })
    }
}

剩下的就看开发商了。 可显示的模型可以来自 ViewModel、observable store 或您希望的任何来源。

使用这种技术,您可以构建快速、可扩展且易于采用的视图。

视图模型示例

我想结合最著名的架构 MVVM 来展示它的外观。 此外,当您只下载一个模型时,我还展示了一个比普通案例复杂一点的商业案例示例。 要求与上述相同:

您不仅要显示用户,还要在同一个列表视图中显示组

classViewModel:ObservableObject{
    @Publishedvarelements: [UserRowDisplayableModel] = []funcfetch(){
        Task {asyncletusers =awaitfetchUsers()// Return [User]asyncletgroup=awaitfetchGroups()// Return [Group]elements =awaitusers +group}
    }///Private methods}
  1. 可显示模型存储在数组中并标记为已发布
  2. 当视图出现时,或者当它需要时,方法 fetch 被调用
  3. 在其中,ViewModel 异步获取两个不同的模型:Group 和 User
  4. 因为两者都实现了 UserRowDisplayableModel 它可以很容易地传递给已发布的元素。

结论

协议是迄今为止 Swift 拥有的最好的特性之一。 它允许编写一个简单、描述良好且清晰的代码,该代码具有一个且只有一个易于理解的目的。

我强烈建议所有开发人员将大部分代码封装到协议中,不仅因为团队中的其他工程师清晰易懂,而且更容易测试。

原文链接:https://www.w1ym.com/83145/,转载请注明出处~~~
0

评论0

请先

站点公告

【温馨提示】 本站不建议您对本站支付任何费用或开通任何会员本站99%资源为免费资源只提供共享不提供技术支持,本站资源主要以学习开发为主,本站是为个人资源记录学习研究等情况而建立,如特殊原因下载,需在24小时删除相关资源。本站资源均来自互联网收集或网友分享,若有侵权,请联系站长删除,谢谢。立即查看
显示验证码
没有账号?注册  忘记密码?