@@ -51,7 +51,7 @@ class URLSessionManager: NSObject {
5151 diskCapacity: 1024 * 1024 * 1024 ,
5252 directory: cacheDir
5353 )
54- private static let userAgent : String = {
54+ static let userAgent : String = {
5555 let version = Bundle . main. object ( forInfoDictionaryKey: " CFBundleShortVersionString " ) as? String ?? " unknown "
5656 return " Immich_iOS_ \( version) "
5757 } ( )
@@ -158,6 +158,49 @@ class URLSessionManager: NSObject {
158158
159159 return URLSession ( configuration: config, delegate: delegate, delegateQueue: nil )
160160 }
161+
162+ /// Patches background_downloader's URLSession to use shared auth configuration.
163+ /// Must be called before background_downloader creates its session (i.e. early in app startup).
164+ static func patchBackgroundDownloader( ) {
165+ // Swizzle URLSessionConfiguration.background(withIdentifier:) to inject shared config
166+ let originalSel = NSSelectorFromString ( " backgroundSessionConfigurationWithIdentifier: " )
167+ let swizzledSel = #selector( URLSessionConfiguration . immich_background ( withIdentifier: ) )
168+ if let original = class_getClassMethod ( URLSessionConfiguration . self, originalSel) ,
169+ let swizzled = class_getClassMethod ( URLSessionConfiguration . self, swizzledSel) {
170+ method_exchangeImplementations ( original, swizzled)
171+ }
172+
173+ // Add auth challenge handling to background_downloader's UrlSessionDelegate
174+ guard let targetClass = NSClassFromString ( " background_downloader.UrlSessionDelegate " ) else { return }
175+
176+ let sessionBlock : @convention ( block) ( AnyObject , URLSession , URLAuthenticationChallenge ,
177+ @escaping ( URLSession . AuthChallengeDisposition , URLCredential ? ) -> Void ) -> Void
178+ = { _, session, challenge, completion in
179+ URLSessionManager . shared. delegate. handleChallenge ( session, challenge, completion)
180+ }
181+ class_replaceMethod ( targetClass,
182+ NSSelectorFromString ( " URLSession:didReceiveChallenge:completionHandler: " ) ,
183+ imp_implementationWithBlock ( sessionBlock) , " v@:@@@? " )
184+
185+ let taskBlock : @convention ( block) ( AnyObject , URLSession , URLSessionTask , URLAuthenticationChallenge ,
186+ @escaping ( URLSession . AuthChallengeDisposition , URLCredential ? ) -> Void ) -> Void
187+ = { _, session, task, challenge, completion in
188+ URLSessionManager . shared. delegate. handleChallenge ( session, challenge, completion, task: task)
189+ }
190+ class_replaceMethod ( targetClass,
191+ NSSelectorFromString ( " URLSession:task:didReceiveChallenge:completionHandler: " ) ,
192+ imp_implementationWithBlock ( taskBlock) , " v@:@@@@? " )
193+ }
194+ }
195+
196+ private extension URLSessionConfiguration {
197+ @objc dynamic class func immich_background( withIdentifier id: String ) -> URLSessionConfiguration {
198+ // After swizzle, this calls the original implementation
199+ let config = immich_background ( withIdentifier: id)
200+ config. httpCookieStorage = URLSessionManager . cookieStorage
201+ config. httpAdditionalHeaders = [ " User-Agent " : URLSessionManager . userAgent]
202+ return config
203+ }
161204}
162205
163206class URLSessionManagerDelegate : NSObject , URLSessionTaskDelegate , URLSessionWebSocketDelegate {
@@ -168,7 +211,7 @@ class URLSessionManagerDelegate: NSObject, URLSessionTaskDelegate, URLSessionWeb
168211 ) {
169212 handleChallenge ( session, challenge, completionHandler)
170213 }
171-
214+
172215 func urlSession(
173216 _ session: URLSession ,
174217 task: URLSessionTask ,
@@ -177,7 +220,7 @@ class URLSessionManagerDelegate: NSObject, URLSessionTaskDelegate, URLSessionWeb
177220 ) {
178221 handleChallenge ( session, challenge, completionHandler, task: task)
179222 }
180-
223+
181224 func handleChallenge(
182225 _ session: URLSession ,
183226 _ challenge: URLAuthenticationChallenge ,
@@ -190,7 +233,7 @@ class URLSessionManagerDelegate: NSObject, URLSessionTaskDelegate, URLSessionWeb
190233 default : completionHandler ( . performDefaultHandling, nil )
191234 }
192235 }
193-
236+
194237 private func handleClientCertificate(
195238 _ session: URLSession ,
196239 completion: @escaping ( URLSession . AuthChallengeDisposition , URLCredential ? ) -> Void
@@ -200,7 +243,7 @@ class URLSessionManagerDelegate: NSObject, URLSessionTaskDelegate, URLSessionWeb
200243 kSecAttrLabel as String : CLIENT_CERT_LABEL,
201244 kSecReturnRef as String : true ,
202245 ]
203-
246+
204247 var item : CFTypeRef ?
205248 let status = SecItemCopyMatching ( query as CFDictionary , & item)
206249 if status == errSecSuccess, let identity = item {
@@ -214,7 +257,7 @@ class URLSessionManagerDelegate: NSObject, URLSessionTaskDelegate, URLSessionWeb
214257 }
215258 completion ( . performDefaultHandling, nil )
216259 }
217-
260+
218261 private func handleBasicAuth(
219262 _ session: URLSession ,
220263 task: URLSessionTask ? ,
0 commit comments