Swift’s filter is a method that lets you sift through an array and pull out only the elements that meet a condition you define. Instead of writing a loop, checking each item manually, and appending it to a new array, you write one clean line of code and let Swift do the work.
Think about a recipe app that has a big list of recipes. You only want to show the vegetarian ones. Or a contacts list where you want to find everyone whose name starts with the letter “A.” Or a list of scores where you need only the high scorers. Those are all filter problems, and Swift’s filter(_:) method is made exactly for them.
In this article you’ll learn what filter does, how to read and write the syntax (including the shorthand that confuses most beginners), the most important variations, how to use filter on your own custom types in real iOS code, and the most common mistakes to watch out for. By the end you’ll have a solid grasp of one of Swift’s most useful higher-order functions.
The Basic Syntax
Here is the simplest possible example of Swift filter. It takes an array of numbers and returns only the ones greater than 5:
let numbers = [2, 8, 3, 11, 4, 9]
let bigNumbers = numbers.filter { number in
return number > 5
}
print(bigNumbers) // [8, 11, 9]Let’s walk through each part of that:
| Line | What it does |
|---|---|
let numbers = [...] | A plain array of integers. This is the data we want to filter. |
numbers.filter { ... } | Calls the filter method on the array. Everything inside the curly braces is called a closure — it’s a small block of code that gets run once for each element in the array. |
number in | This names the current element. As filter loops through the array, each element takes the name number one at a time. You choose this name — it could be item, n, anything that makes sense. |
return number > 5 | This is the condition. For each element, filter asks: does this pass? If you return true, the element gets included in the result. If you return false, it’s left out. |
let bigNumbers = ... | The result of filter is stored here. It’s a brand new array — the original numbers array is not changed at all. |
Breaking It Down
Now let’s look more carefully at the parts that trip beginners up.
What is a closure?
The code inside the curly braces — { number in return number > 5 } — is a closure. Think of it as a mini-function that you define right on the spot. You don’t give it a name or put it somewhere else in your file. You just write the logic inline.
Filter calls your closure once for every element in the array. Each time, it passes in one element, your closure checks the condition, and returns either true or false. Filter collects all the elements where you returned true and gives them back to you as a new array.
What does $0 mean?
You’ll see a lot of Swift filter examples written like this:
let bigNumbers = numbers.filter { $0 > 5 }This is called shorthand argument syntax. Instead of writing number in return number > 5, Swift lets you use $0 as a shortcut for the first argument. The $ is just Swift’s way of saying “argument” and the 0 means the first one. You also don’t need the word return when the closure is a single expression.
Both versions do exactly the same thing. The long form is easier to read when you’re learning. Once you’re comfortable, the shorthand is faster to write. Use whichever makes more sense to you right now.
The original array is never changed
This is important. When you call filter, it creates a brand new array and returns that. Your original array is untouched. So if you need to keep both the full list and the filtered version, you can — no problem.
Filter always returns an array of the same type
If you filter an array of Int, you get back an array of Int. If you filter an array of String, you get back an array of String. Filter never changes the type of the elements — it only decides which ones to keep.
var). If you write numbers.filter { $0 > 5 } without capturing the result, nothing useful happens — the filtered array just gets thrown away.Variations and Syntax Patterns
There are several ways to write and use filter in Swift depending on the situation. Here are the most important ones you’ll encounter as a beginner.
let names = ["Alice", "Bob", "Anna", "Charlie"]
let aNames = names.filter { name in
return name.hasPrefix("A")
}
// ["Alice", "Anna"]name) using the in keyword, then use that name in your condition. This is the clearest form to read and write while you’re still learning. Use it whenever the logic is more than a single comparison.let names = ["Alice", "Bob", "Anna", "Charlie"]
let aNames = names.filter { $0.hasPrefix("A") }
// ["Alice", "Anna"]$0 is shorthand for the first argument — in this case, each element of the array. You’ll see this everywhere in Swift code once you’re past the basics. It’s the same logic as the long form, just compressed. You can drop return too when the closure is a single expression.let words = ["apple", "banana", "avocado", "cherry", "apricot"]
// Filter words that contain "an"
let withAn = words.filter { $0.contains("an") }
// ["banana"]
// Filter words longer than 5 characters
let longWords = words.filter { $0.count > 5 }
// ["banana", "avocado", "cherry", "apricot"].contains(), .hasPrefix(), .hasSuffix(), .count, and .isEmpty inside your closure.let scores = [45, 88, 72, 91, 33, 95, 60]
// Get passing scores (70+), sorted highest to lowest
let topScores = scores
.filter { $0 >= 70 }
.sorted(by: >)
print(topScores) // [95, 91, 88, 72].sorted(), .map(), or .count. Each method returns a new array (or value) so you can keep chaining. This is one of the cleanest ways to express multi-step data processing in Swift.let rawInput = ["42", "hello", "7", "world", "99"]
// Convert strings to Int, dropping any that can't convert
let validNumbers = rawInput.compactMap { Int($0) }
print(validNumbers) // [42, 7, 99]compactMap is not the same as filter, but it solves a related problem. It transforms each element (like map does) and automatically removes any nil results. If your transformation can fail, use compactMap instead of a filter-then-map chain. You’ll see this a lot when parsing data from the outside world.let ages = [14, 22, 17, 31, 19, 28]
// Adults only (18 or older), but not seniors (under 65)
let workingAge = ages.filter { age in
age >= 18 && age < 65
}
print(workingAge) // [22, 31, 19, 28]&& (and) or || (or). When your logic gets complex, switch to the long form with a named parameter so the code stays readable. Trying to cram multiple conditions into a $0 shorthand can quickly become hard to parse.How Filter Appears in Real iOS Code
Simple number and string examples help you learn the syntax, but in a real iOS app you’re usually working with your own custom types. Here’s how filter looks in a more realistic context.
Imagine you’re building a task manager app. You have a Task struct and an array of tasks, and you need to show only the incomplete ones:
import SwiftUI
// Your data model
struct Task {
let title: String
let isCompleted: Bool
let priority: Int // 1 = low, 2 = medium, 3 = high
}
// Some sample data
let allTasks = [
Task(title: "Buy groceries", isCompleted: false, priority: 2),
Task(title: "Finish report", isCompleted: false, priority: 3),
Task(title: "Call dentist", isCompleted: true, priority: 1),
Task(title: "Walk the dog", isCompleted: false, priority: 2),
Task(title: "Fix login bug", isCompleted: true, priority: 3)
]
// Show only tasks that aren't done yet
let pendingTasks = allTasks.filter { !$0.isCompleted }
// Show only high-priority tasks that are still pending
let urgentTasks = allTasks.filter { task in
!task.isCompleted && task.priority == 3
}
// In a SwiftUI view, you'd use filtered data directly
struct TaskListView: View {
let tasks: [Task]
var pendingTasks: [Task] {
tasks.filter { !$0.isCompleted }
}
var body: some View {
List(pendingTasks, id: \.title) { task in
Text(task.title)
}
}
}A few things worth noticing here:
Filtering on a Boolean property is very common. filter { !$0.isCompleted } reads almost like plain English once you get used to it: “give me everything where isCompleted is false.”
The computed property pattern in TaskListView is the standard way to use filter in SwiftUI. Instead of storing the filtered result separately, you compute it on the fly using a var with a getter. Every time the view redraws, pendingTasks recalculates automatically from the source data.
Accessing properties with dot notation inside filter closures is how you work with structs and classes. Once you have a named parameter like task in, you just use task.propertyName exactly the way you would anywhere else in Swift.
Common Mistakes to Avoid
Here are the four mistakes I see most often when beginners first use filter in Swift.
1. Forgetting to capture the result
The most common mistake. You write the filter call, run the app, and nothing changes:
// This does nothing useful — the result is thrown away
numbers.filter { $0 > 5 }
// This is correct — capture the result
let bigNumbers = numbers.filter { $0 > 5 }Filter doesn’t modify anything. It returns a new array. You have to put that return value somewhere.
2. Expecting to modify the original array in-place
Filter is not removeAll(where:). If you want to actually remove items from an existing array, use removeAll(where:) on a var array instead. Filter always creates a new array and leaves the original alone.
var items = [1, 2, 3, 4, 5]
// Remove items in place — items array IS modified
items.removeAll { $0 % 2 == 0 }
print(items) // [1, 3, 5]
// Filter creates a new array — items is NOT modified
let oddItems = items.filter { $0 % 2 != 0 }3. Returning the wrong Boolean
This catches people when they’re thinking about what to remove rather than what to keep. Filter keeps elements where you return true. If you want to remove all empty strings, you return true for strings that are NOT empty:
let userInputs = ["Alice", "", "Bob", "", "Carol"]
// Wrong — this returns only empty strings
let wrong = userInputs.filter { $0.isEmpty }
// Correct — return true for strings you WANT to keep
let nonEmpty = userInputs.filter { !$0.isEmpty }
// ["Alice", "Bob", "Carol"]true to keep it. Return false to drop it.4. Using filter when compactMap is the right tool
If you’re trying to remove nil values from an array of optionals, filter can work but it’s awkward. compactMap is the cleaner choice:
let maybeNumbers: [Int?] = [1, nil, 3, nil, 5]
// Works but awkward — you still have [Int?] type
let withFilter = maybeNumbers.filter { $0 != nil }
// Better — gives you [Int] directly, nils removed
let withCompactMap = maybeNumbers.compactMap { $0 }
// [1, 3, 5]Quick Reference
Here’s a summary of the most common syntax patterns you’ll use with filter in Swift:
| Syntax | What It Does |
|---|---|
| array.filter { $0 > value } | Keep elements greater than a value. Replace > with any comparison operator. |
| array.filter { $0.hasPrefix(“X”) } | Keep strings that start with a specific prefix. |
| array.filter { $0.contains(“X”) } | Keep strings that contain a specific substring. |
| array.filter { !$0.isEmpty } | Remove empty strings from a string array. |
| array.filter { $0.boolProperty } | Keep elements where a Boolean property is true. |
| array.filter { !$0.boolProperty } | Keep elements where a Boolean property is false. |
| array.filter { item in item.a && item.b } | Multiple conditions combined with && (and) or || (or). |
| array.filter { … }.sorted(by: >) | Filter then sort in a single chained expression. |
| array.compactMap { $0 } | Remove all nil values from an array of optionals. |
When To Use Swift Filter
Filter fits naturally into a lot of common beginner app ideas. Here are three scenarios where it’s clearly the right tool:
- ✅
A to-do or task manager app
Your app stores all tasks in one array. Depending on which tab or filter the user picks, you show all tasks, just the incomplete ones, or just the high-priority ones. Filter lets you compute these views cleanly from the same source data without maintaining multiple separate arrays.
- 🍽️
A recipe or product app with search
When the user types in a search box, you filter your array of recipes or products by name. As the user types each character, you recompute the filtered list. Filter is the perfect tool here because it’s concise, doesn’t mutate your source data, and works with any condition you can express as a closure.
- 🏃
A fitness or activity tracker
You have an array of workout sessions and you want to display only the runs from this week, or only the sessions that burned more than 300 calories, or only workouts of a specific type. Each of those is a filter call on the same source array with a different condition.
compactMap (filters nils from a transformation) or map followed by filter. And if you’re removing elements from an existing array directly, removeAll(where:) is more direct than filter.Using AI to Go Deeper with Swift Filter
AI coding tools like Claude, GitHub Copilot, and ChatGPT are part of how developers work today. Knowing the right prompts to ask is genuinely useful. But the goal is always to arrive at your own understanding — use AI to learn faster, not to skip the learning.
Summary
Here’s a quick recap of what you learned about Swift filter:
filter(_:)takes a closure that returnstrueorfalsefor each element, and returns a new array with only the elements that passed- The original array is never modified — filter always creates a new array
$0is shorthand for the current element inside the closure — same as naming it explicitly withname in- You can filter arrays of any type: numbers, strings, or your own custom structs
- Filter can be chained with other methods like
.sorted()and.map()for clean, readable data processing - Use
compactMapinstead of filter when you’re dealing with optionals or want to transform and filter in one step - In SwiftUI, the computed property pattern is the standard way to use filter to drive your UI
The best way to get comfortable with filter is to find a loop in code you’ve already written and see if you can replace it with a filter call. It takes a few tries before the syntax feels natural, but once it does you’ll find yourself reaching for it constantly.
A great next topic to explore after filter is Swift’s map method — it transforms each element in an array into something new. Together, filter and map cover a huge percentage of the array operations you’ll need in real iOS development.

