了解 AlamoFire OAuth 示例
Posted
技术标签:
【中文标题】了解 AlamoFire OAuth 示例【英文标题】:Understanding AlamoFire OAuth Example 【发布时间】:2017-06-24 01:37:06 【问题描述】:我能够获得 AlamoFire 提供的 OAuth 示例的有效实现。不过,我希望了解某些代码行及其工作原理。
完整示例:
class OAuth2Handler: RequestAdapter, RequestRetrier
private typealias RefreshCompletion = (_ succeeded: Bool, _ accessToken: String?, _ refreshToken: String?) -> Void
private let sessionManager: SessionManager =
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
return SessionManager(configuration: configuration)
()
private let lock = NSLock()
private var clientID: String
private var baseURLString: String
private var accessToken: String
private var refreshToken: String
private var isRefreshing = false
private var requestsToRetry: [RequestRetryCompletion] = []
// MARK: - Initialization
public init(clientID: String, baseURLString: String, accessToken: String, refreshToken: String)
self.clientID = clientID
self.baseURLString = baseURLString
self.accessToken = accessToken
self.refreshToken = refreshToken
// MARK: - RequestAdapter
func adapt(_ urlRequest: URLRequest) throws -> URLRequest
if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(baseURLString)
var urlRequest = urlRequest
urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")
return urlRequest
return urlRequest
// MARK: - RequestRetrier
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
lock.lock() ; defer lock.unlock()
if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401
requestsToRetry.append(completion)
if !isRefreshing
refreshTokens [weak self] succeeded, accessToken, refreshToken in
guard let strongSelf = self else return
strongSelf.lock.lock() ; defer strongSelf.lock.unlock()
if let accessToken = accessToken, let refreshToken = refreshToken
strongSelf.accessToken = accessToken
strongSelf.refreshToken = refreshToken
strongSelf.requestsToRetry.forEach $0(succeeded, 0.0)
strongSelf.requestsToRetry.removeAll()
else
completion(false, 0.0)
// MARK: - Private - Refresh Tokens
private func refreshTokens(completion: @escaping RefreshCompletion)
guard !isRefreshing else return
isRefreshing = true
let urlString = "\(baseURLString)/oauth2/token"
let parameters: [String: Any] = [
"access_token": accessToken,
"refresh_token": refreshToken,
"client_id": clientID,
"grant_type": "refresh_token"
]
sessionManager.request(urlString, method: .post, parameters: parameters, encoding: JSONEncoding.default)
.responseJSON [weak self] response in
guard let strongSelf = self else return
if
let json = response.result.value as? [String: Any],
let accessToken = json["access_token"] as? String,
let refreshToken = json["refresh_token"] as? String
completion(true, accessToken, refreshToken)
else
completion(false, nil, nil)
strongSelf.isRefreshing = false
问题:
[weak self] succeeded, accessToken, refreshToken in
guard let strongSelf = self else return
[weak self]
和 guard
for strongSelf
的用途是什么?
requestsToRetry.append(completion)
if !isRefreshing
refreshTokens [weak self] succeeded, accessToken, refreshToken in
guard let strongSelf = self else return
//Implementation
strongSelf.requestsToRetry.forEach $0(succeeded, 0.0)
strongSelf.requestsToRetry.removeAll()
此请求重试如何工作? requestsToRetry
只是 RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval)
的数组,它如何知道要重试的请求?
strongSelf.lock.lock()
NSLock
是否不允许在此方法执行时任何其他线程访问 self
(OAuth2Handler)?
【问题讨论】:
1 是为了让您获得对self
的强引用,这样如果self
为零,则以下代码不会尝试使用它,因为self
在该上下文中很弱,强引用将在范围结束时释放,因此不会发生保留周期。不熟悉 alamofire,所以不能确定其余的是什么。
【参考方案1】:
1) 正如 Fonix 所评论的那样,您强烈引用 self
以避免如果 self
为 nil 您开始收集保留周期..
我指的是:
[weak self] ... in
guard let strongSelf = self else return
由于self
将被捕获在异步分派的块中,self
将被隐式保留并在块完成后再次释放,换句话说,self
将一直延伸到块完成后.编写此代码,您可以避免延长 self
的生命周期,并在 self
等于 nil 时决定不执行该块
2)根据您提到的行:
if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401
requestsToRetry.append(completion)
..
有一个名为requestsToRetry
的数组包含您需要重新启动的所有请求。在此代码中,您将所有具有 401
状态代码的请求附加到数组(当服务器返回状态代码 401 时)
使用代码forEach
循环遍历requestToRetry
数组:
strongSelf.requestsToRetry.forEach $0(succeeded, 0.0)
strongSelf.requestsToRetry.removeAll()
并启动所有项目。循环结束后,您删除所有项目。
事实上,消息来源报道:
public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void
public protocol RequestRetrier
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion)
您可以找到更多详情here
3)正如您所说,经常面临的并发问题是与从不同线程访问/修改共享资源有关的问题。
lock.lock()
是在修改项目时锁定其他执行块的解决方案。 defer
中的代码在离开函数解锁块之前被调用。
【讨论】:
以上是关于了解 AlamoFire OAuth 示例的主要内容,如果未能解决你的问题,请参考以下文章
Xcode:Alamofire 源代码中的 Swift Dropbox 错误
如何在 Alamofire 4.0 中添加带有上传进度百分比的标签的进度条