Fix crash when fetching attachment data fails
This commit is contained in:
parent
e65ed3e773
commit
fa1482a152
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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]
|
||||||
|
|
Loading…
Reference in New Issue