mirror of
https://github.com/yattee/yattee.git
synced 2026-02-21 10:19:46 +00:00
Fix CFNetwork SIGABRT crash when creating download tasks on invalidated session
The background URLSession could be in an invalid state when downloadTask(with:) is called, because invalidateAndCancel() is asynchronous internally. This adds an ObjC exception handler to catch NSExceptions from CFNetwork, nil guards on the session, and safer session lifecycle management (nil after invalidation, finishTasksAndInvalidate for cellular toggle).
This commit is contained in:
@@ -99,10 +99,41 @@ extension DownloadManager {
|
||||
resumeData: Data?,
|
||||
httpHeaders: [String: String]? = nil
|
||||
) {
|
||||
guard urlSession != nil else {
|
||||
LoggingService.shared.logDownloadError(
|
||||
"[Downloads] URLSession is nil in startStreamDownload (\(phase))",
|
||||
error: DownloadError.downloadFailed("URLSession not available - session may be invalidated")
|
||||
)
|
||||
handleDownloadError(
|
||||
downloadID: downloadID,
|
||||
phase: phase,
|
||||
error: DownloadError.downloadFailed("URLSession not available")
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
let task: URLSessionDownloadTask
|
||||
|
||||
if let resumeData {
|
||||
task = urlSession.downloadTask(withResumeData: resumeData)
|
||||
var caughtException: NSException?
|
||||
var resumeTask: URLSessionDownloadTask?
|
||||
let success = tryCatchObjCException({
|
||||
resumeTask = self.urlSession.downloadTask(withResumeData: resumeData)
|
||||
}, &caughtException)
|
||||
|
||||
guard success, let resumeTask else {
|
||||
LoggingService.shared.logDownloadError(
|
||||
"[Downloads] NSException creating resume task (\(phase))",
|
||||
error: DownloadError.downloadFailed("CFNetwork exception: \(caughtException?.reason ?? "unknown")")
|
||||
)
|
||||
handleDownloadError(
|
||||
downloadID: downloadID,
|
||||
phase: phase,
|
||||
error: DownloadError.downloadFailed("Failed to create download task: \(caughtException?.reason ?? "unknown")")
|
||||
)
|
||||
return
|
||||
}
|
||||
task = resumeTask
|
||||
} else {
|
||||
// Starting fresh without resumeData - reset progress for this phase
|
||||
// to avoid jumping when saved progress conflicts with new URLSession progress
|
||||
@@ -128,7 +159,26 @@ extension DownloadManager {
|
||||
request.setValue(value, forHTTPHeaderField: key)
|
||||
}
|
||||
}
|
||||
task = urlSession.downloadTask(with: request)
|
||||
|
||||
var caughtException: NSException?
|
||||
var newTask: URLSessionDownloadTask?
|
||||
let success = tryCatchObjCException({
|
||||
newTask = self.urlSession.downloadTask(with: request)
|
||||
}, &caughtException)
|
||||
|
||||
guard success, let newTask else {
|
||||
LoggingService.shared.logDownloadError(
|
||||
"[Downloads] NSException creating download task (\(phase))",
|
||||
error: DownloadError.downloadFailed("CFNetwork exception: \(caughtException?.reason ?? "unknown")")
|
||||
)
|
||||
handleDownloadError(
|
||||
downloadID: downloadID,
|
||||
phase: phase,
|
||||
error: DownloadError.downloadFailed("Failed to create download task: \(caughtException?.reason ?? "unknown")")
|
||||
)
|
||||
return
|
||||
}
|
||||
task = newTask
|
||||
}
|
||||
|
||||
task.taskDescription = "\(downloadID.uuidString):\(phase.rawValue)"
|
||||
|
||||
@@ -148,8 +148,9 @@ final class DownloadManager: NSObject {
|
||||
self.downloadSettings = settings
|
||||
// Invalidate old session before creating new one with correct settings
|
||||
urlSession?.invalidateAndCancel()
|
||||
urlSession = nil
|
||||
setupSession()
|
||||
|
||||
|
||||
// Resume interrupted downloads only on initial setup
|
||||
if isInitialSetup {
|
||||
resumeInterruptedDownloads()
|
||||
@@ -186,8 +187,9 @@ final class DownloadManager: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Invalidate the old session
|
||||
urlSession.invalidateAndCancel()
|
||||
// 3. Invalidate the old session (use finishTasksAndInvalidate since downloads are already paused)
|
||||
urlSession?.finishTasksAndInvalidate()
|
||||
urlSession = nil
|
||||
|
||||
// 4. Create new session with updated cellular config
|
||||
setupSession()
|
||||
|
||||
Reference in New Issue
Block a user