In Combine, the compactMap
operator allows you to transform values while filtering out any nil
results. This operator is particularly useful in situations where you’re working with optional data and want to ignore any nil
values, passing only non-nil
data through the pipeline.
What is the compactMap
Operator?
The compactMap
operator in Combine takes each value emitted by a publisher and applies a transformation that can return an optional. If the result of the transformation is nil
, it’s discarded. Only non-nil
values are allowed to continue through the data stream. This is useful when dealing with values that may or may not be present, such as optional data from a text input or network response.
For example, you might use compactMap
to:
- Filter out invalid data from user input.
- Transform optional values and ignore
nil
results. - Filter and transform data based on specific criteria.
In this article, we’ll create a SwiftUI view that uses compactMap
to process text input from a user, attempting to convert the input to an integer. Only valid integer values will be displayed.
Example SwiftUI View with compactMap
Below is a SwiftUI view that uses a PassthroughSubject
to emit user input values from a text field. The compactMap
operator filters out any input that cannot be converted to an integer, so only valid integers are displayed in the view.
import SwiftUI
import Combine
struct CompactMapExampleView: View {
// State to hold the user's text input
@State private var userInput: String = ""
// State to display the last valid integer
@State private var validNumberText: String = "Enter a number"
// PassthroughSubject to publish user inputs
private let inputPublisher = PassthroughSubject<String, Never>()
// AnyCancellable to store the subscription
@State private var cancellable: AnyCancellable?
var body: some View {
VStack(spacing: 20) {
Text("CompactMap Operator Example")
.font(.headline)
.padding()
TextField("Type a number", text: $userInput)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.onChange(of: userInput) { oldState, newState in
inputPublisher.send(newState)
}
Text(validNumberText) // Display the last valid integer
.font(.title2)
.padding()
.multilineTextAlignment(.center)
}
.onAppear {
// Use compactMap to attempt to convert each input to an Int and discard non-numeric inputs
cancellable = inputPublisher
.compactMap { Int($0) } // Attempts to convert the input to an integer, ignoring nil results
.sink { validNumber in
validNumberText = "Valid Number: \(validNumber)"
}
}
.onDisappear {
// Cancel the subscription when the view disappears
cancellable?.cancel()
}
}
}
struct CompactMapExampleView_Previews: PreviewProvider {
static var previews: some View {
CompactMapExampleView()
}
}
Explanation of the Code
- State Variables:
@State private var userInput
: Holds the text input from the user, which is updated each time the text field changes.@State private var validNumberText
: Displays the last valid integer entered by the user. This variable updates whenevercompactMap
emits a valid integer.
- TextField for User Input:
- The
TextField
lets the user type any text. Each time the text changes, the.onChange(of:)
modifier triggers, sending the current text toinputPublisher
.
- The
- The
compactMap
Operator:- Inside
.onAppear
, we usecompactMap
oninputPublisher
. Each time a new input is received,compactMap
attempts to convert it to an integer usingInt($0)
. If the conversion fails (e.g., if the input is not a valid number),compactMap
returnsnil
, which is filtered out of the stream. - Only non-
nil
(valid integer) values are allowed through, and.sink
then updatesvalidNumberText
with the last valid integer, refreshing the view.
- Inside
- Displaying the Valid Integer:
- Each time
compactMap
emits a valid integer,.sink
updatesvalidNumberText
, which SwiftUI observes and displays in the view.
- Each time
- Cleaning Up the Subscription:
- The
.onDisappear
modifier cancels the subscription when the view disappears, preventing any potential memory leaks.
- The
How compactMap
Helps in SwiftUI
In this example, compactMap
lets us filter and transform the user’s text input, ensuring that only valid integers are displayed. Any non-numeric input is ignored, making it easy to handle optional values and prevent invalid data from reaching the view. This approach is especially useful for real-time input validation or filtering optional data.
Summary
The compactMap
operator is a powerful tool in Combine for working with optional data. By filtering out nil
values and transforming data in a single step, compactMap
makes it easy to work with optional inputs, such as user text fields or network responses, and ensures only valid, non-nil
values reach your SwiftUI views. This helps create more responsive, user-friendly apps by focusing on meaningful data.
Leave a Reply