How to successfully handle sftp:// protocol by subclassing NSURLProtocol?

The built-in URL protocols supported by NSURLConnection can handle the schemes http, https, file, ftp, about, and data . I want to support sftp . I heard that there is a way to achieve this by subclassing NSURLProtocol . But I'm not getting how to do it. I want to download a image from the folder through sftp .

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

The tutorial says by subclassing we can support custom URL. But when i ran the code the connection always fails. I thought when we try connecting to sftp , delegate method in MyURLProtocol.swift ie didReceiveAuthenticationChallenge would get called but that doesn't happen. Instead delegate method didFailWithError gets called. I not getting why the connection is failing. Both these methods are from NSURLConnectionDelegate

I have a ViewController.swift

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    let urlString = "sftp://username@192.168.0.1:22/batman"
    // Open a connection for the URL.
    var url = NSURL(string: urlString)
    request = NSURLRequest(URL: url!)
    connection = NSURLConnection(request: request, delegate: self, startImmediately: true)//(request: request, delegate: self)

}

In My AppDelegate.swift

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Override point for customization after application launch.
    NSURLProtocol.registerClass(MyURLProtocol)
    return true
}

My MyURLProtocol.swift

import UIKit
import CoreData

var requestCount = 0

class MyURLProtocol: NSURLProtocol, NSURLConnectionDelegate {
var connection: NSURLConnection!
var mutableData: NSMutableData!
var response: NSURLResponse!

override class func canInitWithRequest(request: NSURLRequest) -> Bool {
    print("Request #(requestCount++): URL = (request.URL!.absoluteString)")
    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() {
    // 1
    let possibleCachedResponse = self.cachedResponseForCurrentRequest()
    if let cachedResponse = possibleCachedResponse {
        print("Serving response from cache")

        // 2
        let data = cachedResponse.valueForKey("data") as! NSData
        let mimeType = cachedResponse.valueForKey("mimeType") as! String
        let encoding = cachedResponse.valueForKey("encoding") as! String

        // 3
        let response = NSURLResponse(URL: self.request.URL!, MIMEType: mimeType, expectedContentLength: data.length, textEncodingName: encoding)

        // 4
        self.client!.URLProtocol(self, didReceiveResponse: response, cacheStoragePolicy: .NotAllowed)
        self.client!.URLProtocol(self, didLoadData: data)
        self.client!.URLProtocolDidFinishLoading(self)
    } else {
        // 5
        print("Serving response from NSURLConnection")

        let newRequest = self.request.mutableCopy() as! NSMutableURLRequest
        NSURLProtocol.setProperty(true, forKey: "MyURLProtocolHandledKey", inRequest: newRequest)
        self.connection = NSURLConnection(request: newRequest, delegate: self)
    }
}

override func stopLoading() {
    if self.connection != nil {
        self.connection.cancel()
    }
    self.connection = nil
}

func connection(connection: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
    self.client!.URLProtocol(self, didReceiveResponse: response, cacheStoragePolicy: .NotAllowed)

    self.response = response
    self.mutableData = NSMutableData()
}

func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
    self.client!.URLProtocol(self, didLoadData: data)
    self.mutableData.appendData(data)
}

func connectionDidFinishLoading(connection: NSURLConnection!) {
    self.client!.URLProtocolDidFinishLoading(self)
    self.saveCachedResponse()
}

func connection(connection: NSURLConnection, didFailWithError error: NSError) {
    self.client!.URLProtocol(self, didFailWithError: error)
}

func connection(connection: NSURLConnection, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge) {
}

func saveCachedResponse () {
    print("Saving cached response")

    // 1
    let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let context = delegate.managedObjectContext

    // 2
    let cachedResponse = NSEntityDescription.insertNewObjectForEntityForName("CachedURLResponse", inManagedObjectContext: context) as NSManagedObject

    cachedResponse.setValue(self.mutableData, forKey: "data")
    cachedResponse.setValue(self.request.URL!.absoluteString, forKey: "url")
    cachedResponse.setValue(NSDate(), forKey: "timestamp")
    cachedResponse.setValue(self.response.MIMEType, forKey: "mimeType")
    cachedResponse.setValue(self.response.textEncodingName, forKey: "encoding")

    // 3
    do {
        try context.save()
    } catch let error as NSError {
        print(error)
        print("Could not cache the response")
    }
}

func cachedResponseForCurrentRequest() -> NSManagedObject? {
    // 1
    let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let context = delegate.managedObjectContext

    // 2
    let fetchRequest = NSFetchRequest()
    let entity = NSEntityDescription.entityForName("CachedURLResponse", inManagedObjectContext: context)
    fetchRequest.entity = entity

    // 3
    let predicate = NSPredicate(format:"url == %@", self.request.URL!.absoluteString)
    fetchRequest.predicate = predicate

    // 4
    let possibleResult:Array<NSManagedObject>?
    do {
        possibleResult = try context.executeFetchRequest(fetchRequest) as? Array<NSManagedObject>
        if let result = possibleResult {
            if !result.isEmpty {
                return result[0]
            }
        }
    } catch let error as NSError {
        print(error)
    }
    return nil
}
}

Adding support for the URL scheme itself doesn't add support for the underlying network protocol. The sftp protocol is unrelated to HTTP, and requires entirely different networking code to make the connection and download data. Right now, your custom protocol class is basically just asking the URL loading system to make a new sftp request whenever your protocol gets an sftp URL (or any other URL). This will always fail because the URL loading system doesn't know how to handle sftp requests.

To add sftp support, you would need to bring in an actual sftp library, and then use that instead of creating a new NSURLConnection in your startLoading method. You also need to check the protocol in canInitWithRequest to make sure it really is an sftp request, IIRC. Otherwise, your custom protocol subclass will end up handling all requests for all possible URL schemes.

With that said, unless there's a really good reason to handle sftp using NSURLConnection or NSURLSession , you're probably better off just handling that by using one of those sftp libraries directly, rather than trying to plumb them into the URL loading system.

For info on sftp libraries, see this question:

SFTP libraries for iPhone?

链接地址: http://www.djcxy.com/p/34848.html

上一篇: Python中的相对路径

下一篇: 如何通过继承NSURLProtocol来成功处理sftp://协议?