Skip to content

Instantly share code, notes, and snippets.

@shingohry
Last active March 7, 2022 15:25
Show Gist options
  • Save shingohry/8dab1d0f72b5dfc9577735c4c4e79a62 to your computer and use it in GitHub Desktop.
Save shingohry/8dab1d0f72b5dfc9577735c4c4e79a62 to your computer and use it in GitHub Desktop.
Use Pocket Authentication API in iOS App
import UIKit
import AuthenticationServices
import Combine
class ViewController: UIViewController {
// you must add a URL scheme to your Info.plist
let callbackURLScheme = "pocketsample"
let callbackURL = "pocketsample:authorizationFinished"
// ref: https://getpocket.com/developer/docs/authentication
// Step 1: Obtain a platform consumer key
// you can register for one at http://getpocket.com/developer/apps/new
let consumerKey = "xxxxxxxxxx"
var cancellable = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
obtainRequestToken()
}
}
extension ViewController {
// Step 2: Obtain a request token
func obtainRequestToken() {
print(#function)
let body: [String: Any] = ["redirect_uri": callbackURL,
"consumer_key": consumerKey]
guard let request = request(url: "https://getpocket.com/v3/oauth/request", body: body) else { return }
send(request: request) { [weak self] result in
switch result {
case .success(let data):
if let response = try? JSONDecoder().decode(ObtainRequestTokenResponse.self, from: data) {
print(#function, "response: \(response)")
DispatchQueue.main.async {
self?.authorization(response: response)
}
}
default:
break
}
}
}
// Step 3: Redirect user to Pocket to continue authorization
// Step 4: Receive the callback from Pocket
func authorization(response: ObtainRequestTokenResponse) {
print(#function)
guard let authURL = URL(string: "https://getpocket.com/auth/authorize?request_token=\(response.code)&redirect_uri=\(callbackURL)") else { return }
// Initialize the session.
let session = ASWebAuthenticationSession(url: authURL, callbackURLScheme: nil) { [weak self] callbackURL, error in
if let url = callbackURL, url.absoluteString == self?.callbackURL {
self?.getAccessToken(code: response.code)
} else if let error = error {
print(#function, "error:", error)
}
}
session.presentationContextProvider = self
session.start()
}
// Step 5: Convert a request token into a Pocket access token
func getAccessToken(code: String) {
print(#function)
let body: [String: Any] = ["code": code,
"consumer_key": consumerKey]
guard let request = request(url: "https://getpocket.com/v3/oauth/authorize", body: body) else { return }
send(request: request) { result in
switch result {
case .success(let data):
let decorder = JSONDecoder()
decorder.keyDecodingStrategy = .convertFromSnakeCase
if let response = try? decorder.decode(GetAccessTokenResponse.self, from: data) {
print(#function, "response: \(response)")
}
default:
break
}
}
}
}
extension ViewController {
func request(url: String, body: [String: Any]) -> URLRequest? {
guard let URL = URL(string: url) else { return nil }
var request = URLRequest(url: URL)
request.httpMethod = "POST"
// Headers
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("UTF-8", forHTTPHeaderField: "charset")
request.addValue("application/json", forHTTPHeaderField: "X-Accept")
// JSON Body
guard let body = try? JSONSerialization.data(withJSONObject: body, options: []) else { return nil }
request.httpBody = body
return request
}
func send(request: URLRequest, completion: @escaping (Result<Data, Error>) -> Void) {
URLSession.shared.dataTaskPublisher(for: request)
.sink { _completion in
switch _completion {
case .failure(let error):
print(#function, "dataTask failure. error:", error)
completion(.failure(error))
default:
break
}
} receiveValue: { data, _ in
completion(.success(data))
}
.store(in: &cancellable)
}
}
extension ViewController: ASWebAuthenticationPresentationContextProviding {
func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
return view.window!
}
}
struct ObtainRequestTokenResponse: Codable {
let code: String
}
struct GetAccessTokenResponse: Codable {
let accessToken: String
let username: String
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment