Unwrapping optionals with if let and guard let
Compare local conditional handling with early exits when working with optional values.
Read the Korean original on Velog ↗English translation

Both forms unwrap an optional, but they shape the surrounding control flow differently. Choose based on whether the value is needed inside a short conditional branch or throughout the rest of the scope.
if let
Optional binding runs the branch only when the value is not nil. An else branch is optional. The unwrapped constant is available inside the successful branch.
struct UnwrapStudy: View {
@State var currentUserID: String? = "Snack"
@State var displayText: String? = nil
@State var isLoading: Bool = false
var body: some View {
VStack {
// When displayText is not nil
if let text = displayText {
Text(text)
.font(.title)
}
if isLoading {
ProgressView()
}
}
.onAppear {
loadData()
}
}
func loadData() {
// When currentUserID is not nil
if let userID = currentUserID {
isLoading = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
displayText = "The username is \(userID)."
isLoading = false
}
// When currentUserID is nil
} else {
displayText = "There is no user data."
}
}
}
guard let
- Use
guard letinside a scope to check that a required value exists. - The
elsebranch handles failure and must exit the current scope, for example withreturn,break,continue, orthrow. - After the guard succeeds, the unwrapped value is available in the remaining scope.
- Handling failure first keeps the main path less indented and easier to follow.
struct UnwrapStudy: View {
@State var currentUserID: String? = "Snack"
@State var displayText: String? = nil
@State var isLoading: Bool = false
var body: some View {
VStack {
// When displayText is not nil
if let text = displayText {
Text(text)
.font(.title)
}
if isLoading {
ProgressView()
}
}
.onAppear {
loadData()
}
}
func loadData() {
guard let userID = currentUserID else {
// When currentUserID is nil
displayText = "There is no user data."
return // Return from the function
}
// When currentUserID is not nil
isLoading = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
displayText = "The username is \(userID)."
isLoading = false
}
}
}