Tempura实战指南:从零开始构建一个iOS待办事项应用
Tempura实战指南从零开始构建一个iOS待办事项应用【免费下载链接】tempura-swiftA holistic approach to iOS development, inspired by Redux and MVVM项目地址: https://gitcode.com/gh_mirrors/te/tempura-swift你是否正在寻找一个完整的iOS开发架构解决方案Tempura是一个受Redux和MVVM启发的iOS开发框架它提供了一种简单、快速、免费的方式来构建可维护的iOS应用。本文将带你从零开始使用Tempura框架构建一个功能完整的待办事项应用让你体验终极的iOS开发体验为什么选择TempuraTempura框架采用了Redux单向数据流和MVVM架构的最佳实践为你提供了以下核心优势状态集中管理所有应用状态存储在单一数据结构中可预测的状态变更通过Action来更新状态确保数据流清晰自动UI同步状态变化时UI自动更新易于测试业务逻辑与UI分离便于单元测试和UI测试项目结构概览 让我们先来看看待办事项应用的项目结构Demo/ ├── Sources/ │ ├── Actions/ # 状态更新操作 │ │ └── ItemActions.swift │ ├── State/ # 应用状态定义 │ │ ├── AppState.swift │ │ └── Models.swift │ └── UI/ │ ├── ListScreen/ # 列表界面 │ │ ├── ListView.swift │ │ ├── ListViewController.swift │ │ └── ChildViewController.swift │ └── AddItemScreen/ # 添加项目界面 │ ├── AddItemView.swift │ └── AddItemViewController.swift第一步定义应用状态 ️在Tempura中所有应用状态都集中在一个数据结构中。让我们看看待办事项应用的状态定义// Demo/Sources/State/AppState.swift struct AppState: State { var items: [Todo] [ Todo(text: Pet my unicorn), Todo(text: Become a doctor.\nChange last name to Acula), Todo(text: Hire two private investigators.\nGet them to follow each other), Todo(text: Visit mars), ] var pendingItems: [Todo] { return self.items.filter { !$0.archived } } var completedItems: [Todo] { return self.items.filter { $0.completed } } }第二步创建数据模型 待办事项的数据模型定义了每个任务的基本属性// Demo/Sources/State/Models.swift struct Todo: Equatable { let id: String var text: String var completed: Bool var archived: Bool init(text: String, completed: Bool false) { self.id String.random(length: 16) self.text text self.completed completed self.archived false } }第三步定义状态更新操作 ⚡Tempura使用StateUpdater来更新状态确保状态变更的可预测性// Demo/Sources/Actions/ItemActions.swift struct AddItem: StateUpdater { var text: String func updateState(_ currentState: inout AppState) { let newItem Todo(text: self.text) currentState.items.insert(newItem, at: 0) } } struct ToggleItem: StateUpdater { var itemID: String func updateState(_ currentState: inout AppState) { let position currentState.items.firstIndex { $0.id itemID } guard let index position else { return } currentState.items[index].completed !currentState.items[index].completed } }第四步构建视图模型 视图模型从应用状态中选择所需的数据并将其转换为UI可以使用的格式// Demo/Sources/UI/ListScreen/ListView.swift struct ListViewModel: ViewModelWithLocalState, Equatable { var todos: [Todo] var archived: [Todo] var selectedSection: ListView.Section .todo var archivable: [Todo] var containsArchivableItems: Bool { return !self.archivable.isEmpty } init?(state: AppState?, localState: ListLocalState) { guard let state state else { return nil } self.todos state.pendingItems self.archived state.archivedItems self.selectedSection localState.selectedSection self.archivable state.archivableItems } }第五步创建UI视图 ️Tempura的UI视图实现了ViewControllerModellableView协议能够根据视图模型自动更新// Demo/Sources/UI/ListScreen/ListView.swift class ListView: UIView, ViewControllerModellableView { // 子视图 var todoButton UIButton(type: .custom) var archiveButton UIButton(type: .custom) var scrollView UIScrollView() var todoListView: CollectionViewTodoCell, SimpleSourceTodoCellViewModel! // 交互回调 var didTapAddItem: Interaction? var didToggleItem: ((String) - Void)? var didTapEditItem: ((String) - Void)? // 更新UI func update(oldModel: ListViewModel?) { guard let model self.model else { return } let todos model.todos.map { TodoCellViewModel(todo: $0) } self.todoListView.source SimpleSourceTodoCellViewModel(todos) // 切换选中部分 if model.selectedSection ! oldModel?.selectedSection { if case .todo model.selectedSection { self.scrollView.setContentOffset(.zero, animated: true) } else { let offset CGPoint(x: self.scrollView.bounds.width, y: 0) self.scrollView.setContentOffset(offset, animated: true) } } } }第六步实现视图控制器 视图控制器负责监听用户交互并分发相应的Action// Demo/Sources/UI/ListScreen/ListViewController.swift class ListViewController: ViewControllerWithLocalStateListView { override func setupInteraction() { self.rootView.didToggleItem { [unowned self] id in self.dispatch(ToggleItem(itemID: id)) } self.rootView.didTapAddItem { [unowned self] in self.dispatch(Show(Screen.addItem)) } self.rootView.didTapEditItem { [unowned self] itemID in self.dispatch(Show(Screen.addItem, animated: false, context: itemID)) } } }第七步实现导航功能 Tempura提供了强大的导航系统可以轻松处理屏幕间的跳转// Demo/Sources/Navigation/AppNavigation.swift extension ListViewController: RoutableWithConfiguration { var routeIdentifier: RouteElementIdentifier { return list screen } var navigationConfiguration: [NavigationRequest: NavigationInstruction] { return [ .show(add item screen): .presentModally({ [unowned self] _ in let addVC AddItemViewController(store: self.store) return addVC }) ] } }第八步添加UI测试 Tempura内置了强大的UI快照测试功能可以轻松测试各种UI状态// Demo/UITests/DemoUITests.swift import TempuraTesting class UITests: XCTestCase, ViewTestCase { func testListView() { self.uiTest(testCases: [ list_empty: ListViewModel(todos: [], archived: [], selectedSection: .todo), list_with_items: ListViewModel(todos: [ Todo(text: 学习Tempura), Todo(text: 构建待办事项应用) ], archived: [], selectedSection: .todo) ]) } }构建完成的待办事项应用 通过以上八个步骤你已经成功构建了一个完整的待办事项应用这个应用具有以下功能✅添加新任务- 用户可以轻松添加新的待办事项 ✅标记完成任务- 点击任务可以切换完成状态 ✅编辑任务- 点击任务可以编辑内容 ✅归档任务- 完成的任务可以归档 ✅查看归档任务- 可以查看已归档的任务列表 ✅删除归档任务- 可以清空已归档的任务Tempura的最佳实践 1. 保持状态不可变始终通过创建新的State实例来更新状态而不是直接修改现有状态。2. 使用ViewModel进行数据转换ViewModel应该只包含UI渲染所需的数据避免在视图中进行复杂的数据处理。3. 合理使用LocalState对于只影响单个屏幕的临时状态使用LocalState而不是全局AppState。4. 编写可测试的代码由于业务逻辑与UI分离你可以轻松为StateUpdater编写单元测试。5. 利用快照测试使用TempuraTesting进行UI快照测试确保UI在不同状态下的正确性。常见问题解答 ❓Q: Tempura适合什么规模的项目A: Tempura特别适合中小型应用它提供了清晰的数据流和易于维护的架构。Q: 如何调试状态变化A: 可以使用Katana的中间件功能来记录所有状态变化便于调试。Q: 是否支持SwiftUIA: 目前Tempura主要支持UIKit但可以与SwiftUI结合使用。Q: 性能如何A: Tempura的性能表现优秀因为它只更新需要更新的UI部分。总结 通过本教程你已经掌握了使用Tempura框架构建iOS应用的核心概念。Tempura的Redux风格状态管理和MVVM架构为你提供了一种清晰、可维护的iOS开发方式。从状态定义到UI实现再到测试和导航Tempura为你提供了一整套完整的解决方案。现在你已经准备好使用Tempura构建自己的iOS应用了记住良好的架构是成功应用的基础而Tempura正是帮助你实现这一目标的强大工具。开始你的Tempura之旅吧构建出下一个优秀的iOS应用【免费下载链接】tempura-swiftA holistic approach to iOS development, inspired by Redux and MVVM项目地址: https://gitcode.com/gh_mirrors/te/tempura-swift创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考