Custom NSURLProtocol with NSURLSession

I am trying to implement this tutorial that implements a custom NSURLProtocol with NSURLConnection.

https://www.raywenderlich.com/76735/using-nsurlprotocol-swift

It works as expected, but now that NSURLConnection is deprecated in iOS9, I am trying to convert it to NSURLSession.

Unfortunately, this did not work.

I load the site in uiwebview, if I use NSURLConnection, it loads, and everything works as expected, all HTTP requests from webview are captured, but not when using NSURLSession.

Any help is appreciated.

here is my code

import UIKit class MyProtocol: NSURLProtocol, NSURLSessionDataDelegate, NSURLSessionTaskDelegate, NSURLSessionDelegate { //var connection: NSURLConnection! var mutableData: NSMutableData! var response: NSURLResponse! var dataSession: NSURLSessionDataTask! override class func canInitWithRequest(request: NSURLRequest) -> Bool { if NSURLProtocol.propertyForKey("MyURLProtocolHandledKey", inRequest: request) != nil { return false } return true } override class func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest { return request } override class func requestIsCacheEquivalent(aRequest: NSURLRequest, toRequest bRequest: NSURLRequest) -> Bool { return super.requestIsCacheEquivalent(aRequest, toRequest:bRequest) } override func startLoading() { let newRequest = self.request.mutableCopy() as! NSMutableURLRequest NSURLProtocol.setProperty(true, forKey: "MyURLProtocolHandledKey", inRequest: newRequest) self.dataSession = NSURLSession.sharedSession().dataTaskWithRequest(newRequest) dataSession.resume() self.mutableData = NSMutableData() } override func stopLoading() { print("Data task stop") self.dataSession.cancel() self.mutableData = nil } func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) { self.response = response self.mutableData = NSMutableData() print(mutableData) } func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) { self.client?.URLProtocol(self, didLoadData: data) self.mutableData.appendData(data) } func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) { if (error == nil) { self.client!.URLProtocolDidFinishLoading(self) self.saveCachedResponse() } else { self.client?.URLProtocol(self, didFailWithError: error!) } } func saveCachedResponse () { let timeStamp = NSDate() let urlString = self.request.URL?.absoluteString let dataString = NSString(data: self.mutableData, encoding: NSUTF8StringEncoding) as NSString? print("TiemStamp:\(timeStamp)\nURL: \(urlString)\n\nDATA:\(dataString)\n\n") } } 
+6
source share
3 answers

I solved it.

Here is the code if someone needs it.

 import Foundation class MyProtocol1: NSURLProtocol, NSURLSessionDataDelegate, NSURLSessionTaskDelegate { private var dataTask:NSURLSessionDataTask? private var urlResponse:NSURLResponse? private var receivedData:NSMutableData? class var CustomKey:String { return "myCustomKey" } // MARK: NSURLProtocol override class func canInitWithRequest(request: NSURLRequest) -> Bool { if (NSURLProtocol.propertyForKey(MyProtocol1.CustomKey, inRequest: request) != nil) { return false } return true } override class func canonicalRequestForRequest(request: NSURLRequest) -> NSURLRequest { return request } override func startLoading() { let newRequest = self.request.mutableCopy() as! NSMutableURLRequest NSURLProtocol.setProperty("true", forKey: MyProtocol1.CustomKey, inRequest: newRequest) let defaultConfigObj = NSURLSessionConfiguration.defaultSessionConfiguration() let defaultSession = NSURLSession(configuration: defaultConfigObj, delegate: self, delegateQueue: nil) self.dataTask = defaultSession.dataTaskWithRequest(newRequest) self.dataTask!.resume() } override func stopLoading() { self.dataTask?.cancel() self.dataTask = nil self.receivedData = nil self.urlResponse = nil } // MARK: NSURLSessionDataDelegate func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) { self.client?.URLProtocol(self, didReceiveResponse: response, cacheStoragePolicy: .NotAllowed) self.urlResponse = response self.receivedData = NSMutableData() completionHandler(.Allow) } func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) { self.client?.URLProtocol(self, didLoadData: data) self.receivedData?.appendData(data) } // MARK: NSURLSessionTaskDelegate func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) { if error != nil && error!.code != NSURLErrorCancelled { self.client?.URLProtocol(self, didFailWithError: error!) } else { saveCachedResponse() self.client?.URLProtocolDidFinishLoading(self) } } // MARK: Private methods /** Do whatever with the data here */ func saveCachedResponse () { let timeStamp = NSDate() let urlString = self.request.URL?.absoluteString let dataString = NSString(data: self.receivedData!, encoding: NSUTF8StringEncoding) as NSString? print("TimeStamp:\(timeStamp)\nURL: \(urlString)\n\nDATA:\(dataString)\n\n") } } 
+6
source

Swift 3 version:

 // CustomURLProtocol.swift 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 { guard let host = request.url?.host, host == "your domain.com" else { return false } 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() } 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?) { if error != nil { //&& error.code != NSURLErrorCancelled { self.client?.urlProtocol(self, didFailWithError: error!) } else { //saveCachedResponse() self.client?.urlProtocolDidFinishLoading(self) } } } 
+6
source

The problem you are having is with the code that you use in NSURLSession.sharedSession to contain your data task. Using a shared session, you cannot change the session delegate, so none of your delegate routines will be called.

You will need to create a user session with your protocol set as the delegate for the session. Then, when asked to start the download, you can create a data task in this session.

+5
source

All Articles