← Back to archive

Development · Swift

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

Development archive cover

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 let inside a scope to check that a required value exists.
  • The else branch handles failure and must exit the current scope, for example with return, break, continue, or throw.
  • 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
        }
    }
}
← Back to archive