Skip to content

Instantly share code, notes, and snippets.

@saroar
Last active February 17, 2024 02:19
Show Gist options
  • Save saroar/ca78de9dc798cdbaaa47791380062596 to your computer and use it in GitHub Desktop.
Save saroar/ca78de9dc798cdbaaa47791380062596 to your computer and use it in GitHub Desktop.
RefreshToken URLSession + Combine swift
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, macCatalyst 13.0, *)
extension JSONDecoder {
public static let ISO8601JSONDecoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .iso8601
return decoder
}()
}
var rToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVmNDUyY2RjZDUyMTE1ZDE0Yzk2NDUwYiIsImlhdCI6MTU5ODk4NTQyMCwiZXhwIjoxNjMwNTIxNDIwfQ.of5jT0LlqrlVoPZ7N6zXXWjvgsmZtkQKfhj0sWKB3rinhxFe1QeY-wueaWBrbxHYdI9cI7Kmj6dSPfK8b9Oc4yOGIOzQ4yONHHAShXKp6HszArjVe8wdRcZN02rxilHDCJoqXAMnjQXi7tMsMDtuX2e7iHsuNgSDNU9WMAtMJ6iMHj95IE-W5jOPyNXodQmqfSP5XJcyRI1LPq-nuMWa86L60BVhH2oySUNzFYLYGG3mrF2wAf6pC9XtWUMgQJ1zWptS08_O3DKzWwbDTzjbdnnx5aBH5SwOBxvgci-Ngl4Ix7EyTfHX1Akuxy8gwBu5qTP3erPeyH6uK0vrbkyJeQ"
var invalidToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVmNDUyY2RjZDUyMTE1ZDE0Yzk2NDUwYiIsImlhdCI6MTU5OTAyOTczNCwiZXhwIjoxNjMwNTY1NzM0fQ.T1Emc8J3QUh49nUO9GLwiWkGIKA9EoQBej0P6_-uNo0BenkcLpJWOq_DSKAexhT06S4_CqlGiJ1kn8q7gDJOZR4tX7xDXfObQeZisbbsgo_UIWlaSZTu3l3Ey_93vlt8c0W4-pOj99-voSwQ_Q4RvRi6r3r3P1aGb5JZ48vCZ_ulT13SGSV1xQL08VuV87KwsosoXLa56hJTBqpKyohkbvTr6Nb0rLwS48FEn-T2mKkyZmARvQlpEO3j2IGroskNYelMt2qU80h7k6GyzTOJh1mB1ZTBHXQSaE5z3VHNpFN5M9sRvegkkVucU9zrfQ85OM4Rs4vx9RJMmDfAIbSuyw"
var aToken: String = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0dXMiOjAsImV4cCI6MTU5OTAzMzEyOCwiaWF0IjoxNTk5MDMzMDY4LCJ1c2VySWQiOiI1ZjQ1MmNkY2Q1MjExNWQxNGM5NjQ1MGIiLCJwaG9uZU51bWJlciI6Iis3OTIxODgyMTIxNyJ9.UUZ5TUPGPRreS3AKko7NZ_gcoJYvKuJCipUHJPxS1ormw1yQXolQzrCCf34EK58peZm5WngLwo3nAuPplL5rmInvjcYQotI0N0grpKkMf_ITPRMv80iqObpBr1r2zsvJVqwMysmRM4wP-mipvvwvlb0lkKXPoqn2M5Eckkk97hsrQ5pAFsaMJQytpm6YW-IC3NXinYAeTlWQbm_7_9naxSobLQyVJ2VXc4lArddzDLpEMoZEkC28fXLoQKL36W93MBcoNFxjdHCYyuxBFHQrtu7drCYJ1EyGOA-lCACf5twslGoZKWJjvqa8IjWcaJVfGveMVakaBfR90Do6f-f6ZA"
var headers = [
"authorization": "Bearer \(aToken)",
"Content-Type": "application/json"
]
public struct RefreshTokenResponse: Codable {
public var access_token: String
public var refresh_token: String
// enum CodingKeys: String, CodingKey {
// case accessToken = "access_token"
// case refreshToken = "refresh_token"
// }
}
public struct RefreshTokenInput: Codable {
public var refresh_token: String
// enum CodingKeys: String, CodingKey {
// case refreshToken = "refresh_token"
// }
}
class Authenticator {
private var currentToken = RefreshTokenResponse(
access_token: aToken,
refresh_token: rToken
)
private var cancellationToken: AnyCancellable?
func refreshToken<S: Subject>(using subject: S) where S.Output == RefreshTokenResponse {
//self.currentToken = Token(isValid: true)
let parameters: [String: Any] = [
"refresh_token": rToken,
]
let jsonDecoder: JSONDecoder = .ISO8601JSONDecoder
let url: URL = URL(string: "http://10.0.1.3:8080/v1/auth/refreshToken")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
URLSession.shared.dataTaskPublisher(for: request)
.retry(3)
.sink { com in
print(com)
subject.send(self.currentToken)
} receiveValue: { data in
print(#line, data)
do {
let jsonDecoder2 = JSONDecoder()
let rtResponse = try jsonDecoder2.decode(RefreshTokenResponse.self, from: data.data)
aToken = rtResponse.access_token
rToken = rtResponse.refresh_token
headers = [
"authorization": "Bearer \(rtResponse.access_token)",
"Content-Type": "application/json"
]
} catch {
print(#line, error)
}
}.store(in: &cancellables)
}
func tokenSubject() -> CurrentValueSubject<RefreshTokenResponse, Never> {
print(#line, currentToken)
return CurrentValueSubject(currentToken)
}
}
struct UserApi {
let authenticator: Authenticator
@available(iOS 14.0, *)
func getProfile() -> AnyPublisher<Data, URLError> {
let tokenSubject = authenticator.tokenSubject()
return tokenSubject
.flatMap({ token -> AnyPublisher<Data, URLError> in
let url: URL = URL(string: "http://10.0.1.3:8080/v1/events")!
var request = URLRequest(url: url)
request.allHTTPHeaderFields = headers
print(#line, headers)
return URLSession.shared.dataTaskPublisher(for: request)
.flatMap({ result -> AnyPublisher<Data, URLError> in
if let httpResponse = result.response as? HTTPURLResponse,
httpResponse.statusCode == 401 {
self.authenticator.refreshToken(using: tokenSubject)
print(#line, tokenSubject)
return Empty().eraseToAnyPublisher()
}
return Just(result.data)
.setFailureType(to: URLError.self)
.eraseToAnyPublisher()
})
.eraseToAnyPublisher()
})
.handleEvents(receiveOutput: { _ in
tokenSubject.send(completion: .finished)
})
.eraseToAnyPublisher()
}
}
let authenticator = Authenticator()
let get = UserApi(authenticator: authenticator).getProfile()
var cancellationToken: AnyCancellable?
cancellationToken = get
.retry(3)
.sink { com in
print(com)
} receiveValue: { data in
print(#line, data)
}
233 RefreshTokenResponse(access_token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVmNDUyY2RjZDUyMTE1ZDE0Yzk2NDUwYiIsImlhdCI6MTU5OTAyOTczNCwiZXhwIjoxNjMwNTY1NzM0fQ.T1Emc8J3QUh49nUO9GLwiWkGIKA9EoQBej0P6_-uNo0BenkcLpJWOq_DSKAexhT06S4_CqlGiJ1kn8q7gDJOZR4tX7xDXfObQeZisbbsgo_UIWlaSZTu3l3Ey_93vlt8c0W4-pOj99-voSwQ_Q4RvRi6r3r3P1aGb5JZ48vCZ_ulT13SGSV1xQL08VuV87KwsosoXLa56hJTBqpKyohkbvTr6Nb0rLwS48FEn-T2mKkyZmARvQlpEO3j2IGroskNYelMt2qU80h7k6GyzTOJh1mB1ZTBHXQSaE5z3VHNpFN5M9sRvegkkVucU9zrfQ85OM4Rs4vx9RJMmDfAIbSuyw", refresh_token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVmNDUyY2RjZDUyMTE1ZDE0Yzk2NDUwYiIsImlhdCI6MTU5ODk4NTQyMCwiZXhwIjoxNjMwNTIxNDIwfQ.of5jT0LlqrlVoPZ7N6zXXWjvgsmZtkQKfhj0sWKB3rinhxFe1QeY-wueaWBrbxHYdI9cI7Kmj6dSPfK8b9Oc4yOGIOzQ4yONHHAShXKp6HszArjVe8wdRcZN02rxilHDCJoqXAMnjQXi7tMsMDtuX2e7iHsuNgSDNU9WMAtMJ6iMHj95IE-W5jOPyNXodQmqfSP5XJcyRI1LPq-nuMWa86L60BVhH2oySUNzFYLYGG3mrF2wAf6pC9XtWUMgQJ1zWptS08_O3DKzWwbDTzjbdnnx5aBH5SwOBxvgci-Ngl4Ix7EyTfHX1Akuxy8gwBu5qTP3erPeyH6uK0vrbkyJeQ")
251 ["authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVmNDUyY2RjZDUyMTE1ZDE0Yzk2NDUwYiIsImlhdCI6MTU5OTAyOTczNCwiZXhwIjoxNjMwNTY1NzM0fQ.T1Emc8J3QUh49nUO9GLwiWkGIKA9EoQBej0P6_-uNo0BenkcLpJWOq_DSKAexhT06S4_CqlGiJ1kn8q7gDJOZR4tX7xDXfObQeZisbbsgo_UIWlaSZTu3l3Ey_93vlt8c0W4-pOj99-voSwQ_Q4RvRi6r3r3P1aGb5JZ48vCZ_ulT13SGSV1xQL08VuV87KwsosoXLa56hJTBqpKyohkbvTr6Nb0rLwS48FEn-T2mKkyZmARvQlpEO3j2IGroskNYelMt2qU80h7k6GyzTOJh1mB1ZTBHXQSaE5z3VHNpFN5M9sRvegkkVucU9zrfQ85OM4Rs4vx9RJMmDfAIbSuyw", "Content-Type": "application/json"]
259 Combine.CurrentValueSubject<__lldb_expr_28.RefreshTokenResponse, Swift.Never>
212 (data: 1036 bytes, response: <NSHTTPURLResponse: 0x60000219ea60> { URL: http://10.0.1.3:8080/v1/auth/refreshToken } { Status Code: 200, Headers {
Connection = (
"keep-alive",
"keep-alive"
);
"Content-Length" = (
1036
);
"Content-Type" = (
"application/json; charset=utf-8"
);
Date = (
"Wed, 02 Sep 2020 07:51:08 GMT",
"Wed, 02 Sep 2020 07:51:08 GMT"
);
} })
finished
251 ["Content-Type": "application/json", "authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0dXMiOjAsImV4cCI6MTU5OTAzMzEyOCwiaWF0IjoxNTk5MDMzMDY4LCJ1c2VySWQiOiI1ZjQ1MmNkY2Q1MjExNWQxNGM5NjQ1MGIiLCJwaG9uZU51bWJlciI6Iis3OTIxODgyMTIxNyJ9.UUZ5TUPGPRreS3AKko7NZ_gcoJYvKuJCipUHJPxS1ormw1yQXolQzrCCf34EK58peZm5WngLwo3nAuPplL5rmInvjcYQotI0N0grpKkMf_ITPRMv80iqObpBr1r2zsvJVqwMysmRM4wP-mipvvwvlb0lkKXPoqn2M5Eckkk97hsrQ5pAFsaMJQytpm6YW-IC3NXinYAeTlWQbm_7_9naxSobLQyVJ2VXc4lArddzDLpEMoZEkC28fXLoQKL36W93MBcoNFxjdHCYyuxBFHQrtu7drCYJ1EyGOA-lCACf5twslGoZKWJjvqa8IjWcaJVfGveMVakaBfR90Do6f-f6ZA"]
286 1940 bytes
finished
@rahulGraphy
Copy link

Hi Saroar,

Did you get a chance to look at it?

@saroar
Copy link
Author

saroar commented Apr 16, 2021

sorry don't have time now but when will get time i will update with you real-world app where i will use it thanks

@rahulGraphy
Copy link

sure, hope to see it soon.
Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment