Last active
March 7, 2022 15:25
-
-
Save shingohry/8dab1d0f72b5dfc9577735c4c4e79a62 to your computer and use it in GitHub Desktop.
Use Pocket Authentication API in iOS App
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 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