Created
June 30, 2019 06:04
-
-
Save freshking/1c386d1bb5130bcc00449636e2be9243 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// OAuth2Token structure | |
struct OAuth2Token: Codable { | |
let date = Date() // date when the token was initialized | |
var accessToken: String // access token | |
var refreshToken: String? // refresh token (optional) | |
var expiresIn: Int // seconds until token expires | |
var tokenType: String // for example "Bearer" | |
// returns if token is still valid or has expired | |
var isValid: Bool { | |
let now = Date() | |
let seconds = TimeInterval(expiresIn) | |
return now.timeIntervalSince(date) < seconds | |
} | |
} | |
// Session handles all the requests and token | |
class Session { | |
// data task completion definition | |
typealias DataTaskCompletion = (Result<Any, Error>) -> Void | |
// OAuthRequest structure | |
struct OAuthRequest { | |
var url: URL | |
var completion: Session.DataTaskCompletion | |
} | |
private let oAuthRequestsQueue = DispatchQueue( label: "Session.oAuthRequestsQueue", attributes: .concurrent) | |
private var unsafeOAuthRequests = Array<OAuthRequest>() | |
private var token: OAuth2Token? | |
// begin a data request | |
func request(url: URL, completion: @escaping (Result<Any, Error>) -> Void) { | |
// construct the OAuthRequest in case we need it later | |
let oAuthRequest = OAuthRequest(url: url, completion: completion) | |
// check if a token is available | |
if let token = token { | |
// token found -> check if token is valid | |
if token.isValid == false { | |
// check if refresh token is available | |
if let token = token.refreshToken { | |
// safely add OAuthRequest to be sent later | |
safelyAddRequest(oAuthRequest: oAuthRequest) | |
// refresh current token | |
refreshToken(tokenString: token) | |
} else { | |
// safely add OAuthRequest to be sent later | |
safelyAddRequest(oAuthRequest: oAuthRequest) | |
// fetch new token | |
fetchToken() | |
} | |
} else { | |
// token is valid so now setup url request | |
var request = URLRequest(url: url) | |
request.setValue(token.tokenType + " " + token.accessToken, forHTTPHeaderField: "Authorization") | |
// and data task | |
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in | |
// handle the result of the request however you like | |
} | |
// begin task | |
task.resume() | |
} | |
} else { | |
// no token found -> save request to re-send after fetching token | |
// safely add OAuthRequest to be sent later | |
safelyAddRequest(oAuthRequest: oAuthRequest) | |
// fetch new OAuth2Token | |
fetchToken() | |
} | |
} | |
// fetches a new OAuth2Token | |
private func fetchToken() { | |
// this is where you actually fetch the OAuth2Token from your API provider | |
// after the fetch is complete, the new token must be assigned | |
token = <#OAuth2Token#> | |
// and then all the saved requests will be re-sent | |
safelySendAllRequests() | |
} | |
// refreshes the token using the current refresh token | |
private func refreshToken(tokenString: String) { | |
// this is where you refresh the token with your API provider | |
// after the refresh is complete, the refreshed token must be assigned | |
token = <#OAuth2Token#> | |
// and then all the saved requests will be re-sent | |
safelySendAllRequests() | |
} | |
// safely adds the OAuthRequests to the array | |
private func safelyAddRequest(oAuthRequest: OAuthRequest) { | |
oAuthRequestsQueue.async(flags: .barrier) { [weak self] in | |
self?.unsafeOAuthRequests.append(oAuthRequest) | |
} | |
} | |
// safely sends all saved OAuthRequests | |
private func safelySendAllRequests() { | |
oAuthRequestsQueue.async(flags: .barrier) { [weak self] in | |
self?.unsafeOAuthRequests.forEach { (oAuthRequest) in | |
self?.request(url: oAuthRequest.url, completion: oAuthRequest.completion) | |
} | |
self?.unsafeOAuthRequests.removeAll() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment