2016-03-17 23 views
8

Sto cercando di creare una classe che gestisca le mie chiamate di rete per il mio progetto. Per ora, intendo gestire gli errori a livello globale, tutto andava bene. Ho creato due funzioni; per le richieste di posta postRequest(:_) e per ottenere le richieste getRequests(:_). Le funzioni Datamaker consentono di restituire dati come URL, parametri, intestazioni ecc., Funzioni del datapander per analizzare i dati di risposta e infine una funzione per risolvere gli errori denominati errorHandler().Alamofire: come gestire 401 globalmente?

Quando chiamo quella di funzione di richiesta, fornisco un parametro per aiutare la funzione che deve essere richiesta. Nella funzione chiama i datamakers per ottenere i dati in primo luogo, quindi effettua una richiesta con Alamofire e alla fine se la richiesta ha esito positivo chiama dataparser e onSuccess(data:) oppure se non lo è chiama errorHandler(statusCode:) e onFailure(message:).

Ho inserito un blocco di interruttori in errorHandler e fornito il codice di stato per il suo parametro. In case 401 ho chiamato Token().refresh() e nel suo completamento chiamato completamento errorHanlder. Nel blocco di completamento ho chiamato di nuovo lo postRequest con gli stessi parametri. Non ha funzionato. Non so perché, è andato in loop infinito ogni volta e fatto richieste consecutivamente.

Quindi ho deciso di provare la classe AuthorizationManager (disponibile in questo collegamento; Alamofire : How to handle errors globally). L'ho modificato un po '(ho aggiunto un nuovo parametro come intestazioni e modificato il tipo NetworkSuccessHandler in NSData). Ecco la nuova forma:

public class AuthorizationManager: Manager { 
    public typealias NetworkSuccessHandler = (NSData?) -> Void 
    public typealias NetworkFailureHandler = (NSHTTPURLResponse?, AnyObject?, NSError) -> Void 

    private typealias CachedTask = (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void 

    private var cachedTasks = Array<CachedTask>() 
    private var isRefreshing = false 

    public func startRequest(
     method method: Alamofire.Method, 
     URLString: URLStringConvertible, 
     parameters: [String: AnyObject]?, 
     encoding: ParameterEncoding, 
     headers: [String:String]?, 
     success: NetworkSuccessHandler?, 
     failure: NetworkFailureHandler?) -> Request? 
     { 
      let cachedTask: CachedTask = { [weak self] URLResponse, data, error in 
       guard let strongSelf = self else { return } 

       if let error = error { 
        failure?(URLResponse, data, error) 
       } else { 
        strongSelf.startRequest(
         method: method, 
         URLString: URLString, 
         parameters: parameters, 
         encoding: encoding, 
         headers: headers, 
         success: success, 
         failure: failure 
        ) 
       } 
      } 

      if self.isRefreshing { 
       self.cachedTasks.append(cachedTask) 
       return nil 
      } 

      // Append your auth tokens here to your parameters 

      let request = self.request(method, URLString, parameters: parameters, encoding: encoding, headers: headers) 

      request.response { [weak self] request, response, data, error in 
       guard let strongSelf = self else { return } 

       if let response = response where response.statusCode == 401 { 
        strongSelf.cachedTasks.append(cachedTask) 
        strongSelf.refreshTokens() 
        return 
       } 

       if let error = error { 
        failure?(response, data, error) 
       } else { 
        success?(data) 
       } 
      } 

      return request 
    } 

    func refreshTokens() { 
     self.isRefreshing = true 

      // Make the refresh call and run the following in the success closure to restart the cached tasks 
     Token().refresh {() ->() in 
      let cachedTaskCopy = self.cachedTasks 
      self.cachedTasks.removeAll() 
      cachedTaskCopy.map { $0(nil, nil, nil) } 
      self.isRefreshing = false 
     } 
    } 
} 

Chiamato nel mio postRequest come:

func postRequest(requestType: postRequestType, additionalParameters: [String]?, onSuccess: onSuccessRequest = {_ in }, onFailure: onFailureRequest = {_ in }){ 
    print("post") 
    let requestData = returnStaticDataForPostRequest(requestType, additionalParameters: additionalParameters) 
    let Manager = AuthorizationManager() 
    Manager.startRequest(method: .POST, URLString: requestData.0, parameters: requestData.2, encoding: requestData.3, headers: requestData.1, success: { (data) -> Void in 
     print("Manager") 
     let json = JSON(data: data!) 
     print(json) 
     dataParserForPostRequests(json, parseForWhat: requestType) 
     onSuccess(json: json) 
     }) { (response, message, error) -> Void in 
      print(error) 
    } 

} 

e l'uso del postRequests nel ViewController:

postRequest(.LOGIN, additionalParameters: ["asdasd", "asdasd"], onSuccess: { (json) ->() in 
      print(">>>login_try_succeeded")   
      self.performSegueWithIdentifier("LoginToMain", sender: self) 
      }) { (errorCode) ->() in 
      print(">>>login_try_failed(\(errorCode))") 
     } 

Questo è lo stato corrente. Quando eseguo il codice e provo ad accedere a AuthorizationManager non funziona. Stampa semplicemente;

post 

E, infine, non so se è rilevante, ma non v'è giallo di avvertimento in questa linea:

cachedTaskCopy.map { $0(nil, nil, nil) } 

dice "Result of call to 'map' is unused"

Per riassumere ho bisogno di capire come mi in grado di gestire 401 e so come utilizzare AuthorizationManager in un modo diverso da questo.

EDIT:

ho provato la corsa del questo codice direttamente dal ViewController ma non funziona affatto. È come se il codice fosse invisibile.

AuthorizationManager().startRequest(method: .POST, URLString: NSURL(string: "http://server.url/token")!, parameters: ["":""], encoding: .JSON,headers: ["":""], success: { (data) -> Void in 
      print(data) 
      }) { (response, data, error) -> Void in 
       print(error) 
       print("asdasd") 
     } 

risposta

2

È possibile che il tuo AuthorizationManager non stia persistendo dopo il primo tentativo di inviare la richiesta.

Normalmente è buona norma evitare il pattern Singleton, ma questo non è un caso un male per esso:

public class AuthorizationManager: Manager { 
    static let shared = AuthorizationManager() 
    // ...the rest of your class 
} 

E quando si chiama la vostra richiesta, utilizzare questa istanza Singleton invece di istanziare un nuovo AuthorizationManager come

AuthorizationManager.shared.startRequest(method: .POST, ...etc... 

Sto supponendo che questo potrebbe essere il problema, dal momento che quando si crea il proprio AuthorizationManager in entrambi i casi non c'è nulla che mantenga attivamente quell'oggetto. Il gestore può essere creato, eseguire la richiesta e quindi essere deallocato prima del cachedTask o anche prima della gestione del completamento, nel qual caso il tuo guard let strongSelf = self else { return } restituirebbe semplicemente senza eseguire nessuno dei tuoi completamenti o dei task in cache.

Speriamo che questo aiuti. Se questo è il problema, allora quella soluzione singleton dovrebbe essere molto semplice.

+0

Questa non era la mia domanda, ma la tua risposta funzionerà effettivamente per l'ultima parte che ha spiegato come "EDIT" (o non ricordo quale sia la mia domanda in realtà è :)). Ad ogni modo, poiché è passato molto tempo da quando l'ho chiesto, ho iniziato a utilizzare Moya (https://github.com/Moya/Moya) che fornisce funzionalità davvero interessanti per sviluppare l'utilizzo pulito di Alamofire. Si discute su come gestire il 401 e gli altri codici di stato, in questo collegamento; https://github.com/Moya/Moya/issues/379. Comunque sembra che la tua risposta stia aumentando, presumo che sia utile per le persone. Quindi accetterò questo. – Faruk

+0

Beh, spero che aiuti qualcuno! Inoltre, Moya è fantastica, felice che tu sia riuscito a trovare una soluzione. – tbogosia

+0

Sì! Grazie per il vostro interesse ad aiutare :) – Faruk