Как создать последовательную очередь для нескольких сеансов URL.DataTaskPublisher?

#multithreading #nsurlsession #combine #nsurlsessiondatatask

Вопрос:

У меня есть несколько datataskpublisher, который выполняет запросы к серверу с разных экранов приложений. Как заставить их работать серийно?

Ниже будет приведен примерный пример

Это служба, которая выполняет запросы и дополнительную логику.

 class Service {

   var cacheResult: Data?
   var cacnellable: AnyCancellable?
   static let shared = Service()


   func performPost() -> AnyPublisher<Data, URLError> {
      let task = URLSession.shared.dataTaskPublisher(for: URL(string: "postURL")!)
         .map { (data, _) in data}
         .multicast(subject: PassthroughSubject())

      cacnellable = task.eraseToAnyPublisher().sink(
         receiveCompletion: {_ in },
         receiveValue: { data in
            self.cacheResult = data
         })

      return task.autoconnect().eraseToAnyPublisher()
   }

   func performGet() -> AnyPublisher<Data, URLError> {
      let task = URLSession.shared.dataTaskPublisher(for: URL(string: "getURL")!)
         .map { (data, _) in data}
         .multicast(subject: PassthroughSubject())

      cacnellable = task.eraseToAnyPublisher().sink(
         receiveCompletion: {_ in },
         receiveValue: { data in
            self.cacheResult = data
         })

      return task.autoconnect().eraseToAnyPublisher()
   }

}
 

Это взгляды

 final class GetVC: UIViewController {

   let service = Service.shared
   var cancellable: AnyCancellable?

   override func viewWillAppear(_ animated: Bool) {
      super.viewWillAppear(true)
      cancellable = service.performGet()
         .sink(receiveCompletion: {_ in},
               receiveValue: { data in
                  print("Present data")
               })
   }

}
 
 final class PostVC: UIViewController {

   let service = Service.shared
   var cancellable: AnyCancellable?

   lazy var button: UIButton = {
      let btn = UIButton(primaryAction: UIAction(handler: { _ in
         self.cancellable = self.service.performPost()
            .sink(receiveCompletion: {_ in },
                  receiveValue: { data in
                     print("Some logic")
                  })
      }))
      return btn
   }()
}
 

Мне нужно, чтобы запросы никогда не отправлялись на сервер параллельно

Если «отправить задание» завершено, начнется «получить задание» .

Спасибо!

Ответ №1:

Я пришел с этим решением

 class Service {

   var cacheResult: Data?

   var cancellables = Set<AnyCancellable>()

   var taskSubject = PassthroughSubject<AnyPublisher<Data, URLError>, Never>()
   var taskInProcess: Bool = false
   var tasks: [AnyPublisher<Data, URLError>] = [] {
      didSet {
         if !taskInProcess, let firstTask = tasks.first {
            taskInProcess = true
            taskSubject.send(firstTask)
         }
      }
   }
   static let shared = Service()


   init() {

      taskSubject
         .flatMap { $0 }
         .sink(receiveCompletion: { _ in },
               receiveValue: { _ in
                  self.taskInProcess = false
                  self.tasks.removeFirst()
               })
         .store(in: amp;cancellables)

   }

   func performPost() -> AnyPublisher<Data, URLError> {
      let task = URLSession.shared.dataTaskPublisher(for: URL(string: url)!)
         .map { (data, _) in data}
         .multicast(subject: PassthroughSubject())

      tasks.append(task.autoconnect().eraseToAnyPublisher())

      return task.eraseToAnyPublisher()
   }

   func performGet() -> AnyPublisher<Data, URLError> {
      let task = URLSession.shared.dataTaskPublisher(for: URL(string: url)!)
         .map { (data, _) in data}
         .multicast(subject: PassthroughSubject())

      tasks.append(task.autoconnect().eraseToAnyPublisher())

      return task.eraseToAnyPublisher()
   }

}