ios - URLSession backgroundSession not invoking urlSessionDidFinishEvents or handleEventsForBackgroundURLSession - Stack Overflo

I am struggling with backgroundSession capabilities.I set capabilities to background fetch and process

I am struggling with backgroundSession capabilities. I set capabilities to background fetch and processing, set backgroundSession with URLSessionConfiguration.background.backgroundSession?.uploadTask is functioning good, and

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)`

is invoked in the end without error.

When trying to test background (putting the app in the background), urlSessionDidFinishEvents(forBackgroundURLSession is not triggered nor handleEventsForBackgroundURLSession in appDelegate. (checked also coming back from background to foreground) (BTW - the background completes successfully)

What am I missing?

class AppDelegate: NSObject, UIApplicationDelegate {
    let gcmMessageIDKey = "gcm.message_id"
    var backgroundCompletionHandler: (() -> Void)?
    var userService: UserService?

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        FirebaseApp.configure()
        FirebaseConfiguration.shared.setLoggerLevel(FirebaseLoggerLevel.debug)

        Messaging.messaging().delegate = self
        UNUserNotificationCenter.current().delegate = self
        
        AnalyticsManager.startMonitoringNetwork()

        return true
    }
    
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                       fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
          
          if let messageID = userInfo[gcmMessageIDKey] {
              print("Message ID application: \(messageID)")
          }
          
          print(userInfo)
          
          completionHandler(UIBackgroundFetchResult.newData)
      }
    
    func application(_ app: UIApplication,
                     open url: URL,
                     options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
        return GIDSignIn.sharedInstance.handle(url)
    }
    
    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
        print("☠️ handleEventsForBackgroundURLSession")
        userService?.apiClient.backgroundSessionCompletionHandler = completionHandler
    }
}

final class URLSessionAPIClient: NSObject, ServiceProtocol {
    private let progress: PassthroughSubject<(id: Int, progress: Double), Never> = .init()
    private var session: URLSession
    private var baseUrl: String
    private var decoder: JSONDecoder
    private let tokenExpiredSubject = PassthroughSubject<Void, Never>()
    private var cancellables = Set<AnyCancellable>()
    private var retryCount = 0
    private var fireBaseManager: FireBaseManager
    private var backgroundSession: URLSession?
    private var tempFileURL: URL?
    private var subject = PassthroughSubject<Data, Error>()
    
    var tokenExpiredPublisher: AnyPublisher<Void, Never> {
        tokenExpiredSubject.eraseToAnyPublisher()
    }
    
    var backgroundSessionCompletionHandler: (() -> Void)?
    
    
    init(
        baseUrl: String = GlobalConstants.baseURL,
        sessionConfiguration: URLSessionConfiguration = .default,
        decoder: JSONDecoder = JSONDecoder(),
        firebaseManager: FireBaseManager = FireBaseManager()
    ) {
        
        self.baseUrl = baseUrl
        self.session = URLSession(configuration: sessionConfiguration)
        self.decoder = decoder
        self.fireBaseManager = firebaseManager
        super.init()
        
        let backgroundConfig = URLSessionConfiguration.background(withIdentifier: "backgroundUpload")
        
        self.backgroundSession = URLSession(configuration: backgroundConfig, delegate: self, delegateQueue: nil)
        
    }
    
    private func saveRequestBodyToTemporaryFile(_ body: Data) throws -> URL {
        let tempDirectory = FileManager.default.temporaryDirectory
        let tempFileURL = tempDirectory.appendingPathComponent(UUID().uuidString)
        try body.write(to: tempFileURL)
        
        self.tempFileURL = tempFileURL
        return tempFileURL
    }
    
    private func uploadLargeFile2<R: Decodable>(request: URLRequest, fileURL: URL) -> AnyPublisher<R, Error> {
        print("☠️ inside uploadLargeFile with native URLSession")
        
        let subject = PassthroughSubject<Data, Error>()
        
        
        guard let httpBody = request.httpBody else {
            return Fail(error: NSError(domain: "URLError", code: 0, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]))
                .eraseToAnyPublisher()
        }
        
        guard let temporaryFileURL = try? saveRequestBodyToTemporaryFile(httpBody) else {
            return Fail(error: NSError(domain: "URLError", code: 0, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]))
                .eraseToAnyPublisher()
        }
        var requestTest = request
        requestTest.httpBody = nil
        
        let task = backgroundSession?.uploadTask(with: requestTest, fromFile: temporaryFileURL)
        
        
        task?.resume()
        
        return subject
            .decode(type: R.self, decoder: decoder)
            .eraseToAnyPublisher()
    }
    
    
    func sendRequest<T: HTTPRequest, R: Decodable>(_ request: T) -> AnyPublisher<R, Error> {
        guard let urlRequest = urlRequest(from: request, baseURL: baseUrl) else {
            return Fail(error: APIError.invalidData)
                .eraseToAnyPublisher()
        }
        
        
        let isLargeFileR = request.urlPath.flatMap { isLargeFile(url: $0) } ?? false
        
        
        if isLargeFileR {
            // Use the path as the file URL
            guard URL(string: request.path) != nil else {
                return Fail(error: APIError.invalidData).eraseToAnyPublisher()
            }
            guard let file = request.urlPath else { return Fail(error: APIError.invalidData)
                .eraseToAnyPublisher()}
            
            return uploadLargeFile2(request: urlRequest, fileURL: file)
        } else {
            
            return sendDataTask(request: urlRequest)
        }
    }
}
private extension URLSessionAPIClient {
    func urlRequest(from request: any HTTPRequest, baseURL: String) -> URLRequest? {
        guard let url = URL(string: baseURL + request.path) else {
            return nil
        }
        var urlRequest = URLRequest(url: url)
        urlRequest.timeoutInterval = GlobalConstants.requestTimeoutInterval
        urlRequest.httpMethod = request.method.rawValue
        request.headers?.forEach { urlRequest.addValue($0.value, forHTTPHeaderField: $0.key) }
        
        if request.authRequirement == .requiresAuth {
            urlRequest.addValue(CredentialManager.shared.getValue(.token), forHTTPHeaderField: GlobalConstants.googleTokenHeaderKey)
            urlRequest.addValue(CredentialManager.shared.getValue(.device), forHTTPHeaderField: GlobalConstants.deviceTokenHeaderKey)
            urlRequest.addValue(CredentialManager.shared.getValue(.email), forHTTPHeaderField: GlobalConstants.userEmailKey)
        }
        
        urlRequest.httpBody = request.body
        if let httpBody = urlRequest.httpBody {
            urlRequest.setValue("\(String(describing: httpBody.count))", forHTTPHeaderField: "Content-Length")
        }
        
        return urlRequest
    }
}
extension URLSessionAPIClient {
    func isLargeFile(url: URL) -> Bool {
        // Check if the request contains a valid file URL in the path
        
        // Otherwise, check the file size for large files
        do {
            let fileSize = try FileManager.default.attributesOfItem(atPath: url.path)[.size] as? NSNumber
            if let size = fileSize, size.intValue > 200 * 1024 * 1024 { // 200MB
                return true
            }
        } catch {
            print("Error checking file size: \(error)")
        }
        
        return false
    }
}
extension URLSessionAPIClient: URLSessionDelegate, URLSessionTaskDelegate, 
     URLSessionDataDelegate {
    // Handle the completion of the task
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError 
        error: Error?) {
        if let error = error {
            print("☠️ Upload failed with error: \(error.localizedDescription)")
            // Handle the error here (e.g., notify the user, retry the upload, etc.)
        } else {
            print("☠️ Upload completed successfully.")
            // You can handle the successful upload here
        }
        
        if let tempFileURL = self.tempFileURL {
            try? FileManager.default.removeItem(at: tempFileURL)
        }
        
        // Call the background session completion handler to finish the task
        if let completionHandler = backgroundSessionCompletionHandler {
            completionHandler()
        }
    }
    
    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        print("☠️ Background tasks finished")
        self.backgroundSessionCompletionHandler?()
        self.backgroundSessionCompletionHandler = nil
        
    }
    
    func urlSession(_ session: URLSession, uploadTask: URLSessionUploadTask, didSendBodyData bytesSent: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
        print("☠️ Upload Progress: \(progress * 100)%")
    }
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744266120a4565873.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信