Add basic get support for a hardcoded URL

This commit is contained in:
Maurice Parker 2020-05-04 01:16:27 -05:00
parent 78f72264fd
commit 10b24d92c5
3 changed files with 163 additions and 23 deletions

View File

@ -44,6 +44,7 @@
513323082281070D00C30F19 /* AccountFeedbinSyncTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513323072281070C00C30F19 /* AccountFeedbinSyncTest.swift */; };
5133230A2281082F00C30F19 /* subscriptions_initial.json in Resources */ = {isa = PBXBuildFile; fileRef = 513323092281082F00C30F19 /* subscriptions_initial.json */; };
5133230C2281088A00C30F19 /* subscriptions_add.json in Resources */ = {isa = PBXBuildFile; fileRef = 5133230B2281088A00C30F19 /* subscriptions_add.json */; };
5133BB47245FD8140001E3D0 /* RedditListing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5133BB46245FD8140001E3D0 /* RedditListing.swift */; };
5139A6382459822D004D960C /* CloudKitArticleStatusUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5139A6372459822D004D960C /* CloudKitArticleStatusUpdate.swift */; };
5144EA49227B497600D19003 /* FeedbinAPICaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5144EA48227B497600D19003 /* FeedbinAPICaller.swift */; };
5144EA4E227B829A00D19003 /* FeedbinAccountDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5144EA4D227B829A00D19003 /* FeedbinAccountDelegate.swift */; };
@ -300,6 +301,7 @@
513323072281070C00C30F19 /* AccountFeedbinSyncTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFeedbinSyncTest.swift; sourceTree = "<group>"; };
513323092281082F00C30F19 /* subscriptions_initial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = subscriptions_initial.json; sourceTree = "<group>"; };
5133230B2281088A00C30F19 /* subscriptions_add.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = subscriptions_add.json; sourceTree = "<group>"; };
5133BB46245FD8140001E3D0 /* RedditListing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedditListing.swift; sourceTree = "<group>"; };
5139A6372459822D004D960C /* CloudKitArticleStatusUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitArticleStatusUpdate.swift; sourceTree = "<group>"; };
5144EA48227B497600D19003 /* FeedbinAPICaller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinAPICaller.swift; sourceTree = "<group>"; };
5144EA4D227B829A00D19003 /* FeedbinAccountDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinAccountDelegate.swift; sourceTree = "<group>"; };
@ -633,6 +635,7 @@
isa = PBXGroup;
children = (
5193CD53245E3F7A0092735E /* RedditFeedProvider.swift */,
5133BB46245FD8140001E3D0 /* RedditListing.swift */,
5193CD80245F295E0092735E /* RedditUser.swift */,
);
path = Reddit;
@ -1204,6 +1207,7 @@
51F6C593245DBA8E001E41CA /* CloudKitRemoteNotificationOperation.swift in Sources */,
846E77541F6F00E300A165E2 /* AccountManager.swift in Sources */,
51E490362288C37100C791F0 /* FeedbinDate.swift in Sources */,
5133BB47245FD8140001E3D0 /* RedditListing.swift in Sources */,
9EEAE06E235D002D00E3FEE4 /* FeedlyGetCollectionsService.swift in Sources */,
5165D72922835F7A00D9D53D /* FeedSpecifier.swift in Sources */,
9E85C8ED2367020700D0F1F7 /* FeedlyGetEntriesService.swift in Sources */,

View File

@ -7,6 +7,7 @@
//
import Foundation
import os.log
import OAuthSwift
import Secrets
import RSParser
@ -18,12 +19,14 @@ public enum RedditFeedProviderError: LocalizedError {
public var localizedDescription: String {
switch self {
case .unknown:
return NSLocalizedString("An Reddit Twitter Feed Provider error has occurred.", comment: "Unknown error")
return NSLocalizedString("A Reddit Feed Provider error has occurred.", comment: "Unknown error")
}
}
}
public struct RedditFeedProvider: FeedProvider {
public final class RedditFeedProvider: FeedProvider {
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "RedditFeedProvider")
private static let server = "www.reddit.com"
private static let apiBase = "https://oauth.reddit.com"
@ -42,7 +45,7 @@ public struct RedditFeedProvider: FeedProvider {
return oauthSwift?.client
}
public init?(username: String) {
public convenience init?(username: String) {
guard let tokenCredentials = try? CredentialsManager.retrieveCredentials(type: .oauthAccessToken, server: Self.server, username: username),
let refreshTokenCredentials = try? CredentialsManager.retrieveCredentials(type: .oauthRefreshToken, server: Self.server, username: username) else {
return nil
@ -77,25 +80,20 @@ public struct RedditFeedProvider: FeedProvider {
}
public func iconURL(_ urlComponents: URLComponents, completion: @escaping (Result<String, Error>) -> Void) {
completion(.failure(TwitterFeedProviderError.screenNameNotFound))
completion(.failure(RedditFeedProviderError.unknown))
}
public func assignName(_ urlComponents: URLComponents, completion: @escaping (Result<String, Error>) -> Void) {
let path = urlComponents.path
switch path {
case "", "/":
if path == "" || path == "/" {
let name = NSLocalizedString("Reddit Timeline", comment: "Reddit Timeline")
completion(.success(name))
case "/r", "/u":
let path = String(path.suffix(from: path.index(path.startIndex, offsetBy: 2)))
completion(.success(path))
case "/user":
let path = String(path.suffix(from: path.index(path.startIndex, offsetBy: 5)))
completion(.success(path))
default:
completion(.failure(TwitterFeedProviderError.unknown))
return
}
// TODO: call to get the Subreddit name
completion(.success(path))
}
public func refresh(_ webFeed: WebFeed, completion: @escaping (Result<Set<ParsedItem>, Error>) -> Void) {
@ -103,26 +101,25 @@ public struct RedditFeedProvider: FeedProvider {
// completion(.failure(TwitterFeedProviderError.unknown))
// return
// }
let api = "/r/sphynx/hot.json"
retrieveListing(api: api, parameters: [:]) { result in
completion(.success(Set<ParsedItem>()))
}
completion(.success(Set<ParsedItem>()))
}
public static func create(tokenSuccess: OAuthSwift.TokenSuccess, completion: @escaping (Result<RedditFeedProvider, Error>) -> Void) {
let oauthToken = tokenSuccess.credential.oauthToken
let oauthRefreshToken = tokenSuccess.credential.oauthRefreshToken
var redditFeedProvider = RedditFeedProvider(oauthToken: oauthToken, oauthRefreshToken: oauthRefreshToken)
let redditFeedProvider = RedditFeedProvider(oauthToken: oauthToken, oauthRefreshToken: oauthRefreshToken)
redditFeedProvider.retrieveUserName() { result in
switch result {
case .success(let username):
do {
let tokenCredentials = Credentials(type: .oauthAccessToken, username: username, secret: oauthToken)
try CredentialsManager.storeCredentials(tokenCredentials, server: Self.server)
let tokenSecretCredentials = Credentials(type: .oauthRefreshToken, username: username, secret: oauthRefreshToken)
try CredentialsManager.storeCredentials(tokenSecretCredentials, server: Self.server)
redditFeedProvider.username = username
try storeCredentials(username: username, oauthToken: oauthToken, oauthRefreshToken: oauthRefreshToken)
completion(.success(redditFeedProvider))
} catch {
completion(.failure(error))
@ -157,7 +154,7 @@ extension RedditFeedProvider: OAuth2SwiftProvider {
public static var oauth2Vars: (state: String, scope: String, params: [String : String]) {
let state = generateState(withLength: 20)
let scope = "identity mysubreddits"
let scope = "identity mysubreddits read"
let params = [
"client_id" : Secrets.redditConsumerKey,
"response_type" : "code",
@ -192,5 +189,125 @@ private extension RedditFeedProvider {
}
}
}
func retrieveListing(api: String, parameters: [String: Any], completion: @escaping (Result<RedditListing, Error>) -> Void) {
guard let client = client else {
completion(.failure(RedditFeedProviderError.unknown))
return
}
let url = "\(Self.apiBase)\(api)"
client.get(url, parameters: parameters, headers: Self.userAgentHeaders) { result in
switch result {
case .success(let response):
let jsonString = String(data: response.data, encoding: .utf8)
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("reddit.json")
print("******** writing to: \(url.path)")
try? jsonString?.write(toFile: url.path, atomically: true, encoding: .utf8)
// let decoder = JSONDecoder()
// let dateFormatter = DateFormatter()
// dateFormatter.dateFormat = Self.dateFormat
// decoder.dateDecodingStrategy = .formatted(dateFormatter)
// do {
// let listing = try decoder.decode(RedditListing.self, from: response.data)
// completion(.success(listing))
// } catch {
// completion(.failure(error))
// }
let listing = RedditListing(name: "")
completion(.success(listing))
case .failure(let oathError):
self.handleFailure(error: oathError) { error in
if let error = error {
completion(.failure(error))
} else {
self.retrieveListing(api: api, parameters: parameters, completion: completion)
}
}
}
}
}
// func makeParsedItems(_ webFeedURL: String, _ statuses: [TwitterStatus]) -> Set<ParsedItem> {
// var parsedItems = Set<ParsedItem>()
//
// for status in statuses {
// guard let idStr = status.idStr, let statusURL = status.url else { continue }
//
// let parsedItem = ParsedItem(syncServiceID: nil,
// uniqueID: idStr,
// feedURL: webFeedURL,
// url: statusURL,
// externalURL: nil,
// title: nil,
// language: nil,
// contentHTML: status.renderAsHTML(),
// contentText: status.renderAsText(),
// summary: nil,
// imageURL: nil,
// bannerImageURL: nil,
// datePublished: status.createdAt,
// dateModified: nil,
// authors: makeParsedAuthors(status.user),
// tags: nil,
// attachments: nil)
// parsedItems.insert(parsedItem)
// }
//
// return parsedItems
// }
//
// func makeUserURL(_ screenName: String) -> String {
// return "https://twitter.com/\(screenName)"
// }
//
// func makeParsedAuthors(_ user: TwitterUser?) -> Set<ParsedAuthor>? {
// guard let user = user else { return nil }
// return Set([ParsedAuthor(name: user.name, url: user.url, avatarURL: user.avatarURL, emailAddress: nil)])
// }
func handleFailure(error: OAuthSwiftError, completion: @escaping (Error?) -> Void) {
if case .tokenExpired = error {
os_log(.debug, log: self.log, "Access token expired, attempting to renew...")
oauthSwift?.renewAccessToken(withRefreshToken: oauthRefreshToken) { [weak self] result in
guard let self = self, let username = self.username else {
completion(nil)
return
}
switch result {
case .success(let tokenSuccess):
self.oauthToken = tokenSuccess.credential.oauthToken
self.oauthRefreshToken = tokenSuccess.credential.oauthRefreshToken
do {
try Self.storeCredentials(username: username, oauthToken: self.oauthToken, oauthRefreshToken: self.oauthRefreshToken)
os_log(.debug, log: self.log, "Access token renewed.")
} catch {
completion(error)
return
}
completion(nil)
case .failure(let oathError):
completion(oathError)
}
}
} else {
completion(error)
}
}
static func storeCredentials(username: String, oauthToken: String, oauthRefreshToken: String) throws {
let tokenCredentials = Credentials(type: .oauthAccessToken, username: username, secret: oauthToken)
try CredentialsManager.storeCredentials(tokenCredentials, server: Self.server)
let tokenSecretCredentials = Credentials(type: .oauthRefreshToken, username: username, secret: oauthRefreshToken)
try CredentialsManager.storeCredentials(tokenSecretCredentials, server: Self.server)
}
}

View File

@ -0,0 +1,19 @@
//
// RedditListing.swift
// Account
//
// Created by Maurice Parker on 5/3/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
struct RedditListing: Codable {
let name: String?
enum CodingKeys: String, CodingKey {
case name = "name"
}
}