如何解决CoreData导致应用程序崩溃:崩溃:NSOperationQueue 0x10530af90QOS:未定
1:文件值来自CoreData
。
2:文件成功上传后,将从CoreData
中删除。
这是我的UploadManager类
struct APINotificationParameters: Codable {
let status: String
}
final class UploadManager: NSObject {
enum FileStatus {
case uploading,uploaded,error
}
private var BASE_API = UserDefaults.standard.value(forKey: "CustomWebUrl") as? String ?? ""
private var cancellableSet: Set<AnyCancellable>
private var storage: NSPersistentContainer
init(storage: NSPersistentContainer) {
self.storage = storage
self.cancellableSet = []
super.init()
}
private func notifyApi(recordingId: String,fileId: String,fileStatus: FileStatus) {
BASE_API = UserDefaults.standard.value(forKey: "CustomWebUrl") as? String ?? ""
let url = URL(string: "\(self.BASE_API)/guest/recordings/\(recordingId)/files/\(fileId)")
let status: String
switch fileStatus {
case .uploading:
status = "uploading"
case .uploaded:
status = "uploaded"
case .error:
status = "error"
}
let jsonEncoder = JSONEncoder()
var request = URLRequest(url: url!)
request.httpMethod = "PATCH"
request.addValue("application/json",forHTTPHeaderField: "Content-Type")
request.httpBody = try! jsonEncoder.encode(APINotificationParameters(status: status))
URLSession.shared.dataTaskPublisher(for: request)
.tryMap { data,response in
// Check for fundamental networking error.
guard let response = response as? HTTPURLResponse else {
// print("UnkNown error while fetching session information.")
throw APIError.unkNown
}
// Check for http errors.
guard (200 ... 299) ~= response.statusCode else {
// print("HTTP error with status code = \(response.statusCode).")
throw APIError.http(reason: "Failed with http status = \(response.statusCode)")
}
}
.mapError { error -> APIError in
if let error = error as? APIError {
return error
} else {
return APIError.network(reason: error.localizedDescription)
}
}
.erasetoAnyPublisher()
.receive(on: RunLoop.main)
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Sucessfully notified api")
break
case .failure(let error):
print(error.localizedDescription)
}
},receiveValue: {})
.store(in: &self.cancellableSet)
}
private func deleteFile(path: URL) {
let fileManager = FileManager()
do {
try fileManager.removeItem(at: path)
print("Deleted file: \(path.absoluteString)")
} catch {
print("Impossible to delete local file: \(error.localizedDescription)")
}
}
private func uploadFiletoS3(remoteUrl: URL,localPath: URL,fileId: String) {
var request = URLRequest(url: remoteUrl)
request.httpMethod = "PUT"
let config = URLSessionConfiguration.background(withIdentifier: fileId)
let session = URLSession(configuration: config,delegate: self,delegateQueue: nil)
let task = session.uploadTask(with: request,fromFile: localPath)
task.resume()
}
func uploadFile(remoteUrl: URL,localName: String,recordingId: String,recordingName: String,sessionId: String) {
let context = self.storage.viewContext
let uploadRecord = UploadRecord(context: context)
uploadRecord.fileId = fileId
uploadRecord.localFileName = localName
uploadRecord.recordingId = recordingId
uploadRecord.recordingName = recordingName
uploadRecord.sessionId = sessionId
// store file id here
UserDefaults.standard.setValue(fileId,forKey: "uploadingFileID")
//try! context.save()
do {
try context.save()
} catch {
print("An error occurred while saving: \(error)")
}
self.notifyApi(recordingId: recordingId,fileId: fileId,fileStatus: .uploading)
self.uploadFiletoS3(remoteUrl: remoteUrl,localPath: localPath,fileId: fileId)
}
}
extension UploadManager: URLSessionTaskDelegate {
private func findUploadRecord(fileId: String) -> UploadRecord? {
let context = self.storage.viewContext
let uploadFetch = NSFetchRequest<UploadRecord>(entityName: "Upload")
uploadFetch.predicate = nspredicate(format: "fileId == %@",fileId)
var uploadRecords = [UploadRecord]()
do {
uploadRecords = try context.fetch(uploadFetch)
return uploadRecords[0]
} catch let error {
print(error.localizedDescription)
return nil
}
}
func urlSession(_ session: URLSession,task: URLSessionTask,didCompleteWithError error: Error?) {
guard let fileId = session.configuration.identifier,error == nil else {
// Todo(jdaeli): Retry upload
print("Upload Failed: \(String(describing: error))")
return;
}
let context = self.storage.viewContext
let uploadRecord = self.findUploadRecord(fileId: fileId)
let localName = uploadRecord?.localFileName ?? ""
print("Uploaded: \(localName)")
let documentsDirectory = FileManager.default.urls(for: .documentDirectory,in: .userDomainMask).last
let localPath = documentsDirectory?.appendingPathComponent(localName)
self.deleteFile(path: localPath!)
// self.notifyApi(recordingId: recordingId,fileId:fileId,fileStatus: .uploaded)
dispatchQueue.main.async {
context.delete(uploadRecord ?? UploadRecord())
//try? context.save()
do {
try context.save()
} catch {
print("Failed saving")
}
}
}
func urlSession(_ session: URLSession,didSendBodyData bytesSent: Int64,totalBytesSent: Int64,totalBytesExpectedToSend: Int64) {
let fileId = session.configuration.identifier!
let context = self.storage.viewContext
let recordUpload = self.findUploadRecord(fileId: fileId)
dispatchQueue.main.async {
recordUpload?.totalBytesSent = totalBytesSent
recordUpload?.totalBytes = totalBytesExpectedToSend
let fileID = UserDefaults.standard.value(forKey: "uploadingFileID") as? String ?? ""
do {
try context.save()
} catch {
print("Failed saving")
}
//print("Sent \(totalBytesSent) of \(totalBytesExpectedToSend)")
// only show on going uplaod progress
if recordUpload?.fileId == fileID {
//print("same file,need to show")
let uploadProgress = (String(format: "%.2f",Double(recordUpload?.totalBytesSent ?? 0)/Double(recordUpload?.totalBytes ?? 0)*100))
// store value here,so we can use in uploading progress
UserDefaults.standard.setValue(uploadProgress,forKey: "uploadingProgress")
}
}
}
}
它工作正常,但是有时在视频上传过程中应用程序崩溃了,当我登录Firebase Crashlytics
时,发现此在图片下方检查
应用程序在这里崩溃
do {
uploadRecords = try context.fetch(uploadFetch)
return uploadRecords[0]
} catch let error {
print(error.localizedDescription)
return nil
}
此行 uploadRecords =试试context.fetch(uploadFetch)
即使我正在使用“执行捕获”功能,应用仍然崩溃。 问题未在我的手机中重现,但客户端面临此类崩溃。
修复此崩溃所需的帮助很大。
解决方法
崩溃可能是由于无条件访问空数组的索引所致,所以我建议改为类似
if let uploadRecords = try? context.fetch(uploadFetch),!uploadRecords.isEmpty {
return uploadRecords[0]
} else {
return nil
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。