ios - example - download alamofire swift 3
Alamofire: cómo manejar los errores globalmente (1)
Manejar la actualización de 401 respuestas en un flujo de oauth es bastante complicado dada la naturaleza paralela de NSURLSessions. Pasé bastante tiempo construyendo una solución interna que funcionó extremadamente bien para nosotros. La siguiente es una extracción de muy alto nivel de la idea general de cómo se implementó.
import Foundation
import Alamofire
public class AuthorizationManager: Manager {
public typealias NetworkSuccessHandler = (AnyObject?) -> 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,
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,
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)
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
let cachedTaskCopy = self.cachedTasks
self.cachedTasks.removeAll()
cachedTaskCopy.map { $0(nil, nil, nil) }
self.isRefreshing = false
}
}
Lo más importante aquí para recordar es que no desea ejecutar una llamada de actualización por cada 401 que se devuelve. Una gran cantidad de solicitudes pueden competir al mismo tiempo. Por lo tanto, desea actuar en el primer 401 y poner en cola todas las solicitudes adicionales hasta que el 401 haya tenido éxito. La solución que describí anteriormente hace exactamente eso. Cualquier tarea de datos que se inicie a través del método startRequest
se actualizará automáticamente si golpea un 401.
Algunas otras cosas importantes a tener en cuenta aquí que no se tienen en cuenta en este ejemplo muy simplificado son:
- Seguridad de subprocesos
- Llamadas garantizadas de éxito o cierre de fallas
- Almacenamiento y recuperación de los tokens oauth
- Analizando la respuesta
- Casting la respuesta analizada al tipo apropiado (genéricos)
Espero que esto ayude a arrojar algo de luz.
Actualizar
Ahora hemos lanzado 🔥🔥 Alamofire 4.0 🔥🔥 que agrega los protocolos RequestAdapter
y RequestRetrier
que le permiten crear fácilmente su propio sistema de autenticación independientemente de los detalles de la implementación de la autorización. Para obtener más información, consulte nuestro README que tiene un ejemplo completo de cómo puede implementar en el sistema OAuth2 en su aplicación.
Divulgación completa: el ejemplo en el archivo README solo se debe usar como ejemplo. Por favor, por favor, NO vaya y copie y pegue el código en una aplicación de producción.
Mi pregunta es bastante similar a esta, pero para Alamofire: AFNetworking: maneja el error globalmente y repite la solicitud
¿Cómo se puede detectar globalmente un error (típicamente un 401) y manejarlo antes de que se realicen otras solicitudes (y eventualmente fallar si no se gestiona)?
Estaba pensando en encadenar un controlador de respuesta personalizado, pero es una tontería hacerlo en cada solicitud de la aplicación.
Tal vez subclassing, pero ¿qué clase debería subclase para manejar eso?