2017-02-06 22 views
10

Tôi đã có thể thực hiện triển khai ví dụ OAuth đang hoạt động như được cung cấp bởi AlamoFire. Tuy nhiên, tôi đang tìm hiểu một số dòng mã và cách thức hoạt động của nó.Hiểu ví dụ về AlamoFire OAuth

Full Ví dụ:

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

Câu hỏi:

[weak self] succeeded, accessToken, refreshToken in 
        guard let strongSelf = self else { return } 
  1. mục đích [weak self]guard cho strongSelf là gì?

    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() 
         } 
        } 
    
  2. Yêu cầu này thử lại hoạt động như thế nào? Các requestsToRetry chỉ là một mảng của RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) Làm thế nào để nó biết những gì yêu cầu thử lại?

strongSelf.lock.lock()

  1. Liệu NSLock chỉ không cho phép self (OAuth2Handler) để được truy cập bởi bất kỳ thread khác trong khi phương pháp này được thực hiện?
+2

1 để bạn có được tham chiếu mạnh mẽ đến 'tự' để mã sau không sử dụng' self' nếu nil của nó vì 'self' trong ngữ cảnh đó yếu, tham chiếu mạnh sẽ được phát hành ở cuối của phạm vi như vậy không có chu kỳ giữ lại sẽ xảy ra. Không quen thuộc với alamofire vì vậy không thể nói cho một số phần còn lại là gì. – Fonix

Trả lời

7

1) Đúng như nhận xét của Fonix, bạn có một tài liệu tham khảo mạnh để self để tránh điều đó nếu self là nil bạn bắt đầu thu thập giữ lại chu kỳ ..

Tôi refeer tới:

[weak self] ... in 
guard let strongSelf = self else { return } 

Kể từ khi self sẽ được giữ lại trong khối được gửi đi không đồng bộ, self sẽ được giữ lại hoàn toàn khi khối đã được hoàn thành, nói cách khác là self sẽ được kéo dài cho đến khi khối kết thúc. Làm mã này, bạn tránh để kéo dài tuổi thọ thời gian của self và quyết định không thực hiện khối nếu self bằng nil

2) Theo dòng bạn đề cập:

if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 { 
      requestsToRetry.append(completion) 
      .. 

có là một mảng có tên là requestsToRetry, chứa tất cả yêu cầu bạn cần khởi chạy lại. Trong đoạn mã này bạn thêm vào mảng tất cả các yêu cầu mà có mã 401 trạng thái (khi máy chủ trả về mã trạng thái 401) Với mã forEach bạn lặp qua mảng requestToRetry:

strongSelf.requestsToRetry.forEach { $0(succeeded, 0.0) } 
strongSelf.requestsToRetry.removeAll() 

và khởi động tất cả các mục. Sau khi chu trình được kết thúc, bạn xóa tất cả các mục.

Trong thực tế, các nguồn báo cáo:

public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void 

public protocol RequestRetrier { 
    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) 
} 

Bạn có thể tìm thêm chi tiết here

3) Đúng như bạn nói những vấn đề thường xuyên đồng thời phải đối mặt là người liên quan đến việc truy cập/sửa đổi các tài nguyên chia sẻ từ các chủ đề khác nhau. lock.lock() là giải pháp để khóa các khối thực hiện khác khi các mục đang được sửa đổi. Mã trong defer được gọi ngay trước khi rời khỏi chức năng để mở khóa khối.