apple_kit

Apple-native services for Radroots iOS and macOS apps
git clone https://radroots.dev/git/apple_kit.git
Log | Files | Refs | README | LICENSE

RadrootsAppleBackgroundTransferAdapters.swift (4815B)


      1 import Foundation
      2 
      3 public struct RadrootsAppleBackgroundTransferAdapters: Sendable {
      4     /// System-managed background sessions cannot establish the required
      5     /// redirect/connect guarantee. Keep their legacy recovery operational,
      6     /// and route new public work through the host's shared foreground uploader.
      7     public static func supportsNewEnqueue(for policy: RadrootsBackgroundTransferNetworkPolicy) -> Bool {
      8         #if os(iOS) && targetEnvironment(simulator)
      9             policy == .simulatorLoopbackHTTP
     10         #else
     11             false
     12         #endif
     13     }
     14 
     15     public let now: @Sendable () -> Date
     16     public let enqueue: @Sendable (RadrootsBackgroundTransferRequest, UUID) async throws -> Void
     17     public let cancel: @Sendable (RadrootsBackgroundTransferIdentifier) async throws -> Void
     18     public let activeTransferIdentifiers: @Sendable () async throws -> Set<RadrootsBackgroundTransferIdentifier>
     19     public let handleBackgroundEvents: @Sendable (String, @escaping @Sendable () -> Void) async -> Void
     20 
     21     public init(
     22         now: @escaping @Sendable () -> Date = Date.init,
     23         enqueue: @escaping @Sendable (RadrootsBackgroundTransferRequest, UUID) async throws -> Void,
     24         cancel: @escaping @Sendable (RadrootsBackgroundTransferIdentifier) async throws -> Void,
     25         activeTransferIdentifiers:
     26         @escaping @Sendable () async throws -> Set<RadrootsBackgroundTransferIdentifier>,
     27         handleBackgroundEvents:
     28         @escaping @Sendable (String, @escaping @Sendable () -> Void) async -> Void
     29     ) {
     30         self.now = now
     31         self.enqueue = enqueue
     32         self.cancel = cancel
     33         self.activeTransferIdentifiers = activeTransferIdentifiers
     34         self.handleBackgroundEvents = handleBackgroundEvents
     35     }
     36 
     37     public static let unavailable = Self(
     38         enqueue: { _, _ in
     39             throw RadrootsBackgroundTransferError.unavailable
     40         },
     41         cancel: { _ in
     42             throw RadrootsBackgroundTransferError.unavailable
     43         },
     44         activeTransferIdentifiers: {
     45             throw RadrootsBackgroundTransferError.unavailable
     46         }, handleBackgroundEvents: { _, completionHandler in completionHandler() }
     47     )
     48 
     49     public static func live(
     50         sessionIdentifier: String, store: any RadrootsBackgroundTransferStore,
     51         fileResolver: any RadrootsBackgroundTransferFileResolver,
     52         downloadStagingRoot: URL, now: @escaping @Sendable () -> Date = Date.init
     53     ) throws -> Self {
     54         #if os(iOS)
     55             let normalizedSessionIdentifier =
     56                 try RadrootsBackgroundTransferValidation.normalizedIdentifier(sessionIdentifier)
     57             let session = RadrootsAppleBackgroundURLSession(
     58                 identifier: normalizedSessionIdentifier, store: store, fileResolver: fileResolver,
     59                 downloadStagingRoot: downloadStagingRoot,
     60                 now: now
     61             )
     62             #if targetEnvironment(simulator)
     63                 let simulatorSession = RadrootsAppleBackgroundURLSession(
     64                     identifier: normalizedSessionIdentifier, store: store, fileResolver: fileResolver,
     65                     downloadStagingRoot: downloadStagingRoot,
     66                     now: now, usesForegroundSession: true
     67                 )
     68             #endif
     69             return Self(
     70                 now: now,
     71                 enqueue: { request, executionID in
     72                     guard Self.supportsNewEnqueue(for: request.networkPolicy) else {
     73                         throw RadrootsBackgroundTransferError.unavailable
     74                     }
     75                     #if targetEnvironment(simulator)
     76                         try await simulatorSession.enqueue(request, executionID: executionID)
     77                     #else
     78                         throw RadrootsBackgroundTransferError.unavailable
     79                     #endif
     80                 },
     81                 cancel: { identifier in
     82                     #if targetEnvironment(simulator)
     83                         try await simulatorSession.cancel(identifier)
     84                     #endif
     85                     try await session.cancel(identifier)
     86                 },
     87                 activeTransferIdentifiers: {
     88                     var identifiers = try await session.activeTransferIdentifiers()
     89                     #if targetEnvironment(simulator)
     90                         try await identifiers.formUnion(simulatorSession.activeTransferIdentifiers())
     91                     #endif
     92                     return identifiers
     93                 },
     94                 handleBackgroundEvents: { identifier, completionHandler in
     95                     await session.handleBackgroundEvents(
     96                         identifier: identifier, completionHandler: completionHandler
     97                     )
     98                 }
     99             )
    100         #else
    101             return .unavailable
    102         #endif
    103     }
    104 }