[代码审查挑战]解释第二个问题:SwiftUI 和 SwiftData #try!

你好。我叫 Tomita,是一名负责天气和灾害服务的 iOS 应用工程师。

在前几天举行的 try!Swift Tokyo 2024 的 LINE Yahoo 企业展位上,我们举办了代码审查挑战赛。 代码审查挑战赛是一项公共代码审查,旨在将坏代码变成好代码。我们在本次活动中采取了与以往活动相同的举措,目的是让参与者对技术产生兴趣,并帮助员工通过外部各方的评论进行学习。

在这篇文章中,我将解释这次提出的 Code Review Challenge 的第二个问题。

问题代码

import SwiftUI
import SwiftData

struct ContentView: View {
    // Initialized ModelContainer will be passed from the parent view.
    @Environment(.modelContext) private var modelContext
    // Todo class is decleared with @Model annotation on another file.
    @Query private var todos: [Todo]
    @State private var showingAddItemAlert = false
    @State private var newTodo = ""

    var body: some View {
        NavigationView {
            List {
                ForEach(Array(todos.filter { !$0.isDone }.enumerated()), id: .offset) { index, todo in
                    Button(todo.title) {
                        todos[index].isDone.toggle()
                    }
                }
                .onDelete(perform: deleteItems)
            }
            .toolbar {
                Button(action: {
                    showingAddItemAlert = true
                }, label: {
                    Image(systemName: "plus")
                })
            }
            .overlay {
                if todos.count == 0 {
                    ContentUnavailableView(
                        "All Done",
                        systemImage: "checkmark",
                        description: Text("You've completed all your tasks. Congratulations!")
                    )
                }
            }
            .alert("Add New Todo", isPresented: $showingAddItemAlert) {
                TextField("New Todo", text: $newTodo)
                Button("Add", action: addItem)
                    .disabled(newTodo.isEmpty)
                Button("Cancel", role: .cancel, action: {})
            }
        }
    }

    private func addItem() {
        let newItem = Todo(title: newTodo)
        modelContext.insert(newItem)
        newTodo = ""
    }

    private func deleteItems(offsets: IndexSet) {
        for index in offsets {
            modelContext.delete(todos[index])
        }
    }
}

这是使用 SwiftData 的 TODO 应用程序的代码。其中包括10多个改进,但这次我主要介绍以下两点。

  1. 关于从 SwiftData 获取的数据的处理
  2. 关于使用警报添加 TODO

关于从 SwiftData 获取的数据的处理

上面的代码中,显示List时todos正在过滤。因此,显示上没有问题,但是在删除或编辑时(本例中将任务的isDone设置为true),会操作移动索引的元素。这个问题可以通过搜索过滤后的TODO索引来解决,但搜索会增加处理时间,因此不是最佳解决方案。这个问题可以通过使用SwiftData提供的API来解决。实际修改后的代码如下。

// 前略
    @Query(filter: #Predicate { !$0.isDone }) private var todos: [Todo]
    @State private var showingAddItemAlert = false

    var body: some View {
        NavigationView {
            List {
                ForEach(todos) { todo in
                    Button(todo.title) {
                        todo.isDone.toggle()
                    }
                }
                .onDelete(perform: deleteItems)
            }
// 後略

检索 Swift Data 中存储的内容时@Query使用 检索数据,但可以在此处添加过滤和排序等选项。这允许您在检索元素时对其进行过滤。您还可以在更改元素时进行过滤。

1713268110
#代码审查挑战解释第二个问题SwiftUI #和 #SwiftData
2024-04-16 02:00:00

Leave a Reply

Your email address will not be published. Required fields are marked *

近期新闻​

编辑精选​