Search code examples
iosnsurlsessionswift-extensionsprotocol-extension

Default implementation of protocol method with Swift extension


I'm trying to write default behaviour for a delegate method using a Swift extension as below, but it is never called. Does anyone know why or how to do it the right way?

extension NSURLSessionDelegate {

    public func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
        //default behaviour here
    }
}

Adding override does not work either.

According to this, Apple's default implementation looks like:

extension NSURLSessionDelegate {
    func URLSession(session: NSURLSession, didBecomeInvalidWithError error: NSError?) { }
    func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) { }
}

My DataTask calls typically look like this:

let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
    sessionConfiguration.HTTPCookieStorage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
let session = NSURLSession(configuration: sessionConfiguration)
let requestURL = NSURL(string:"https:www.google.com/blabla")

session.dataTaskWithURL(requestURL!, completionHandler: completion).resume()

Where completion will typically be a Swift closure received via parameter.

I need to implement the URLSession(... didReceiveChallenge ...) function for all nsurlsessiontask implementations throughout my app, but can't set my session's delegate as I need to use the completionHandler (as mentioned in my comment below).


Solution

  • You can extends the NSURLSessionDelegate protocol for adding default implementation, but your NSURLSession objects needs a delegate.

    This delegate can only be set using +sessionWithConfiguration:delegate:delegateQueue: (since the delegate property is read only), so your only way to set it is to subclass NSURLSession, override +sessionWithConfiguration: and call the initializer with the delegate property. The issue here is that you have to replace all your NSURLSession objects to MyCustomSessionClass. objects.

    I suggest you to create a SessionCreator class which will conforms to NSURLSessionDelegate protocol and will create NSURLSessionobjects. You still have to replace the creation of your objects, but at least the object isn't the delegate of itself.

    public class SessionCreator:NSObject,NSURLSessionDelegate {
    
        //MARK: - Singleton method    
        class var sharedInstance :SessionCreator {
            struct Singleton {
                static let instance = SessionCreator()
            }
            return Singleton.instance
        }
    
        //MARK: - Public class method    
        public class func createSessionWithConfiguration (configuration:NSURLSessionConfiguration) -> NSURLSession {
            return sharedInstance.createSessionWithConfiguration(configuration)
        }
    
        //MARK: - Private methods
        private func createSessionWithConfiguration (configuration:NSURLSessionConfiguration) -> NSURLSession {
            return NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
        }
    
        //MARK: - NSURLSessionDelegate protocol conformance
        public func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
            // Always called since it's the delegate of all NSURLSession created using createSessionWithConfiguration
        }
    }
    
    // Let create a NSURLSession object :
    let session = SessionCreator.createSessionWithConfiguration(NSURLSessionConfiguration())