iOS background download not respecting httpMaximumConnectionsPerHost - ios

I am on iOS 14.2...
I have a URLSession that i configure that way:
private lazy var backgroundURLSession: URLSession = {
let config = URLSessionConfiguration.background(withIdentifier: "background-download")
config.sessionSendsLaunchEvents = true
config.httpMaximumConnectionsPerHost = 2
config.timeoutIntervalForRequest = 120
return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()
I give it like 100 URLs to download
let downloadTask = session.downloadTask(with: offlineTile.url)
downloadTask.resume()
and even with httpMaximumConnectionsPerHost = 2 the server gets ALL requests at ONCE... ?!?
What could I be doing wrong
One more note: We have a Varnish Cache in the background... and noticed that the behavior differs, if Varnish is set to pipe (no caching)

In our case it was the load balancer of our server that did not properly to it's https termination, which caused iOS to send all requests at basically once.

Related

Background transfer of HLS using makeAssetDownloadTask only downloads about half of the data

I am using AVAssetDownloadURLSession's makeAssetDownloadTask method on iOS 15 and iOS 16 to simultaneously play and download an HLS VOD stream. Sample code:
#objc func createDownloadSession() {
// Create new background session configuration.
let configuration = URLSessionConfiguration.background(withIdentifier: "TestAppSession")
configuration.timeoutIntervalForResource = 60 * 60 * 2
// Create a new AVAssetDownloadURLSession with background configuration, delegate, and queue
downloadSession = AVAssetDownloadURLSession(configuration: configuration,
assetDownloadDelegate: self,
delegateQueue: OperationQueue.main)
}
#objc func downloadAndCreatePlayerItem(_ url: URL) -> AVPlayerItem? {
// Create new AVAssetDownloadTask for the desired asset
let asset = AVURLAsset(url: url)
// Create the task to download the track.
let downloadTask = downloadSession.makeAssetDownloadTask(asset: asset,
assetTitle: "Test Title",
assetArtworkData: nil,
options: nil)
// Start task
downloadTask!.resume()
// Return the URL asset
return AVPlayerItem(asset: downloadTask!.urlAsset)
}
Everything seems to work fine; it starts pulling the data down at a fast rate, and plays the audio immediately.
However, after a few seconds it stops downloading. It seems to only load around 15 minutes of the HLS audio (I can see the loadedTimeRanges).
How do I get it to download the entire HLS stream? Is there some throttling I am encountering by the OS? The app is in the foreground, though it is a background session. Anything I'm missing? Thank you in advance!

WKWebCache Cache

I need to cache webview url on WKWebview. I'm able to do it using the below configuration
var webViewConfiguration:WKWebViewConfiguration {
get {
// Create WKWebViewConfiguration instance
let webCfg:WKWebViewConfiguration = WKWebViewConfiguration()
// Configure the WKWebViewConfiguration instance with the WKUserContentController
webCfg.userContentController = userContentController
webCfg.websiteDataStore = WKWebsiteDataStore.default()
webCfg.processPool = ProcessPool.shared.processPool
return webCfg
}
}
and while loading the webview I'm using the below code:
let request = URLRequest(url: self.url!, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 600)
self.webView.load(request)
Issue I'm facing right now is the cache is taking time on every launch. i.e on every launch webview is taking a lot of time to load, after one load it is loading fast.
What I need to achieve is once webview is loaded, it should load faster on consecutive loads.
.useProtocolCachePolicy is the default policy for URL load requests. It should work for your case.
let request = URLRequest(url: self.url!, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 600)
Using the default .useProtocolCachePolicy instead of .returnCacheDataElseLoad is OK. This policy will automatically look at the response from the server to decide whether or not it should actually go and grab the data again. On the server side, set the Cache-Control HTTP headers max age for its responses.

Changing AlamoFire Config

In a class my project is using have a var to store the alamofire manager:
var alamoManager: Manager!
A method is called repeatedly in the app to config this manager like so:
func configAlamoManager() {
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.timeoutIntervalForRequest = 20
//ETC
alamoManager = Alamofire.Manager(configuration: configuration)
}
I have a HTTP call in my app that is ocasiaonally returning a 999 canceled error code. I suspect this is because the manager currently trying to perform the request is replaced by another one from the configAlamoManager() method. Is there any way to just change the config settings in the manager without creating a new instance? alamoManager.session.configuration has no setter. Any pointers on this would be really appreciated! Thanks
Instead of changing the configuration and creating a new Manager, you should override the configuration in the actual NSURLRequest.
let urlRequest = NSURLRequest(url: url)
urlRequest.timeoutInterval = 20
Alamofire.request(urlRequest).responseJSON { response in
debugPrint(response)
}
For more info on what you can override using an NSURLRequest, I'd check out the docs.

NSURLSession, after the data task is converted to download task, it can't download in background

If I run the following code and let the app in background, the download is still continuing. Finally, when the download is finished, I can get the right callback.
let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(SessionProperties.identifier)
let backgroundSession = NSURLSession(configuration: configuration, delegate: self.delegate, delegateQueue: nil)
let url = NSURLRequest(URL: NSURL(string: data[1])!)
let downloadTask = backgroundSession.downloadTaskWithRequest(url)
downloadTask.resume()
But I have a requirement, that is I have to judge what the server returns to me, if it is a json, I don't do the download, so I want to get the response header first, then if it needs to download, I change the data task to download task, so I did as the following code
let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(SessionProperties.identifier)
let backgroundSession = NSURLSession(configuration: configuration, delegate: self.delegate, delegateQueue: nil)
let url = NSURLRequest(URL: NSURL(string: data[1])!)
//I change the downloadTaskWithRequest to dataTaskWithRequest
let downloadTask = backgroundSession.dataTaskWithRequest(url)
downloadTask.resume()
Then I can get the response header in the callback, and if it needs to download file, I can change the data task to download task, as following
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
if let response = response as? NSHTTPURLResponse {
let contentType = response.allHeaderFields["Content-Type"] as! String
if contentType == "image/jpeg" {
//change the data task to download task
completionHandler(.BecomeDownload)
return
}
}
completionHandler(.Allow)
}
So far so good. When I run the app in the foreground, the effect is like what I thought. But after the app runs in background, the download is stoped, then when I open the app, the console says "Lost connection to background transfer service".
I thought Apple is so smart, he gives us many useful callbacks, but now, I didn't know where I am wrong, and I also see the source code about the AFNetworking and Alamofire, but I didn't find the referring thing.
I also think it is a common requirement, but I can't find any helpful information on the internet, it is too odd.
So hope you can help me out, thanks a billion.
Enable Background Mode in
Xcode->Target->Capabilities->On Background Mode and select the option Background Fetch.
The main issue I see is that you're calling the completionHandler twice. You need to return out of your content-type conditional like so:
if contentType == "image/jpeg" {
//change the data task to download task
completionHandler(.BecomeDownload)
return
}
Otherwise it appears that you are using the logic correctly. Hope that helps.
The problem is evident from your own answer. It's not a bug, you simply couldn't use data tasks for background transfers just download tasks.
Here is the correct full answer.

Prevent NSURLSessionDataTask from repeatedly firing request

I am using a NSURLSessionDataTask to get JSON data from a device that opens an adhoc-WiFi which is joined by an iPhone. (The connection is running via TCP).
The device is pretty slow - it takes roundabout 5sec to send the particular response.
After firing the request via dataTask.resume() my app is just waiting for the completionHandler to be called.
Unfortunately the request is fired again and again (each after a delay of ~ 1 sec). I guess this is initiated by the dataTask or NSURLSession. I can monitor the repeated requests when debugging the device.
Here's the code I'm using:
let URL = NSURL(string: "<myURL>")!
let configuration = NSURLSessionConfiguration.ephemeralSessionConfiguration()
configuration.HTTPShouldUsePipelining = false
configuration.HTTPMaximumConnectionsPerHost = 1
configuration.allowsCellularAccess = false
configuration.timeoutIntervalForRequest = 30
let session = NSURLSession(configuration: configuration)
let dataTask = session.dataTaskWithURL(URL) {
// the request gets fired repeatedly before this completionHandler is called!
(data, response, error) -> Void in
...
}
dataTask.resume()
Does anyone know how to prevent iOS from re-firing these requests?

Resources