What You Will Build
A multi-step account deletion flow with a safety confirmation: the user taps "Delete My Account," a text field appears asking them to type DELETE, and only then can they confirm. After confirmation, a success state is shown. This pattern follows Apple's App Store guidelines for account deletion and is used in settings screens across apps that handle user accounts.
Why This Pattern Matters
Apple requires all apps with account creation to offer account deletion. But accidental deletion is catastrophic, so a confirmation step is essential. This tutorial shows a progressive disclosure pattern where each step requires explicit user action, combined with smooth transitions.
Key SwiftUI Concepts
- Progressive disclosure with
@Statebooleans controlling which UI elements appear. - .disabled() modifier to prevent the confirm button from working until the user types the exact confirmation text.
- .transition() for smooth enter/exit animations on conditional views.
The Complete Deletion Flow
struct DeleteAccountDemo: View {
@State private var showConfirmation = false
@State private var confirmText = ""
@State private var isDeleted = false
var body: some View {
VStack(spacing: 24) {
if isDeleted {
// Success state
VStack(spacing: 16) {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 60))
.foregroundStyle(.green)
Text("Account Deleted")
.font(.title2.bold())
Button("Reset Demo") {
withAnimation {
isDeleted = false
showConfirmation = false
confirmText = ""
}
}
.buttonStyle(.bordered)
}
.transition(.scale.combined(with: .opacity))
} else {
Image(systemName: "person.crop.circle.badge.xmark")
.font(.system(size: 60))
.foregroundStyle(.red)
Text("Delete Account")
.font(.title2.bold())
Text("This action cannot be undone. All your data "
+ "will be permanently removed.")
.font(.subheadline)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal)
if showConfirmation {
VStack(spacing: 12) {
Text("Type DELETE to confirm")
.font(.caption)
.foregroundStyle(.secondary)
TextField("DELETE", text: $confirmText)
.textFieldStyle(.roundedBorder)
.autocorrectionDisabled()
.padding(.horizontal, 40)
Button("Confirm Deletion") {
withAnimation(.spring) {
isDeleted = true
}
}
.buttonStyle(.borderedProminent)
.tint(.red)
.disabled(confirmText != "DELETE")
}
.transition(.move(edge: .bottom)
.combined(with: .opacity))
}
if !showConfirmation {
Button("Delete My Account") {
withAnimation(.spring) {
showConfirmation = true
}
}
.foregroundStyle(.red)
.fontWeight(.semibold)
}
}
}
.padding()
.animation(.spring, value: isDeleted)
}
}
The Three States
- Initial: Warning icon, explanation text, and a red "Delete My Account" button.
- Confirmation: A text field requiring the user to type "DELETE" and a disabled confirm button that enables only when the text matches exactly.
- Success: A green checkmark with "Account Deleted" message and a reset button for the demo.
Tips and Pitfalls
- .autocorrectionDisabled() prevents the system from autocorrecting "DELETE" to something else.
- String comparison is case-sensitive: The user must type exactly "DELETE". For production, consider
confirmText.uppercased() == "DELETE"to be more forgiving. - .spring animation on the confirm button appearance prevents jarring layout shifts.
- In production: Make an API call in the confirm action and show a loading state before the success screen.
iOS Version: iOS 15+