2017-02-06 40 views
10

Udało mi się uzyskać działającą implementację przykładu OAuth dostarczonego przez AlamoFire. Jednak staram się zrozumieć pewne linie kodu i jak to działa.Zrozumienie AlamoFire OAuth Przykład

Pełna Przykład:

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

Pytania:

[weak self] succeeded, accessToken, refreshToken in 
        guard let strongSelf = self else { return } 
  1. Jaki jest cel [weak self] i guard dla 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() 
         } 
        } 
    
  2. Jak działa ta próba ponowienia próby? requestsToRetry to tylko zestaw RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) Skąd wiadomo, jakie są prośby o ponowienie próby?

strongSelf.lock.lock()

  1. Nie NSLock tylko pozwala self (OAuth2Handler) w sposób przez inne nici podczas gdy sposób ten jest wykonywany?
+2

1 jest tak, że dostajesz silne odniesienie do 'self', więc poniższy kod nie będzie próbował użyć' self' jeśli jego zero, ponieważ 'self' w tym kontekście jest słabe, silne odwołanie zostanie zwolnione na końcu zakresu, aby nie nastąpiły żadne cykle zatrzymania. Nie zna się na alamofire, więc nie mogę powiedzieć na pewno, co reszta. – Fonix

Odpowiedz

7

1) Dokładnie jak komentował Fonix, masz silne odniesienie do self aby uniknąć że jeśli self była zerowa zacząć zbierać zachować cykli ..

jestem refeer do:

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

Ponieważ self zostanie przechwycony w bloku, który jest wywoływany asynchronicznie, self zostanie niejawnie zachowany i zwolniony ponownie po zakończeniu bloku, innymi słowy self zostanie przedłużony aż do zakończenia bloku. Dokonywanie tego kodu, można uniknąć przedłużyć czas życia self i zdecydować się nie robić wykonać blok jeśli self jest równa zero

2) Zgodnie z liniami wspomniałeś:

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

tam jest tablicą o nazwie requestsToRetry, która zawiera wszystkie żądania potrzebne do ponownego uruchomienia. W tym kodzie dołączyć do tablicy wszystkie żądania, które mają kod 401 statusu (gdy serwer zwraca kod stanu 401) Z kodu forEach ci pętlę przez tablicę requestToRetry:

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

i uruchomienie wszystkich elementów. Po zakończeniu cyklu usuwasz wszystkie przedmioty.

W rzeczywistości źródła podają:

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

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

można znaleźć więcej szczegółów here

3) Dokładnie jak pan powiedział, że często współbieżności problemy napotykane są jednym związane dostępu/modyfikacji zasobu udostępnionego z różnych wątków. lock.lock() to rozwiązanie polegające na blokowaniu innych bloków wykonawczych podczas modyfikowania elementów. Kod w defer jest wywoływany tuż przed opuszczeniem funkcji odblokowywania bloku.