Skip to content

Instantly share code, notes, and snippets.

@pexavc
Created July 26, 2023 03:13
Show Gist options
  • Save pexavc/14c96f343699c787c658491a81021799 to your computer and use it in GitHub Desktop.
Save pexavc/14c96f343699c787c658491a81021799 to your computer and use it in GitHub Desktop.
Simple Search Query Debouncer
import Foundation
import SwiftUI
import Combine
class SearchConductor: ObservableObject {
var searchTimer: Timer? = nil
private var task: Task<Void, Error>? = nil
@Published var query: String
@Published var isEditing: Bool = false
@Published var isSearching: Bool = false
@Published var response: SearchResponse? = nil
var isEmpty: Bool {
response == nil
}
private var handler: ((String) async -> SearchResponse?)?
internal var cancellables: Set<AnyCancellable> = .init()
init() {
query = ""
self.handler = nil
$query
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.removeDuplicates()
.sink { [weak self] value in
// guard value.isNotEmpty else {
// self?.clean()
// return
// }
self?.startTimer()
}.store(in: &cancellables)
}
@discardableResult
func hook(_ commit: @escaping ((String) async -> SearchResponse?)) -> Self {
self.handler = commit
return self
}
//basic debouncing
func startTimer() {
searchTimer?.invalidate()
searchTimer = nil
isSearching = true
searchTimer = Timer.scheduledTimer(
withTimeInterval: 0.4.randomBetween(0.8),
repeats: false) { [weak self] timer in
timer.invalidate()
guard let q = self?.query else { return }
self?.task?.cancel()
self?.task = Task.detached { [weak self] in
let response = await self?.handler?(q)
DispatchQueue.main.async { [weak self] in
GraniteHaptic.light.invoke()
self?.response = response
self?.isSearching = false
}
}
}
}
func clean() {
isSearching = false
self.task?.cancel()
self.task = nil
searchTimer?.invalidate()
searchTimer = nil
query = ""
}
}
@pexavc
Copy link
Author

pexavc commented Jul 26, 2023

Example use:

@StateObject var conductor: SearchConductor = .init()

SomeSearchBar($conductor.query)

SomeView()
   .task {
        conductor.hook { query in
            //BE request with query
            await Lemmy.search(query,
                               type_: selectedSearch,
                               communityId: nil,
                               communityName: community?.name,
                               creatorId: nil,
                               sort: selectedSort,
                               listingType: selectedListing,
                               page: 1,
                               limit: 12)
        }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment