Fix crash when fetching attachment data fails

This commit is contained in:
Shadowfacts 2022-01-21 11:10:03 -05:00
parent e65ed3e773
commit fa1482a152
3 changed files with 45 additions and 15 deletions

View File

@ -48,13 +48,13 @@ enum CompositionAttachmentData {
} }
} }
func getData(completion: @escaping (_ data: Data, _ mimeType: String) -> Void) { func getData(completion: @escaping (Result<(Data, String), Error>) -> Void) {
switch self { switch self {
case let .image(image): case let .image(image):
// Export as JPEG instead of PNG, otherweise photos straight from the camera are too large // Export as JPEG instead of PNG, otherweise photos straight from the camera are too large
// for Mastodon in its default configuration (max of 10MB). // for Mastodon in its default configuration (max of 10MB).
// The quality of 0.8 was chosen completely arbitrarily, it may need to be tuned in the future. // The quality of 0.8 was chosen completely arbitrarily, it may need to be tuned in the future.
completion(image.jpegData(compressionQuality: 0.8)!, "image/jpeg") completion(.success((image.jpegData(compressionQuality: 0.8)!, "image/jpeg")))
case let .asset(asset): case let .asset(asset):
if asset.mediaType == .image { if asset.mediaType == .image {
let options = PHImageRequestOptions() let options = PHImageRequestOptions()
@ -63,7 +63,10 @@ enum CompositionAttachmentData {
options.resizeMode = .none options.resizeMode = .none
options.isNetworkAccessAllowed = true options.isNetworkAccessAllowed = true
PHImageManager.default().requestImageDataAndOrientation(for: asset, options: options) { (data, dataUTI, orientation, info) in PHImageManager.default().requestImageDataAndOrientation(for: asset, options: options) { (data, dataUTI, orientation, info) in
guard var data = data, let dataUTI = dataUTI else { fatalError() } guard var data = data, let dataUTI = dataUTI else {
completion(.failure(.missingData))
return
}
let mimeType: String let mimeType: String
if dataUTI == "public.heic" { if dataUTI == "public.heic" {
@ -77,7 +80,7 @@ enum CompositionAttachmentData {
mimeType = UTType(dataUTI)!.preferredMIMEType! mimeType = UTType(dataUTI)!.preferredMIMEType!
} }
completion(data, mimeType) completion(.success((data, mimeType)))
} }
} else if asset.mediaType == .video { } else if asset.mediaType == .video {
let options = PHVideoRequestOptions() let options = PHVideoRequestOptions()
@ -100,20 +103,23 @@ enum CompositionAttachmentData {
case let .drawing(drawing): case let .drawing(drawing):
let image = drawing.imageInLightMode(from: drawing.bounds, scale: 1) let image = drawing.imageInLightMode(from: drawing.bounds, scale: 1)
completion(image.pngData()!, "image/png") completion(.success((image.pngData()!, "image/png")))
} }
} }
private static func exportVideoData(session: AVAssetExportSession, completion: @escaping (Data, String) -> Void) { private static func exportVideoData(session: AVAssetExportSession, completion: @escaping (Result<(Data, String), Error>) -> Void) {
session.outputFileType = .mp4 session.outputFileType = .mp4
session.outputURL = FileManager.default.temporaryDirectory.appendingPathComponent("exported_video_\(UUID())").appendingPathExtension("mp4") session.outputURL = FileManager.default.temporaryDirectory.appendingPathComponent("exported_video_\(UUID())").appendingPathExtension("mp4")
session.exportAsynchronously { session.exportAsynchronously {
guard session.status == .completed else { fatalError("video export failed: \(String(describing: session.error))") } guard session.status == .completed else {
completion(.failure(.export(session.error!)))
return
}
do { do {
let data = try Data(contentsOf: session.outputURL!) let data = try Data(contentsOf: session.outputURL!)
completion(data, "video/mp4") completion(.success((data, "video/mp4")))
} catch { } catch {
fatalError("Unable to load video: \(error)") completion(.failure(.export(error)))
} }
} }
} }
@ -121,6 +127,11 @@ enum CompositionAttachmentData {
enum AttachmentType { enum AttachmentType {
case image, video case image, video
} }
enum Error: Swift.Error {
case missingData
case export(Swift.Error)
}
} }
extension PHAsset { extension PHAsset {

View File

@ -97,7 +97,18 @@ struct ComposeAttachmentRow: View {
mode = .recognizingText mode = .recognizingText
DispatchQueue.global(qos: .userInitiated).async { DispatchQueue.global(qos: .userInitiated).async {
self.attachment.data.getData { (data, mimeType) in self.attachment.data.getData { (result) in
let data: Data
do {
try data = result.get().0
} catch {
DispatchQueue.main.async {
self.mode = .allowEntry
self.isShowingTextRecognitionFailedAlert = true
self.textRecognitionErrorMessage = error.localizedDescription
}
return
}
let handler = VNImageRequestHandler(data: data, options: [:]) let handler = VNImageRequestHandler(data: data, options: [:])
let request = VNRecognizeTextRequest { (request, error) in let request = VNRecognizeTextRequest { (request, error) in
DispatchQueue.main.async { DispatchQueue.main.async {

View File

@ -244,17 +244,17 @@ struct ComposeView: View {
private func uploadAttachments(_ completion: @escaping (Result<[Attachment], AttachmentUploadError>) -> Void) { private func uploadAttachments(_ completion: @escaping (Result<[Attachment], AttachmentUploadError>) -> Void) {
let group = DispatchGroup() let group = DispatchGroup()
var attachmentDatas = [(Data, String)?]() var attachmentDataResults = [Result<(Data, String), CompositionAttachmentData.Error>?]()
for (index, compAttachment) in draft.attachments.enumerated() { for (index, compAttachment) in draft.attachments.enumerated() {
group.enter() group.enter()
attachmentDatas.append(nil) attachmentDataResults.append(nil)
compAttachment.data.getData { (data, mimeType) in compAttachment.data.getData { (result) in
postProgress += 1 postProgress += 1
attachmentDatas[index] = (data, mimeType) attachmentDataResults[index] = result
group.leave() group.leave()
} }
} }
@ -271,7 +271,15 @@ struct ComposeView: View {
// posted status reflects order the user set. // posted status reflects order the user set.
// Pleroma does respect the order of the `media_ids` parameter. // Pleroma does respect the order of the `media_ids` parameter.
for (index, (data, mimeType)) in attachmentDatas.map(\.unsafelyUnwrapped).enumerated() { let datas: [(Data, String)]
do {
datas = try attachmentDataResults.map { try $0!.get() }
} catch {
completion(.failure(AttachmentUploadError(errors: [error])))
return
}
for (index, (data, mimeType)) in datas.enumerated() {
group.enter() group.enter()
let compAttachment = draft.attachments[index] let compAttachment = draft.attachments[index]