Блокировка пользовательского интерфейса при использовании программы загрузки AlamoFire

#swift #swiftui #alamofire

#быстрый #свифтуи #аламофайр

Вопрос:

Я пытаюсь создать индикатор выполнения загрузки и показывать предупреждение одновременно с завершением загрузки.

Для этой задачи я использую AlamoFire с SwiftUI, так как это упрощает загрузку. Однако, когда я отслеживаю прогресс с помощью progressView с опубликованной переменной, весь пользовательский интерфейс блокируется, и я не могу понять, как это исправить.

Я попытался добавить программу загрузки в отдельную очередь отправки, но мне все равно нужно обновить пользовательский интерфейс из основного потока, иначе Xcode будет жаловаться.

Как протестировать прилагаемый пример кода:

  • Нажмите «Начать загрузку».
  • Подождите, пока представление прогресса немного сдвинется
  • Нажмите кнопку «Показать предупреждение» .
  • Попробуйте закрыть оповещение, оно не закроется.

Я был бы признателен за любую помощь.

импорт SwiftUI импорт Alamofire

 struct ContentView: View {  @StateObject var viewModel: ViewModel = ViewModel()  @State private var showAlert = false   var body: some View {  VStack {  Button("Show Alert") {  showAlert.toggle()  }    Button("Start download") {  viewModel.startDownload()  }    if viewModel.showProgressView {  ProgressView("Downloading…", value: viewModel.downloadProgress, total: 1.0)  .progressViewStyle(.linear)  }  }  .alert(isPresented: $showAlert) {  Alert(  title: Text("Text"),  dismissButton: .cancel()  )  }  } }  class ViewModel: ObservableObject {  @Published var currentDownload: DownloadRequest? = nil  @Published var downloadProgress: Double = 0.0  @Published var showProgressView: Bool = false    func startDownload() {  print("Function called!")    showProgressView.toggle()    let queue = DispatchQueue(label: "alamofire", qos: .utility)  let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)   AF.download("https://speed.hetzner.de/10GB.bin", to: destination)  .downloadProgress(queue: queue) { progress in  print(progress.fractionCompleted)    DispatchQueue.main.async {  self.downloadProgress = progress.fractionCompleted  }  }  .response { response in  print(response)  }  } }  struct ContentView_Previews: PreviewProvider {  static var previews: some View {  ContentView()  } }  

Ответ №1:

Проблема здесь в том, что вы эффективно отправляете спам в поток пользовательского интерфейса с обновлениями, так downloadProgress как alamofire очень часто вызывает закрытие, предоставляемое (посмотрите на отпечатки консоли). Вам нужно немного изменить ход обновления с AF, чтобы можно было зарегистрировать нажатие кнопки для отмены предупреждения (в Сочетании это будет называться отменой). То, что я сделал здесь, добавлено немного счетчика времени, чтобы он обновлял прогресс каждую 1 секунду. Время между этими обновлениями позволяет потоку пользовательского интерфейса свободно реагировать на нажатия и т. Д.

 import SwiftUI import Alamofire  struct ContentView: View {  @StateObject var viewModel: ViewModel = ViewModel()  @State private var showAlert = false   var body: some View {  VStack {  Button("Show Alert") {  showAlert.toggle()  }    Button("Start download") {  viewModel.startDownload()  }    if viewModel.showProgressView {  ProgressView("Downloading…", value: viewModel.downloadProgress, total: 1.0)  .progressViewStyle(.linear)  }  }  .alert(isPresented: $showAlert) {  Alert(  title: Text("Text"),  dismissButton: .cancel()  )  }  } }  class ViewModel: ObservableObject {  @Published var currentDownload: DownloadRequest? = nil  @Published var downloadProgress: Double = 0.0  @Published var showProgressView: Bool = false    func startDownload() {  print("Function called!")    showProgressView.toggle()    let queue = DispatchQueue(label: "net", qos: .userInitiated)  let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)  var last = Date()  AF.download("https://speed.hetzner.de/10GB.bin", to: destination)  .downloadProgress(queue:queue) { progress in  print(progress.fractionCompleted)  if Date().timeIntervalSince(last) gt; 1 {  last = Date()  DispatchQueue.main.async {  self.downloadProgress = progress.fractionCompleted  }  }  }  .response { response in // print(response)  }  } }  struct ContentView_Previews: PreviewProvider {  static var previews: some View {  ContentView()  } }