AttachMediaUtility.swift (4095B)
1 // 2 // AttachMediaUtility.swift 3 // damus 4 // 5 // Created by Swift on 2/17/23. 6 // 7 8 import SwiftUI 9 import UIKit 10 import CoreGraphics 11 import UniformTypeIdentifiers 12 13 enum ImageUploadResult { 14 case success(String) 15 case failed(Error?) 16 } 17 18 enum ImageUploadMediaType { 19 case normal 20 case profile_picture 21 } 22 23 protocol AttachMediaUtilityProtocol { 24 static func create_upload_request(mediaToUpload: MediaUpload, mediaUploader: any MediaUploaderProtocol, mediaType: ImageUploadMediaType, progress: URLSessionTaskDelegate, keypair: Keypair?) async -> ImageUploadResult 25 } 26 27 class AttachMediaUtility { 28 fileprivate static func create_upload_body(mediaData: Data, boundary: String, mediaUploader: any MediaUploaderProtocol, mediaToUpload: MediaUpload, mediaType: ImageUploadMediaType) -> Data { 29 let mediaTypeFieldValue = mediaUploader.mediaTypeValue(for: mediaType) 30 let mediaTypeFieldEntry: String? 31 if let mediaTypeFieldValue { 32 mediaTypeFieldEntry = "; \(mediaUploader.mediaTypeParam)=\(mediaTypeFieldValue)" 33 } 34 else { 35 mediaTypeFieldEntry = nil 36 } 37 let body = NSMutableData(); 38 let contentType = mediaToUpload.mime_type 39 body.appendString(string: "Content-Type: multipart/form-data; boundary=\(boundary)\r\n\r\n") 40 body.appendString(string: "--\(boundary)\r\n") 41 body.appendString(string: "Content-Disposition: form-data; name=\(mediaUploader.nameParam); filename=\(mediaToUpload.genericFileName)\(mediaTypeFieldEntry ?? "")\r\n") 42 body.appendString(string: "Content-Type: \(contentType)\r\n\r\n") 43 body.append(mediaData as Data) 44 body.appendString(string: "\r\n") 45 body.appendString(string: "--\(boundary)--\r\n") 46 return body as Data 47 } 48 49 static func create_upload_request(mediaToUpload: MediaUpload, mediaUploader: any MediaUploaderProtocol, mediaType: ImageUploadMediaType, progress: URLSessionTaskDelegate, keypair: Keypair? = nil) async -> ImageUploadResult { 50 var mediaData: Data? 51 guard let url = URL(string: mediaUploader.postAPI) else { 52 return .failed(nil) 53 } 54 55 var request = URLRequest(url: url) 56 request.httpMethod = "POST"; 57 let boundary = "Boundary-\(UUID().description)" 58 request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") 59 60 // If uploading to a media host that support NIP-98 authorization, add the header 61 if mediaUploader.requiresNip98, 62 let keypair, 63 let method = request.httpMethod, 64 let signature = create_nip98_signature(keypair: keypair, method: method, url: url) { 65 66 request.setValue(signature, forHTTPHeaderField: "Authorization") 67 } 68 69 switch mediaToUpload { 70 case .image(let url): 71 do { 72 mediaData = try Data(contentsOf: url) 73 } catch { 74 return .failed(error) 75 } 76 case .video(let url): 77 do { 78 mediaData = try Data(contentsOf: url) 79 } catch { 80 return .failed(error) 81 } 82 } 83 84 guard let mediaData else { 85 return .failed(nil) 86 } 87 88 request.httpBody = create_upload_body(mediaData: mediaData, boundary: boundary, mediaUploader: mediaUploader, mediaToUpload: mediaToUpload, mediaType: mediaType) 89 90 do { 91 let (data, _) = try await URLSession.shared.data(for: request, delegate: progress) 92 93 guard let url = mediaUploader.getMediaURL(from: data) else { 94 print("Upload failed getting media url") 95 return .failed(nil) 96 } 97 98 return .success(url) 99 100 } catch { 101 return .failed(error) 102 } 103 } 104 } 105 106 extension NSMutableData { 107 func appendString(string: String) { 108 guard let data = string.data(using: String.Encoding.utf8, allowLossyConversion: true) else { 109 return 110 } 111 append(data) 112 } 113 }