I recently started to study the Combine and ran into a certain problem. First, I will describe what I am doing. I trying to use Clean Architecture Here you can see my Repository
protocol Repository {
func test()
}
class MockRepository: Repository {
func test() {
sleep(3)
}
}
Then I created UseCase
class UseCaseBase<TInput, TOutput> {
var task: TOutput? { return nil }
var repository: Repository
init(_ repository: Repository) {
self.repository = repository
}
func execute(with payload: TInput) -> AnyPublisher<TOutput, Never> {
return AnyPublisher(Future<TOutput, Never> { promise in
promise(.success(self.task!))
})
.eraseToAnyPublisher()
}
}
class MockUseCase: UseCaseBase<String, Int> {
override var task: Int? {
repository.test()
return 1
}
}
And then in a init block ContentView I did something like that
init() {
let useCase = MockUseCase(MockRepository())
var cancellables = Set<AnyCancellable>()
useCase.execute(with: "String")
.sink(receiveValue: { value in
print(value)
})
.store(in: &cancellables)
print("Started")
}
At first, I want to get
"Started"
and then after sleep(3)
value "1"
Now I get "1" and then "Started"
Your sleep(3)
call runs on the main thread, which means that it blocks any other operations, including the code that prints the "Started" text.
I won't be rambling about how bad it is to block the main thread, this is well known information, but this is the reason you see the behaviour you asked about.
I don't see any thread switching code in your question, so if you wish to achieve some kind of asynchronicity, then you can either go with Rob's solution of using dispatch(after:)
, or do the locomotion (the sleep) on another thread:
func execute(with payload: TInput) -> AnyPublisher<TOutput, Never> {
return AnyPublisher(Future<TOutput, Never> { promise in
DispatchQueue.global().async {
promise(.success(self.task!))
}
})
.eraseToAnyPublisher()
}