Guideline 4.0
Guideline 4.0 - Design: Excessive or Inappropriate Alerts
Our Take
Apple rejected your app because it displays too many alerts, uses system alerts (UIAlertController) for non-critical information, or presents alerts at inappropriate times such as immediately on launch. System alerts are interruptive by design and should be reserved for critical decisions or errors. Using them for tips, announcements, ratings prompts, onboarding information, or non-blocking messages degrades the user experience.
Resolution Guide
**Audit all alert usage** — Search your codebase for `UIAlertController`, `.alert(`, and `Alert(` to find every alert in your app. Categorize each as critical (requires user decision) or non-critical (informational).
**Replace non-critical alerts with inline UI** — For success confirmations, use a toast or banner that auto-dismisses. For tips, use an inline card or tooltip. For feature announcements, use a non-modal sheet or "what's new" screen:
```swift
// Instead of an alert for success:
withAnimation { showSuccessBanner = true }
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation { showSuccessBanner = false }
}
```
**Use SKStoreReviewController for ratings** — Never build a custom rating alert. Use the system API which Apple controls and rate-limits automatically:
```swift
import StoreKit
if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
SKStoreReviewController.requestReview(in: scene)
}
```
**Stagger permission requests** — Do not ask for notifications, location, camera, etc. all at once on launch. Request each permission only when the user reaches a feature that needs it, and explain why before showing the system dialog.
**Remove alert chains** — Never present alerts sequentially. If you need multiple pieces of information from the user, use a multi-step form or onboarding flow instead.
**Reserve alerts for critical decisions** — Alerts should be used for: destructive actions ("Delete this item?"), authentication failures, required updates, or irrecoverable errors. Almost nothing else warrants a system alert.
**Test the first launch experience** — Reset your app and walk through the first launch. Count how many alerts appear before the user can interact with the main content. The ideal number is zero.
Example Rejection Email
Consider Appealing
Do not appeal. Reduce the number of alerts and replace non-critical ones with inline UI. This rejection is straightforward to fix and Apple is consistent about enforcing it.
Before & After
// Excessive: alert for non-critical success message
func saveCompleted() {
let alert = UIAlertController(
title: "Success!",
message: "Your changes have been saved.",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
// Non-interruptive: inline toast banner for success
@State private var showSavedBanner = false
func saveCompleted() {
withAnimation(.spring()) { showSavedBanner = true }
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation { showSavedBanner = false }
}
}
// In the view body:
.overlay(alignment: .top) {
if showSavedBanner {
Text("Changes saved")
.padding()
.background(.thinMaterial, in: Capsule())
.transition(.move(edge: .top).combined(with: .opacity))
}
}
What changed: Replace success confirmation alerts with a non-interruptive toast banner that auto-dismisses. The user sees the confirmation without having to tap 'OK' to continue using the app.
// Custom rating alert — violates guidelines
let alert = UIAlertController(
title: "Enjoying the app?",
message: "Please rate us on the App Store!",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "Rate Now", style: .default) { _ in
// open App Store
})
alert.addAction(UIAlertAction(title: "Later", style: .cancel))
present(alert, animated: true)
// System rating prompt — Apple-controlled, rate-limited
import StoreKit
func requestReviewIfAppropriate() {
guard let scene = UIApplication.shared.connectedScenes
.first(where: { $0.activationState == .foregroundActive })
as? UIWindowScene else { return }
SKStoreReviewController.requestReview(in: scene)
}
What changed: SKStoreReviewController presents Apple's own rating dialog, which is rate-limited to 3 times per year per app. Apple controls the presentation and timing, ensuring it doesn't annoy users.
Community Solutions · 0
Sign in to share your solution.
More Guideline 4 (Design) rejections
- Guideline 4.0 - Design: App Does Not Include iOS Features
- Guideline 4.0 - Design: App Looks Like a Website
- Guideline 4.0 - Design: Apple Pay Button Not Following Guidelines
- Guideline 4.0 - Design: Blurry Icons or Low-Resolution Assets
- Guideline 4.0 - Design: Broken Layout on iPad
- Guideline 4.0 - Design: Content Clipped by Notch or Safe Area