CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
iOS 开发入门/课时/综合练习
RawBlame
课时目录12 节

iOS 开发入门

  1. 01iOS 生态与 Xcode
  2. 02Swift 语言基础
  3. 03UIKit 与 Interface Builder
  4. 04SwiftUI 入门
  5. 05架构模式
  6. 06数据持久化
  7. 07网络与 API
  8. 08并发与响应式
  9. 09依赖管理与 CI
  10. 10测试与调试
  11. 11上架与 ASO
  12. 12综合练习

综合练习

Todo App(SwiftUI + URLSession)。

40 分钟

综合练习:Todo App

用本 track 所学完成 Todo App:SwiftUI 界面 + MVVM + URLSession(或本地 Core Data)+ 单元测试。对照 /roadmaps/ios 全路径验收。

学习目标

  • 完成可运行的 Todo 应用(增删改查 + 完成切换)
  • 实践 MVVM 分层与 Repository 抽象
  • 可选对接 Spring Boot API 或纯本地持久化
  • 规划 TestFlight 或下一步 Swift 进阶

核心概念

项目结构

CodeRoadMapTodo/
├── App/
│   └── CodeRoadMapTodoApp.swift
├── Models/
│   └── TodoItem.swift
├── ViewModels/
│   └── TodoListViewModel.swift
├── Views/
│   ├── TodoListView.swift
│   └── TodoRowView.swift
├── Services/
│   ├── TodoRepository.swift
│   └── APIClient.swift
└── Tests/
    └── TodoListViewModelTests.swift

Model 与 ViewModel

struct TodoItem: Identifiable, Codable, Equatable {
    let id: UUID
    var title: String
    var isCompleted: Bool
    var createdAt: Date
}

@MainActor
final class TodoListViewModel: ObservableObject {
    @Published var items: [TodoItem] = []
    @Published var newTitle = ""
    @Published var isLoading = false
    @Published var errorMessage: String?

    private let repository: TodoRepositoryProtocol

    init(repository: TodoRepositoryProtocol = TodoRepository()) {
        self.repository = repository
    }

    func load() async {
        isLoading = true
        do {
            items = try await repository.fetchAll()
        } catch {
            errorMessage = error.localizedDescription
        }
        isLoading = false
    }

    func addTodo() async {
        let title = newTitle.trimmingCharacters(in: .whitespaces)
        guard !title.isEmpty else { return }
        let item = TodoItem(id: UUID(), title: title, isCompleted: false, createdAt: .now)
        do {
            try await repository.save(item)
            items.append(item)
            newTitle = ""
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    func toggle(_ item: TodoItem) async {
        var updated = item
        updated.isCompleted.toggle()
        try? await repository.save(updated)
        if let idx = items.firstIndex(where: { $0.id == item.id }) {
            items[idx] = updated
        }
    }

    func delete(at offsets: IndexSet) async {
        for index in offsets {
            let item = items[index]
            try? await repository.delete(item)
        }
        items.remove(atOffsets: offsets)
    }
}

SwiftUI 主界面

struct TodoListView: View {
    @StateObject private var viewModel = TodoListViewModel()

    var body: some View {
        NavigationStack {
            Group {
                if viewModel.isLoading {
                    ProgressView("加载中…")
                } else {
                    List {
                        ForEach(viewModel.items) { item in
                            TodoRowView(item: item) {
                                Task { await viewModel.toggle(item) }
                            }
                        }
                        .onDelete { offsets in
                            Task { await viewModel.delete(at: offsets) }
                        }
                    }
                }
            }
            .navigationTitle("待办")
            .toolbar {
                ToolbarItem(placement: .topBarTrailing) {
                    EditButton()
                }
            }
            .safeAreaInset(edge: .bottom) {
                HStack {
                    TextField("新待办", text: $viewModel.newTitle)
                        .textFieldStyle(.roundedBorder)
                    Button("添加") {
                        Task { await viewModel.addTodo() }
                    }
                    .disabled(viewModel.newTitle.trimmingCharacters(in: .whitespaces).isEmpty)
                }
                .padding()
            }
            .task { await viewModel.load() }
            .alert("错误", isPresented: .constant(viewModel.errorMessage != nil)) {
                Button("确定") { viewModel.errorMessage = nil }
            } message: {
                Text(viewModel.errorMessage ?? "")
            }
        }
    }
}

功能清单

功能要求
列表展示List + 完成态图标
新增底部输入框 + 添加按钮
切换完成点击行或 checkbox
删除左滑或 Edit 模式
持久化Core Data 或 REST API
测试ViewModel 至少 2 个单元测试

与 Java/Spring 或 React 对照

本练习可选后端Web 等价
TodoRepositorySpring @RestController CRUDReact Todo + fetch
MVVM ViewModel@ServiceCustom Hook
SwiftUI List—<ul> + map
XCTest@WebMvcTestVitest

选做:用 spring-boot-essentials 实现 /api/todos,iOS 端 URLSession 联调。

练习清单

  • 完成上述 Todo App,模拟器可运行
  • 编写 ViewModel 成功/失败单元测试
  • 在 /roadmaps/ios 标记已学节点
  • 可选:Archive 上传 TestFlight

小结

iOS 核心在本 track 闭环:Swift → SwiftUI → MVVM → 网络/存储 → 测试 → 上架。 下一步可深入 Swift 进阶、Combine 或读 Apple 官方 Sample Code。

加载本节练习…
← 上一课上架与 ASO