Andrey Solera
Andrey Solera

Reputation: 2402

Multithreading iOS with segmented control

I have the following task to do: I have a segmented control with two main tabs, the first tab has to load ads and data from the network, each of them is via different urls. What I want to do is query the ads and the data in parallel, after both of them have finished, I want to merge them into a single list and display it to the user. This can be done with a DispatchGroup easily using .enter() and .leave(). However since this is in a segmented control with a search bar. The DispatchGroup needs to be cancellable in order for a new set of ads and data to be loaded. Is there a way to cancel the DispatchGroup, or is there a way to achieve this logic via other multithreading functionality?

Upvotes: 2

Views: 164

Answers (1)

Josh Homann
Josh Homann

Reputation: 16327

This is exactly the kind of thing that Combine is good for (if you are using iOS 13). ie you can make your network requests a function of the currently selected segment and what is typed in search bar and when you call switchToLatest it will cancel any uncompleted requests and only leave the most request that reflects the most recent state alive. I just wrote and example that does exactly that here: https://github.com/joshuajhomann/Combine-NasaMediaSearch

Or more succinctly, setup two passthrough subjects, one for your search bar and one for your segmented control, combine them into a network request, flatten it with `switchToLatest, then assign the result wherever you need it:

  private var data: [MyData] = []
  private var searchTermSubject = PassthroughSubject<String, Never>()
  private var segmentSubject = PassthroughSubject<SegmentType, Never>()

...

    let searchResults = Publishers
      .CombineLatest(
        searchTermSubject,
        segmentSubject
      )
      .debounce(for: .milliseconds(250), scheduler: RunLoop.main)
      .map { combined -> AnyPublisher<[MyData],Never> in
        let (term, segmentType) = combined
        guard !(term.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) else {
          return Just(.empty).eraseToAnyPublisher()
        }
        return NetworkRequestPublisher
          .search(query: term, mediaType: segmentType)
          .replaceErrorWith([])
          .eraseToAnyPublisher()
    }
    .switchToLatest()
    .receive(on: RunLoop.main)
    // assign or sink here depending on what you want to do

If you are not using iOS 13 then you can use OperationQueue or simply call cancel on the data task yourself.

Upvotes: 1

Related Questions