Skip to content

Instantly share code, notes, and snippets.

@profh
Created October 30, 2025 02:16
Show Gist options
  • Select an option

  • Save profh/f3fdaaa9966c46d341c011b8ebdc7c27 to your computer and use it in GitHub Desktop.

Select an option

Save profh/f3fdaaa9966c46d341c011b8ebdc7c27 to your computer and use it in GitHub Desktop.
Combine_Movie_Search_Filter
import Combine
import Foundation
// MARK: - Movie Database
let movies = [
"The Matrix",
"The Godfather",
"The Dark Knight",
"Monty Python and the Holy Grail",
"Forrest Gump",
"Inception",
"The Shawshank Redemption",
"Goodfellas",
"The Empire Strikes Back",
"True Lies",
"Interstellar",
"The Princess Bride",
"Chariots of Fire",
"Gladiator",
"Captain America: Winter Soldier",
"Blade Runner",
"The Lion King",
"Back to the Future",
"Buckaroo Banzai",
"The Avengers"
]
// MARK: - Search Publisher
let searchPublisher = PassthroughSubject<String, Never>()
// Storage for our subscription
var cancellables = Set<AnyCancellable>()
// MARK: Complete Search Pipeline
searchPublisher
// 1. Debounce: Wait 0.3 seconds after user stops typing
.debounce(for: .milliseconds(300), scheduler: RunLoop.main)
// 2. Filter: Ignore empty search terms
// 3. Map: Filter movies based on search term (case-insensitive)
// 4. Subscribe: Print the results
// if noting, print "no movies found", otherwise print a
// count of the movies found followed by their titles (with a dash)
// 5. Store: Keep the subscription alive
.store(in: &cancellables)
// MARK: - Test Scenarios
// If you did this right, you should get the output given farther below...
print("Starting search filter test...")
print("\n--- First search: 'dark' ---")
searchPublisher.send("dark")
// Wait for debounce, then try another search
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
print("\n--- New search: 'go' (2 results) ---")
searchPublisher.send("go")
}
// Try an empty string (should be filtered out)
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
print("\n--- Testing empty string (should not print any results) ---")
searchPublisher.send("")
}
// Try a search with no matches
DispatchQueue.main.asyncAfter(deadline: .now() + 4.5) {
print("\n--- New search: 'xyz' (no matches) ---")
searchPublisher.send("xyz")
}
// Keep the playground running
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
// MARK: Expected Output
/*
Starting search filter test...
--- First search: 'dark' ---
Found 1 movie(s):
- The Dark Knight
--- New search: 'go' (2 results) ---
Found 2 movie(s):
- The Godfather
- Goodfellas
--- Testing empty string (should not print any results) ---
--- New search: 'xyz' (no matches) ---
No movies found
*/
/*
KEY LEARNING POINTS SUMMARIZED:
===============================
- debounce prevents excessive work (important for API calls!)
- filter removes invalid input before expensive operations
- map transforms the stream (here, from String to [String])
- Case-insensitive search requires lowercasing both sides
- Storing subscription in Set<AnyCancellable> keeps it alive
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment