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条)