2016-03-30 22 views
6

Sto cercando di implementare questo tutorial che implementa un NSURLProtocol personalizzato con NSURLConnection.Custom NSURLProtocol con NSURLSession

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

funziona come previsto, ma ora che NSURLConnection è deprecato in iOS9, sto cercando di convertirlo in NSURLSession.

Sfortunatamente non ha funzionato.

Sto caricando un sito Web in uiwebview, se utilizzo NSURLConnection viene caricato e tutto funziona come previsto, tutte le richieste http dalla webview vengono acquisite, ma non quando si utilizza NSURLSession.

Qualsiasi aiuto è apprezzato.

Ecco il mio codice

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") 
    } 


    } 
+0

Come fai a sapere il codice non funziona? Che cosa hai fatto per rintracciare il problema da solo? Puoi tornare indietro nell'esempio di codice e rimuovere tutte le sezioni commentate? Forse aggiungi alcuni commenti su ciò che stai cercando di ottenere in ogni routine. –

+0

Ehi, sono nuovo in swift e affronta il problema con il caching della webview. Sto provando con la stessa fonte, la pagina web si carica correttamente e salva nella cache. Ma quando il dispositivo è offline, non riesco a ottenerlo dai dati della cache. Potrebbe essere necessario aggiornare il codice con l'URL. Potete per favore aiutarmi con questa fonte per favore :(https://drive.google.com/file/d/0B-5GPXUpPZh-Q2FOWEJudXRaQkE/view?usp=sharing –

risposta

6

L'ho risolto.

Ecco il codice se qualcuno ne ha bisogno.

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") 
} 


} 
+0

Grazie amico. aiutato molto Di seguito è disponibile una versione 3 rapida della stessa classe nel caso in cui qualcuno ne avesse bisogno. – deepax11

+0

Non è davvero necessario utilizzare un NSURLSession separato per ogni richiesta. Questo è grossolanamente inefficiente. Si dovrebbe prendere in considerazione l'utilizzo di una singola istanza condivisa (utilizzando una variabile globale inizializzata in un blocco dispatch_once). – dgatwood

5

Il problema riscontrato con il codice è che si sta utilizzando il la NSURLSession.sharedSession per contenere il vostro compito di dati. Utilizzando la sessione condivisa, non è possibile modificare il delegato della sessione in modo che nessuna delle routine delegate venga invocata.

Sarà necessario creare una sessione personalizzata con il protocollo stabilito come delegato per la sessione. Quindi, quando viene chiesto di iniziare il caricamento, è possibile creare un'attività dati in quella sessione.

+0

ciao, grazie per la risposta, il codice commentato è per NSURLConnection dato che è deprecato in iOS9 ho bisogno di convertirlo in NSURLSession, quello che sto facendo è che voglio monitorare tutte le richieste http da uiwebview e salvare la risposta JSON da alcune richieste di URL. Il codice ha funzionato bene usando NSURLConnection, ma ora Con NSURLConnection posso vedere le richieste e la webview carica il sito Web e qualsiasi collegamento ha fatto clic, ma con la modifica a NSURLSession la webview non carica il sito Web. – kupilot

+0

Sì, ho capito. Come ho detto nella mia risposta, quando stavo usando NSURLConnection, hai impostato il delegato per la connessione e i tuoi metodi delegati hanno raccolto i dati. Nel tuo nuovo codice basato su NSSession devi creare una sessione personalizzata invece di usare NSSession.sharedSession in modo che tu possa impostare il delegato su quella sessione in modo che sia possibile richiamare i callback delle attività di sessione e dati. –

+0

grazie per il tuo aiuto, l'ho risolto, vedi la mia risposta. – kupilot

6

Swift 3 Versione:

// 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) 
     } 
    } 
} 
+0

Funziona ma mostra il sito web completo. Non mostrare la vista del sito mobile. – vp2698