The AnyScheduler provides a type-erasing wrapper for the Scheduler protocol. This allows you to inject different types of schedulers (like DispatchQueue.main for production and ImmediateScheduler for testing) into your classes without making those classes generic.
Instead of using a generic type like class MyViewModel<S: Scheduler>, use AnySchedulerOf<T> where T is the underlying scheduler type (e.g., DispatchQueue).
To create an AnyScheduler from a live scheduler, use .eraseToAnyScheduler(). For common schedulers like DispatchQueue, OperationQueue, and RunLoop, you can use convenient static helpers like .main or .immediate.
class EpisodeViewModel: ObservableObject {
@Published var episode: Episode?
let apiClient: ApiClient
let scheduler: AnySchedulerOf<DispatchQueue>
init(apiClient: ApiClient, scheduler: AnySchedulerOf<DispatchQueue>) {
self.apiClient = apiClient
self.scheduler = scheduler
}
func reloadButtonTapped() {
self.apiClient.fetchEpisode()
.receive(on: self.scheduler)
.assign(to: &self.$episode)
}
}
// Production usage
let viewModel = EpisodeViewModel(
apiClient: ...,
scheduler: .main
)
// Test usage
let viewModel = EpisodeViewModel(
apiClient: ...,
scheduler: .immediate
)