- Nov 7, 2025
Mastering the Redacted Modifier in SwiftUI: Building Elegant Loading and Placeholder States
- DevTechie
- SwiftUI
The redacted modifier in SwiftUI is a powerful tool that allows developers to visually mask or obscure the content of their views, typically for the purpose of showing loading placeholders or hiding sensitive information until data is ready. This modifier opens the door to creating elegant skeleton screens, improving user experience by reducing abrupt changes in UI states, and giving apps a polished, professional appearance.
What Is the Redacted Modifier?
The redacted(reason:) modifier was introduced in iOS 14 as a way to transform text, images, and other views into placeholder representations. Its primary use case is to simulate the final structure of your interface while waiting for actual data to load.
For example, instead of displaying a spinning progress view or a blank screen while an API call is in progress, you can use the redacted modifier to display “skeleton” versions of your app’s content. This helps users anticipate the layout of incoming content and keeps the interface lively.
struct RedactedViewExample: View {
var body: some View {
Text("Hello, DevTechie!")
.font(.largeTitle)
.redacted(reason: .placeholder)
}
}In the above snippet, the text is shown as a gray box or line, indicating a placeholder for the real content.
Why Use Redacted?
Improve Loading Experience: Placeholder skeletons feel faster and smoother than blank spaces or loading spinners.
Maintain Layout Consistency: Prevents the layout from “jumping” when content arrives.
Privacy and Sensitivity: The modifier can also be utilized to obscure sensitive information until appropriate conditions are met
How Does It Work?
At its core, the redacted modifier accepts a RedactionReasons option set. In iOS 14+, the only built-in reason is .placeholder, but developers can extend this set to implement custom redaction behavior.
When you apply .redacted(reason: .placeholder) the system overlays visual placeholders across the targeted views. You can apply this modifier to individual views or to container views, effectively redacting all subviews.
Let’s look at a basic use case — showing an article’s title and author with redacted placeholders while waiting to load:
struct Article {
let title: String
let author: String
}
struct ArticleView: View {
@State var article: Article?
var body: some View {
VStack(alignment: .leading) {
Text(article?.title ?? "placeholder-copy-title")
.font(.headline)
Text(article?.author ?? "placeholder-copy-author")
.font(.subheadline)
}
.padding()
.redacted(reason: article == nil ? .placeholder : [])
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.article = Article(title: "Apple Foundation Models!", author: "DevTechie")
}
}
}
}This ensures the shape of the view is visible even before the real data is present, making loading states seamless.
Redacted works well with container views. You may apply the modifier at the top level for a more comprehensive skeleton:
struct CourseView: View {
var body: some View {
VStack(alignment: .leading) {
Image("PostImage")
.resizable()
.frame(width: 100, height: 100)
.clipShape(Circle())
Text("DevTechie.com")
.font(.title)
.fontWeight(.bold)
.foregroundColor(Color.blue)
Text("Learn iOS App Development with DevTechie")
.font(.body)
Button("Bookmark", action: { print("Bookmark") })
}
.padding()
.redacted(reason: .placeholder)
}
}Here, all child views in CourseView get the placeholder overlay, which makes the entire section look like a skeleton
Sometimes you need to exclude specific subviews from a redacted parent view. SwiftUI provides unredacted() for this purpose:
struct CourseView: View {
var body: some View {
VStack(alignment: .leading) {
Image("PostImage")
.resizable()
.frame(width: 100, height: 100)
.clipShape(Circle())
.unredacted()
Text("DevTechie.com")
.font(.title)
.fontWeight(.bold)
.foregroundColor(Color.blue)
Text("Learn iOS App Development with DevTechie")
.font(.body)
Button("Bookmark", action: { print("Bookmark") })
}
.padding()
.redacted(reason: .placeholder)
}
}With this approach, you can finely tune which elements display as redacted, and which are shown as normal.
For convenience, consider creating a Boolean-based extension to make code cleaner:
extension View {
@ViewBuilder
func redacted(if condition: @autoclosure () -> Bool) -> some View {
redacted(reason: condition() ? .placeholder : [])
}
}Usage
struct CourseView: View {
@State private var isLoading = true
var body: some View {
VStack(alignment: .leading) {
Image("PostImage")
.resizable()
.frame(width: 100, height: 100)
.clipShape(Circle())
.unredacted()
Text("DevTechie.com")
.font(.title)
.fontWeight(.bold)
.foregroundColor(Color.blue)
Text("Learn iOS App Development with DevTechie")
.font(.body)
Button("Bookmark", action: { print("Bookmark") })
}
.padding()
.redacted(if: isLoading)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.isLoading = false
}
}
}
}Real-World Use Cases
Profile screens: Show placeholders for user names and avatars before loading.
News feeds: Redact headlines and images in feed cells while fetching updates.
Sensitive data: Hide salary, credit card, or other confidential information until user takes action.
Conclusion
The redacted modifier elevates loading states and privacy in SwiftUI apps, converting ordinary views into dynamic, user-friendly placeholders with minimal effort. By understanding how to apply, customize, and combine redaction reasons, you can give your applications a refined and thoughtful user experience in any data-laden context. Explore more with creative extensions, experiment with custom effects, and ensure your interfaces feel modern and responsive as users interact with your app at every stage.





