------------------------- | Task Manager | ------------------------- | Work | | - Email client [ ] | | - Prepare report [x] | |-----------------------| | Home | | - Clean kitchen [ ] | | - Fix sink [ ] | -------------------------
Task and TaskGroup in iOS Swift - Mini App: Build & Ship
import SwiftUI struct TaskItem: Identifiable { let id = UUID() let name: String let done: Bool } struct TaskGroupData: Identifiable { let id = UUID() let category: String let tasks: [TaskItem] } struct TaskManagerView: View { @State private var taskGroups: [TaskGroupData] = [] @State private var isLoading = true var body: some View { NavigationView { Group { if isLoading { ProgressView("Loading tasks...") } else { List { // TODO: Show grouped tasks here } .listStyle(GroupedListStyle()) } } .navigationTitle("Task Manager") .task { // TODO: Load tasks asynchronously using TaskGroup } } } } struct TaskManagerView_Previews: PreviewProvider { static var previews: some View { TaskManagerView() } }
import SwiftUI struct TaskItem: Identifiable { let id = UUID() let name: String let done: Bool } struct TaskGroupData: Identifiable { let id = UUID() let category: String let tasks: [TaskItem] } struct TaskManagerView: View { @State private var taskGroups: [TaskGroupData] = [] @State private var isLoading = true var body: some View { NavigationView { Group { if isLoading { ProgressView("Loading tasks...") .progressViewStyle(CircularProgressViewStyle()) } else { List { ForEach(taskGroups) { group in Section(header: Text(group.category)) { ForEach(group.tasks) { task in HStack { Text(task.name) Spacer() Image(systemName: task.done ? "checkmark.square" : "square") .foregroundColor(task.done ? .green : .secondary) .accessibilityLabel(task.done ? "Done" : "Not done") } .padding(.vertical, 4) } } } } .listStyle(GroupedListStyle()) } } .navigationTitle("Task Manager") .task { await loadTasks() } } } func loadTasks() async { isLoading = true var loadedGroups: [TaskGroupData] = [] await withTaskGroup(of: TaskGroupData?.self) { group in let categories = ["Work", "Home"] for category in categories { group.addTask { // Simulate network delay try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 second switch category { case "Work": let tasks = [ TaskItem(name: "Email client", done: false), TaskItem(name: "Prepare report", done: true) ] return TaskGroupData(category: category, tasks: tasks) case "Home": let tasks = [ TaskItem(name: "Clean kitchen", done: false), TaskItem(name: "Fix sink", done: false) ] return TaskGroupData(category: category, tasks: tasks) default: return nil } } } for await result in group { if let groupData = result { loadedGroups.append(groupData) } } } // Sort groups by category name taskGroups = loadedGroups.sorted { $0.category < $1.category } isLoading = false } } struct TaskManagerView_Previews: PreviewProvider { static var previews: some View { TaskManagerView() } }
This solution uses Swift concurrency's withTaskGroup to load task groups asynchronously. Each category is loaded in its own child task with a simulated 1-second delay to mimic network loading.
After all tasks complete, the results are collected and sorted alphabetically by category. The UI shows a loading spinner while loading, then displays the tasks grouped by category in a List with sections.
Each task row shows the task name and a checkbox icon that is green if done, or gray if not. Accessibility labels describe the checkbox state for screen readers.
This approach teaches how to use TaskGroup to run multiple async tasks concurrently and update UI state when all finish.
------------------------- | Task Manager | ------------------------- | Work | | - Email client [ ] | | - Prepare report [x] | |-----------------------| | Home | | - Clean kitchen [ ] | | - Fix sink [ ] | -------------------------
Practice
Task in Swift concurrency?Solution
Step 1: Understand what
ATaskdoesTaskis designed to run one asynchronous job in the background.Step 2: Differentiate from
TaskGroupTaskGroupruns many jobs together, not a single job.Final Answer:
To run a single background job asynchronously -> Option AQuick Check:
Task= single background job [OK]
- Confusing Task with TaskGroup
- Thinking Task updates UI directly
- Assuming Task pauses app execution
Task that prints "Hello" asynchronously?Solution
Step 1: Recall correct
The correct way to create aTasksyntaxTaskis usingTask { ... }with a closure.Step 2: Check other options for errors
TaskGroupis not used like this;async TaskandTask.runare invalid syntax.Final Answer:
Task { print("Hello") } -> Option DQuick Check:
CorrectTasksyntax = Task { print("Hello") } [OK]
- Using TaskGroup instead of Task
- Adding 'async' keyword before Task
- Trying to call Task.run which doesn't exist
TaskGroup?
func fetchData() async {
await withTaskGroup(of: String.self) { group in
group.addTask { "A" }
group.addTask { "B" }
for await result in group {
print(result)
}
}
}
fetchData()Solution
Step 1: Understand
TaskGroupconcurrencyTaskGroupruns tasks concurrently and collects results as they finish, order is not guaranteed.Step 2: Analyze the code output
Since tasks return "A" and "B" concurrently, the print order can be either "A" then "B" or "B" then "A".Final Answer:
Either A then B or B then A (order not guaranteed) -> Option AQuick Check:
TaskGroup results order is unpredictable [OK]
- Assuming TaskGroup results are always ordered
- Thinking tasks run sequentially
- Believing syntax error prevents output
TaskGroup:
await withTaskGroup(of: Int.self) { group in
group.addTask { 1 }
group.addTask { 2 }
for result in group {
print(result)
}
}Solution
Step 1: Check iteration over TaskGroup
Iteration over aTaskGroupis asynchronous, so it requiresfor await.Step 2: Identify the error in the code
The code usesfor result in groupwithoutawait, causing a compile error.Final Answer:
Using 'for' instead of 'for await' to iterate the group -> Option BQuick Check:
Async iteration needs 'for await' [OK]
- Forgetting 'await' in asynchronous loops
- Confusing TaskGroup type declaration
- Thinking tasks can't be added inside withTaskGroup
TaskGroup is best to achieve this?Solution
Step 1: Understand concurrency needs
Fetching from three servers concurrently requires running tasks in parallel to save time.Step 2: Choose the best Swift concurrency pattern
TaskGroupallows adding multiple tasks and collecting results asynchronously, perfect for this use case.Final Answer:
Use aTaskGroupto add three tasks fetching data, then collect all results asynchronously -> Option CQuick Check:
TaskGroup = concurrent multiple tasks [OK]
- Running tasks sequentially losing concurrency benefits
- Using synchronous calls blocking main thread
- Using single Task for sequential fetches
Ready to prove it?
Pass the test and this pattern moves unknown
