SwiftUI,如何在使用 await urlSession.upload(...) 上传 multipart/form-data 文件时取得进展

SwiftUI, how to get progress whilst uploading multipart/form-data files using await urlSession.upload(...)

iOS15+

如何使用 await urlSession.upload 从以下获取上传进度?

我特别想要使用 urlSession.uploadTask 似乎无法获得(或理解如何)的等待功能。

代码上传文件正常。

我试过在底部使用 urlSession 函数,但它从未触发。

https://developer.apple.com/documentation/foundation/urlsessiontaskdelegate/1408299-urlsession

请注意,我通常使用 C# 编写代码,而且我主要是 swift 的初学者。

谢谢。

NetworkManager.swift

import SwiftUI

class NetworkManager {
    
    static let shared = NetworkManager()
    
    private init() {}
    
    func uploadZipFile (
        zipFileURL: URL) async throws -> (Data, URLResponse) {
    
            let name:     String = zipFileURL.deletingPathExtension().lastPathComponent
            let fileName: String = zipFileURL.lastPathComponent
            
            let zipFileData: Data?
            
            do {
                zipFileData = try Data(contentsOf: zipFileURL)
            } catch {
                throw error
            }
            
            let uploadApiUrl: URL? = URL(string: "https://someapi.com/upload")
        
            // Generate a unique boundary string using a UUID.
            let uniqueBoundary = UUID().uuidString

            var bodyData = Data()
            
            // Add the multipart/form-data raw http body data.
            bodyData.append("\r\n--\(uniqueBoundary)\r\n".data(using: .utf8)!)
            bodyData.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!)
            bodyData.append("Content-Type: application/zip\r\n\r\n".data(using: .utf8)!)
            
            // Add the zip file data to the raw http body data.
            bodyData.append(zipFileData!)
    
            // End the multipart/form-data raw http body data.
            bodyData.append("\r\n--\(uniqueBoundary)--\r\n".data(using: .utf8)!)
            
            let urlSessionConfiguration = URLSessionConfiguration.default
            
            let urlSession
                = URLSession(
                    configuration: urlSessionConfiguration,
                    delegate: nil,                           // Something I need here maybe?
                    delegateQueue: nil)
            
            var urlRequest = URLRequest(url: uploadApiUrl!)
            
            // Set Content-Type Header to multipart/form-data with the unique boundary.
            urlRequest.setValue("multipart/form-data; boundary=\(uniqueBoundary)", forHTTPHeaderField: "Content-Type")
            
            urlRequest.httpMethod = "POST"
            
            let (data, urlResponse) = try await urlSession.upload(
                for: urlRequest,
                from: bodyData,
                delegate: nil   // Something I need here maybe?
            )

            return (data, urlResponse)
    }
    
    // Tried this but it never fires.
    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        didSendBodyData bytesSent: Int64,
        totalBytesSent: Int64,
        totalBytesExpectedToSend: Int64) {
        
        print("fractionCompleted  : \(Float(totalBytesSent) / Float(totalBytesExpectedToSend))")
            
    }
}

回答我自己的问题。

如果有人发现任何错误或不良做法,请随时发表评论。

NetworkManager.swift

import SwiftUI

class NetworkManager: NSObject {
    
    static let shared = NetworkManager()
    
    private override init() {}
    
    func uploadZipFile (
        zipFileURL: URL) async throws -> (Data, URLResponse) {
    
            let name:     String = zipFileURL.deletingPathExtension().lastPathComponent
            let fileName: String = zipFileURL.lastPathComponent
            
            let zipFileData: Data?
            
            do {
                zipFileData = try Data(contentsOf: zipFileURL)
            } catch {
                throw error
            }
            
            let uploadApiUrl: URL? = URL(string: "https://someapi.com/upload")
        
            // Generate a unique boundary string using a UUID.
            let uniqueBoundary = UUID().uuidString

            var bodyData = Data()
            
            // Add the multipart/form-data raw http body data.
            bodyData.append("\r\n--\(uniqueBoundary)\r\n".data(using: .utf8)!)
            bodyData.append("Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!)
            bodyData.append("Content-Type: application/zip\r\n\r\n".data(using: .utf8)!)
            
            // Add the zip file data to the raw http body data.
            bodyData.append(zipFileData!)
    
            // End the multipart/form-data raw http body data.
            bodyData.append("\r\n--\(uniqueBoundary)--\r\n".data(using: .utf8)!)
            
            let urlSessionConfiguration = URLSessionConfiguration.default
            
            let urlSession
                = URLSession(
                    configuration: urlSessionConfiguration,
                    delegate: self,         
                    delegateQueue: nil)
            
            var urlRequest = URLRequest(url: uploadApiUrl!)
            
            // Set Content-Type Header to multipart/form-data with the unique boundary.
            urlRequest.setValue("multipart/form-data; boundary=\(uniqueBoundary)", forHTTPHeaderField: "Content-Type")
            
            urlRequest.httpMethod = "POST"
            
            let (data, urlResponse) = try await urlSession.upload(
                for: urlRequest,
                from: bodyData,
                delegate: nil 
            )

            return (data, urlResponse)
    }
}

extension NetworkManager: URLSessionTaskDelegate {
    
    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        didSendBodyData bytesSent: Int64,
        totalBytesSent: Int64,
        totalBytesExpectedToSend: Int64) {
        
        print("fractionCompleted  : \(Int(Float(totalBytesSent) / Float(totalBytesExpectedToSend) * 100))")
            
    }
    
}