Ich habe eine App, in der ich eine API-Anfrage aufrufe. Bei einigen Benutzern tritt ein Fehler auf, bei dem die Anwendung einen Zeitüberschreitungsfehler auslöst, wenn sie die App schließen, während Daten abgerufen werden, und sie später öffnen.
Ich verwende die Standard-URLSession-Datenaufgabe wie im folgenden Beispiel:
var session = URLSession(configuration:.ephemeral, delegate: self, delegateQueue: queue)
private func loadModels -> AnyPublisher<[Model], LoadModelsUseCaseError> {
guard let keyID = keyAdapter.getKeyID() else {
return Fail<[Model], LoadModelsUseCaseError>(error:.keyIDNotFound).eraseToAnyPublisher()
}
let url = Environment.loadModelsURL(for: keyID)
return apiAdapter.session
.dataTaskPublisher(for: url)
.decode(type: [Model].self, decoder: decoder)
.mapError(LoadModelsUseCaseError.init)
.eraseToAnyPublisher()
}
Eine Problemumgehung besteht darin, das .retry(1)
Ansichtsmodell aufzurufen, von dem aus ich die Methode aufrufe, aber diese Lösung hat offensichtliche Mängel.
Eine andere Problemumgehung besteht darin, den Zeitüberschreitungsfehler abzufangen und die Lademethode erneut aufzurufen. Das ist auch nicht perfekt, da die Anfrage niemals abläuft (selbst wenn es sich um einen relevanten Fall handelt).
Irgendwelche Vorschläge, wie man mit dieser Situation umgeht? Vielen Dank
Lösung des Problems
In Ordnung, also habe ich das Problem gelöst, indem ich den folgenden Code in meine APIAdapter / APIManager-Komponente eingefügt habe:
// MARK: - Configuration
private func configureNewSession() {
session?.invalidateAndCancel()
backgroundSession?.invalidateAndCancel()
let configuration = URLSessionConfiguration.default
configuration.isDiscretionary = true
configuration.sessionSendsLaunchEvents = true
session = URLSession(configuration: configuration, delegate: self, delegateQueue: queue)
let backgroundSessionConfiguration = URLSessionConfiguration.background(withIdentifier: "background")
backgroundSessionConfiguration.isDiscretionary = true
backgroundSessionConfiguration.sessionSendsLaunchEvents = true
backgroundSession = URLSession(configuration: backgroundSessionConfiguration, delegate: self, delegateQueue: queue)
}
private func subscribeToApplicationStateNotifications() {
NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)
.sink { _ in
self.moveTasksToForeground()
}
.store(in: &subscriptions)
NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)
.sink { _ in
self.moveTasksToBackground()
}
.store(in: &subscriptions)
}
// MARK: - App Lifecycle
/// The method currently doesn't move tasks in the background (as only download / upload tasks can be resumed),
/// but cancels them. Cancelled tasks doesn't produce errors, so they doesn't need to be catched in the View Models.
public func moveTasksToBackground() {
guard case.foreground = state else {
return
}
// Arguments in completion handlers are: data tasks, download tasks and upload tasks respectively.
session.getTasksWithCompletionHandler { dataTasks, _, _ in
for dataTask in dataTasks {
dataTask.suspend()
// NOTE: - Download tasks can produce resume data that can be resumed by standard url session in rhe
// foreground.
//
// Example:
//
// guard let downloadTask = downloadTask as? URLSessionDownloadTask else {
// continue
// }
// downloadTask.cancel(byProducingResumeData: { [self] resumeData in
// var downloadTask: URLSessionDownloadTask? = nil
// if let resumeData = resumeData {
// downloadTask = backgroundSession.downloadTask(withResumeData: resumeData)
// }
// downloadTask?.resume()
// })
}
}
state =.background
}
/// The method currently doesn't move tasks in the background (as only download / upload tasks can be resumed),
/// but cancels them. Cancelled tasks doesn't produce errors, so they doesn't need to be catched in the View Models.
public func moveTasksToForeground() {
guard case.background = state else {
return
}
// Arguments in completion handlers are: data tasks, download tasks and upload tasks respectively.
backgroundSession.getTasksWithCompletionHandler { dataTasks, _, _ in
for dataTask in dataTasks {
dataTask.suspend()
// NOTE: - Download tasks can produce resume data that can be resumed by standard url session in rhe
// foreground.
//
// Example:
//
// guard let downloadTask = downloadTask as? URLSessionDownloadTask else {
// continue
// }
// downloadTask.cancel(byProducingResumeData: { [self] resumeData in
// var downloadTask: URLSessionDownloadTask? = nil
// if let resumeData = resumeData {
// downloadTask = urlSession.downloadTask(withResumeData: resumeData)
// }
// downloadTask?.resume()
// })
}
}
state =.foreground
}
Wenn Sie Datenaufgaben aussetzen, erzeugt die Sitzung keinen Fehler, sodass es nicht erforderlich ist, Abbrüche in Ansichtsmodell/Ansicht/Anwendungsfall/Dienst/wo auch immer Sie die API-Aufrufe aufrufen, zu filtern. Alles, was Sie tun müssen, ist, die Remote-Daten zu aktualisieren, wenn der Benutzer die App öffnet / den Bildschirm betritt.
Keine Kommentare:
Kommentar veröffentlichen