Project 16 — Hot Prospects
Hot Prospects помогает обмениваться контактами на встречах: показывает QR-код, сканирует чужой, разделяет людей на contacted/uncontacted и ставит напоминания.
Готовность к проекту
0 из 6 рекомендуемых тем изучено. Проект всегда доступен — это не ограничение.
Рабочий vertical slice
import SwiftUI
import Observation
struct Prospect: Identifiable {
let id = UUID()
var name: String
var isContacted = false
}
@Observable
final class Prospects {
var people = [
Prospect(name: "Анна"),
Prospect(name: "Борис", isContacted: true)
]
func toggleContacted(for person: Prospect) {
guard let index = people.firstIndex(where: { $0.id == person.id }) else { return }
people[index].isContacted.toggle()
}
}
struct ProspectList: View {
let prospects: Prospects
let contacted: Bool
var body: some View {
List {
ForEach(prospects.people.filter { $0.isContacted == contacted }) { person in
Button {
withAnimation {
prospects.toggleContacted(for: person)
}
} label: {
HStack {
Text(person.name)
Spacer()
Image(systemName: person.isContacted ? "checkmark.circle.fill" : "circle")
}
}
.buttonStyle(.plain)
.accessibilityLabel("\(person.name), \(person.isContacted ? "связались" : "ещё не связались")")
.accessibilityHint("Изменить статус контакта")
}
}
}
}
struct ContentView: View {
@State private var prospects = Prospects()
var body: some View {
TabView {
ProspectList(prospects: prospects, contacted: false)
.tabItem { Label("Новые", systemImage: "person.badge.plus") }
ProspectList(prospects: prospects, contacted: true)
.tabItem { Label("Связались", systemImage: "checkmark.circle") }
}
}
}Запусти slice, нажми на Анну во вкладке «Новые» и открой «Связались»: Анна исчезнет из первого фильтра и появится во втором. Это видимый эффект одной shared Prospects, которую читают оба tab. После этого добавляй QR и notifications по одному.
Shared data между tabs
В минимальном slice один observable container явно передаётся обоим экранам. Когда экранов станет больше, тот же объект удобно положить в environment для всего TabView. Каждый tab показывает фильтр одного Array, поэтому изменения сразу видны везде.
TabView {
ProspectsView(filter: .none).tabItem { Label("Everyone", systemImage: "person.3") }
ProspectsView(filter: .contacted).tabItem { Label("Contacted", systemImage: "checkmark.circle") }
}
.environment(prospects)Filter и sort
Не заводи три независимых Arrays. Computed property выбирает записи по enum filter, затем сортирует по выбранному правилу.
Context menu и swipe actions
Действия должны работать не только через скрытый long press. Для важных операций добавь видимую кнопку или swipe action и accessibility label.
QR codes
Core Image generator принимает Data и создаёт CIImage; масштабируй его без интерполяции, иначе края станут размытыми и код хуже сканируется.
Сканер требует camera permission и строки NSCameraUsageDescription. До показа камеры проверь authorization status, отдельно обработай denied/restricted и предложи ручной ввод как доступный fallback. QR payload версионируй и валидируй, например swiftcourse:v1:<uuid>: произвольную строку с камеры нельзя сразу считать доверенной моделью контакта. В Simulator показывай явное состояние «Камера недоступна».
Local notifications
Сначала запроси разрешение в контексте понятного действия. Затем создай content, trigger и request с уникальным identifier.
Перед запросом прочитайте notificationSettings(): для .denied покажите путь в Settings, для .notDetermined объясните пользу и запросите разрешение, а для разрешённого состояния планируйте уведомление. Не просите camera и notifications при первом запуске без контекста.
let content = UNMutableNotificationContent()
content.title = "Связаться с \(prospect.name)"
content.sound = .default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: false)Испытание
Задание
Добавь ручное редактирование контакта, отмену reminder и сохранение prospects в SwiftData.Что уже нужно знать
Arrays, observable state, Codable/SwiftData, context menus и notifications.Показать подсказку 1
Показать подсказку 2
Показать решение
UNUserNotificationCenter.current()
.removePendingNotificationRequests(withIdentifiers: [prospect.id.uuidString])Что ты сейчас использовал
Swift Знакомые основы
SwiftUI Новое в проекте
- + Observable state
- + SwiftData
- + TabView
- + Notifications
- + QR codes
Дальше применим gestures к карточкам: Flashzilla →.
Комментарии
Вопросы, находки и подсказки по этой странице.
Войди, чтобы задать вопрос или помочь другим.