URLSession delegate methods don't works - ios

I'm going to read file from API, but its size equal 1.6mb and it takes so much time. I wish to read it by parts, and when i founds data which i needs, i'm going to stop recieve data. I trying to use some delegate methods, but they don't works. I don't understand what goes wrong?
I have next code:
class ViewController: UIViewController, URLSessionTaskDelegate, URLSessionDelegate, URLSessionDataDelegate {
var httpString = "hided"
override func viewDidLoad() {
super.viewDidLoad()
getLogBinData()
}
func getLogBinData() {
let session = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue.main)
if let url = URL(string: httpString + "log.bin") {
var request = URLRequest(url: url)
request.httpMethod = "GET"
let task = session.dataTask(with: request)
task.resume()
}
}
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
print()
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
if dataTask.countOfBytesReceived >= 500 {
print(dataTask.countOfBytesReceived)
}
}
func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive response: URLResponse,
completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
if dataTask.countOfBytesReceived >= 500 {
print(dataTask.countOfBytesReceived)
}
}
}

according the comment, i edited code and it's worked.
class ViewController: UIViewController, URLSessionTaskDelegate, URLSessionDelegate, URLSessionDataDelegate {
var httpString = "hided"
override func viewDidLoad() {
super.viewDidLoad()
getLogBinData()
}
func getLogBinData() {
let session = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue.main)
if let url = URL(string: httpString + "log.bin") {
var request = URLRequest(url: url)
request.httpMethod = "GET"
let task = session.dataTask(with: request)
task.resume()
}
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
if dataTask.countOfBytesReceived >= 500 {
print(dataTask.countOfBytesReceived)
}
}
}

Related

WKWebview could not load asset files because of Authorization

I have a WKWebview applying AWS Cognito.
Every request to the server has to be added Authorization into request header.
let access_token = "Bearer \(key)"
let header: [String: String] = [
"Authorization": access_token
]
if let url = URL(string: "https://myserverdomain.amazonaws.com/api/v3/graphs?date=2020-08-28") {
var request: URLRequest = URLRequest(url: url)
request.allHTTPHeaderFields = header
wkWebview.load(request)
}
With this code, I already can load the page content but CSS in the page. I checked with chrome (using ModHeader chrome extension to add header) and it works, show correctly, also Android.
I inspected by Chrome and the CSS link in < head > tag like this, it is not the same folder with the HTML file (I don't know if it is the reason).
<link rel="stylesheet" type="text/css" href="https://myserverdomain.amazonaws.com/assets/graphs/style.css"></script>
I can load the css content only with the code:
let access_token = "Bearer \(key)"
let header: [String: String] = [
"Authorization": access_token
]
if let url = URL(string: "https://myserverdomain.amazonaws.com/assets/graphs/style.css") {
var request: URLRequest = URLRequest(url: url)
request.allHTTPHeaderFields = header
wkWebview.load(request)
}
UIWebview was deprecated, Is there any way to set WKWebview with a global header as always?
Thank you for your help.
You can redirect all webview's requests to your URLSession with your configuration. To do that you can register your custom URLProtocol for https scheme. There is a hack for WKWebView to intercept url requests with WKBrowsingContextController private class and your URLProtocol implementation e.g.:
class MiddlewareURLProtocol : URLProtocol {
static let handledKey = "handled"
lazy var session : URLSession = {
// Config your headers
let configuration = URLSessionConfiguration.default
//configuration.httpAdditionalHeaders = ["Authorization" : "..."]
return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
}()
var sessionTask : URLSessionTask?
override var task: URLSessionTask? {
return sessionTask
}
static func registerClass() {
let sel = NSSelectorFromString("registerSchemeForCustomProtocol:")
if let cls = NSClassFromString("WKBrowsingContextController") as? NSObject.Type, cls.responds(to:sel) {
// Register https protocol
cls.perform(sel, with: "https")
}
URLProtocol.registerClass(Self.self)
}
override class func canInit(with request: URLRequest) -> Bool {
return URLProtocol.property(forKey: Self.handledKey, in: request) == nil
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
override class func requestIsCacheEquivalent(_ a: URLRequest, to b: URLRequest) -> Bool {
super.requestIsCacheEquivalent(a, to: b)
}
override func startLoading() {
let redirect = (request as NSURLRequest).mutableCopy() as! NSMutableURLRequest
URLProtocol.setProperty(true, forKey: Self.handledKey, in: redirect)
sessionTask = session.dataTask(with: redirect as URLRequest)
task?.resume()
}
override func stopLoading() {
task?.cancel()
}
}
extension MiddlewareURLProtocol : URLSessionDataDelegate {
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let err = error {
client?.urlProtocol(self, didFailWithError: err)
}
else {
client?.urlProtocolDidFinishLoading(self)
}
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .allowed)
completionHandler(.allow)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
client?.urlProtocol(self, didLoad: data)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: #escaping (CachedURLResponse?) -> Void) {
completionHandler(proposedResponse)
}
func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: #escaping (URLRequest?) -> Void) {
let redirect = (request as NSURLRequest).mutableCopy() as! NSMutableURLRequest
Self.removeProperty(forKey: Self.handledKey, in: redirect)
client?.urlProtocol(self, wasRedirectedTo: redirect as URLRequest, redirectResponse: response)
self.task?.cancel()
let error = NSError(domain: NSCocoaErrorDomain, code: CocoaError.Code.userCancelled.rawValue, userInfo: nil)
client?.urlProtocol(self, didFailWithError: error)
}
}
Just register your protocol on app start to handle all requests:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
MiddlewareURLProtocol.registerClass()
...
}
NOTE: To prevent Apple static checks for private classes you can store class names in the array:
let className = ["Controller", "Context", "Browsing", "WK"].reversed().joined()

URL session saving image

quick question. I want to extract the dowloaded image from the url and save it to a UI Image.
How would I do this?
fileprivate func beginDownload() {
let url = URL(string: "URL")!
let configuration = URLSessionConfiguration.default
let operationQueue = OperationQueue()
let urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: operationQueue)
let downloadTask = urlSession.downloadTask(with: url)
downloadTask.resume()
}
Here are my URL session protocol stubs:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
print(totalBytesWritten, totalBytesExpectedToWrite)
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("Finished dowloading file")
}
You need to load the image from the download location created in the delegate method urlSession(_:downloadTask:didFinishDownloadingTo:):
class Request: NSObject {
func getPicture() {
let url = URL(string: "https://media.tractorsupply.com/is/image/TractorSupplyCompany/1305371?$456$")!
let session = URLSession(configuration: .default,
delegate: self,
delegateQueue: nil)
session.downloadTask(with: url).resume()
}
}
extension Request: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL) {
guard let data = try? Data(contentsOf: location),
let image = UIImage(data: data) else { return }
print(image)
}
}
Request().getPicture()

Cannot get data from URLSessionTaskDelegate

I am trying to add background fetch capability to my app. Currently, the delegate function urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) is called after the network call is complete, but the URL in the cache directory does not exist:
class DownloadManager: NSObject, URLSessionTaskDelegate, URLSessionDownloadDelegate {
static var shared = DownloadManager()
var session : URLSession {
get {
let config = URLSessionConfiguration.background(withIdentifier: "my_Identifier")
return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print(location.absoluteString)
do {
let myData = try Data(contentsOf: location)
} catch let error {
print(error.localizedDescription)
// The The file “file_id.tmp” couldn’t be opened because there is no such file.
}
}
public func fetch() {
guard let url = URL(string: "#{myURL}") else {
return
}
let task = session.downloadTask(with: url)
task.resume()
}
}
And in my App Delegate:
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("Executing background fetch")
DownloadManager.shared.fetch()
completionHandler(.newData)
}
What am I missing?
Try using this:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var progressView: UIProgressView!
override func viewDidLoad() {
let _ = DownloadManager.shared.activate()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DownloadManager.shared.onProgress = { (progress) in
OperationQueue.main.addOperation {
self.progressView.progress = progress //assign progress value
}
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
DownloadManager.shared.onProgress = nil
}
#IBAction func startDownload(_ sender: Any) {
let url = URL(string: "YourFileURL")!
DownloadManager.shared.download(url)
}
}
Replace your DownloadManager:
import Foundation
class DownloadManager : NSObject, URLSessionDelegate, URLSessionDownloadDelegate {
static var shared = DownloadManager()
var url : URL?
typealias ProgressHandler = (Float) -> ()
var onProgress : ProgressHandler? {
didSet {
if onProgress != nil {
let _ = activate()
}
}
}
override private init() {
super.init()
}
func activate() -> URLSession {
let config = URLSessionConfiguration.background(withIdentifier: "\(Bundle.main.bundleIdentifier!).background")
// Warning: If an URLSession still exists from a previous download, it doesn't create a new URLSession object but returns the existing one with the old delegate object attached!
return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
}
private func calculateProgress(session : URLSession, completionHandler : #escaping (Float) -> ()) {
session.getTasksWithCompletionHandler { (tasks, uploads, downloads) in
let progress = downloads.map({ (task) -> Float in
if task.countOfBytesExpectedToReceive > 0 {
return Float(task.countOfBytesReceived) / Float(task.countOfBytesExpectedToReceive)
} else {
return 0.0
}
})
completionHandler(progress.reduce(0.0, +))
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
if totalBytesExpectedToWrite > 0 {
if let onProgress = onProgress {
calculateProgress(session: session, completionHandler: onProgress)
}
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
debugPrint("Progress \(downloadTask) \(progress)")
}
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
debugPrint("Download finished: \(location)")
// try? FileManager.default.removeItem(at: location)
//copy downloaded data to your documents directory with same names as source file
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let destinationUrl = documentsUrl!.appendingPathComponent(url!.lastPathComponent)
let dataFromURL = try? Data(contentsOf: location)
try? dataFromURL?.write(to: destinationUrl, options: [.atomic])
print(destinationUrl)
//now it is time to do what is needed to be done after the download
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
debugPrint("Task completed: \(task), error: \(String(describing: error))")
}
func download(_ url: URL)
{
self.url = url
//download identifier can be customized. I used the "ulr.absoluteString"
let task = DownloadManager.shared.activate().downloadTask(with: url)
task.resume()
}
}
And in my App Delegate:
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: #escaping () -> Void) {
debugPrint("handleEventsForBackgroundURLSession: \(identifier)")
completionHandler()
}
Reference: Tutorial by ralfebert

ios download file with data chunks delegate

I want to download a file from a certain url .
what I need is the data delegate ( the delegate which gives me the downloaded data) .
I implemented the :
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
delegate but it doesn't get called .
my code is :
func download(url: URL)
{
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
let task = session.downloadTask(with: url)
task.resume()
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
print("apending ")
self.dataaa.append(data)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
print("here")
}
the two delegate functions don't get called !
You're using the delegate methods for a data task, but you're creating a download task. Those two task types work differently and use completely different delegate methods.

Swift 3: UrlProtocol didCompleteWithError delegate never be called

As title, I'm trying to build a custom url protocol.
I found this and I followed the code provided completely.
However, this delegate
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
is never be triggered or called.
Furthermore, compiler prompted a warning message said that it nearly matches the optionals requirement of URLSessionTaskDelegate, quick fix provided by compiler was to make it private.
So, How do I call the didCompleteWithError delegate, is the code provided in here missing out some parts? Or this is a known issue? Please let me know if there is any workaround solution or a better Swift 3 example of custom UrlProtocol. Thanks in advance!、
Edit 1:
class CustomURLProtocol: URLProtocol, URLSessionDataDelegate, URLSessionTaskDelegate {
private var dataTask: URLSessionDataTask?
private var urlResponse: URLResponse?
private var receivedData: NSMutableData?
class var CustomHeaderSet: String {
return "CustomHeaderSet"
}
// MARK: NSURLProtocol
override class func canInit(with request: URLRequest) -> Bool {
if (URLProtocol.property(forKey: CustomURLProtocol.CustomHeaderSet, in: request as URLRequest) != nil) {
return false
}
return true
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}
override func startLoading() {
let mutableRequest = NSMutableURLRequest.init(url: self.request.url!, cachePolicy: NSURLRequest.CachePolicy.useProtocolCachePolicy, timeoutInterval: 240.0)//self.request as! NSMutableURLRequest
//Add User Agent
var userAgentValueString = "myApp"
mutableRequest.setValue(userAgentValueString, forHTTPHeaderField: "User-Agent")
print(mutableRequest.allHTTPHeaderFields ?? "")
URLProtocol.setProperty("true", forKey: CustomURLProtocol.CustomHeaderSet, in: mutableRequest)
let defaultConfigObj = URLSessionConfiguration.default
let defaultSession = URLSession(configuration: defaultConfigObj, delegate: self, delegateQueue: nil)
self.dataTask = defaultSession.dataTask(with: mutableRequest as URLRequest)
self.dataTask!.resume()
print("loaded")
}
override func stopLoading() {
self.dataTask?.cancel()
self.dataTask = nil
self.receivedData = nil
self.urlResponse = nil
}
// MARK: NSURLSessionDataDelegate
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask,
didReceive response: URLResponse,
completionHandler: #escaping (URLSession.ResponseDisposition) -> Void) {
self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
self.urlResponse = response
self.receivedData = NSMutableData()
completionHandler(.allow)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
self.client?.urlProtocol(self, didLoad: data as Data)
self.receivedData?.append(data as Data)
}
// MARK: NSURLSessionTaskDelegate
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
print("completed")
if error != nil { //&& error.code != NSURLErrorCancelled {
self.client?.urlProtocol(self, didFailWithError: error! as! Swift.Error)
} else {
//saveCachedResponse()
self.client?.urlProtocolDidFinishLoading(self)
}
}
}
As the code posted there, those are the changes I made, the 'loaded' was called but 'completed' is never be called.
Edit 2:
Warning message prompted by compiler

Resources