diff --git a/Frameworks/FeedProvider/Twitter/TwitterFeedProvider.swift b/Frameworks/FeedProvider/Twitter/TwitterFeedProvider.swift index db6ce1f63..1905eae83 100644 --- a/Frameworks/FeedProvider/Twitter/TwitterFeedProvider.swift +++ b/Frameworks/FeedProvider/Twitter/TwitterFeedProvider.swift @@ -11,9 +11,15 @@ import Secrets import OAuthSwift import RSParser +// TODO: Beef up error handling... +public enum TwitterFeedProviderError: Error { + case unknown +} + public struct TwitterFeedProvider: FeedProvider { private static let server = "api.twitter.com" + private static let apiBase = "https://api.twitter.com/1.1/" public var userID: String public var screenName: String @@ -21,6 +27,8 @@ public struct TwitterFeedProvider: FeedProvider { private var oauthToken: String private var oauthTokenSecret: String + private var client: OAuthSwiftClient + public init?(tokenSuccess: OAuthSwift.TokenSuccess) { guard let userID = tokenSuccess.parameters["user_id"] as? String, let screenName = tokenSuccess.parameters["screen_name"] as? String else { @@ -37,6 +45,12 @@ public struct TwitterFeedProvider: FeedProvider { let tokenSecretCredentials = Credentials(type: .oauthAccessTokenSecret, username: userID, secret: oauthTokenSecret) try? CredentialsManager.storeCredentials(tokenSecretCredentials, server: Self.server) + + client = OAuthSwiftClient(consumerKey: Secrets.twitterConsumerKey, + consumerSecret: Secrets.twitterConsumerSecret, + oauthToken: oauthToken, + oauthTokenSecret: oauthTokenSecret, + version: .oauth1) } public init?(userID: String, screenName: String) { @@ -50,6 +64,12 @@ public struct TwitterFeedProvider: FeedProvider { self.oauthToken = tokenCredentials.secret self.oauthTokenSecret = tokenSecretCredentials.secret + + client = OAuthSwiftClient(consumerKey: Secrets.twitterConsumerKey, + consumerSecret: Secrets.twitterConsumerSecret, + oauthToken: oauthToken, + oauthTokenSecret: oauthTokenSecret, + version: .oauth1) } public func ability(_ urlComponents: URLComponents, forUsername username: String?) -> FeedProviderAbility { @@ -65,7 +85,8 @@ public struct TwitterFeedProvider: FeedProvider { } public func iconURL(_ url: URLComponents, completion: @escaping (Result) -> Void) { - // TODO: Finish implementation + let screenName = extractScreenName(url) + fetchIconURL(screenName: screenName, completion: completion) } public func provide(_ url: URLComponents, completion: @escaping (Result) -> Void) { @@ -93,3 +114,39 @@ extension TwitterFeedProvider: OAuth1SwiftProvider { } } + +// MARK: Private + +private extension TwitterFeedProvider { + + // TODO: Full parsing routine + func extractScreenName(_ urlComponents: URLComponents) -> String { + let path = urlComponents.path + if let index = path.firstIndex(of: "?") { + let range = path.index(path.startIndex, offsetBy: 1)...index + return String(path[range]) + } else { + return String(path.suffix(from: path.index(path.startIndex, offsetBy: 1))) + } + } + + // TODO: Update to retrieve the full user + func fetchIconURL(screenName: String, completion: @escaping (Result) -> Void) { + let url = "\(Self.apiBase)users/show.json" + let parameters = ["screen_name": screenName] + + client.get(url, parameters: parameters) { result in + switch result { + case .success(let response): + if let json = try? response.jsonObject() as? [String: Any], let url = json["profile_image_url_https"] as? String { + completion(.success(url)) + } else { + completion(.failure(TwitterFeedProviderError.unknown)) + } + case .failure(let error): + completion(.failure(error)) + } + } + } + +} diff --git a/Shared/Images/WebFeedIconDownloader.swift b/Shared/Images/WebFeedIconDownloader.swift index bb67e2706..77f16f5ce 100644 --- a/Shared/Images/WebFeedIconDownloader.swift +++ b/Shared/Images/WebFeedIconDownloader.swift @@ -82,22 +82,38 @@ public final class WebFeedIconDownloader { } } } - - if let iconURL = feed.iconURL { - icon(forURL: iconURL, feed: feed) { (image) in - if let image = image { - self.postFeedIconDidBecomeAvailableNotification(feed) - self.cache[feed] = IconImage(image) - } - else { - checkHomePageURL() + + func checkFeedIconURL() { + if let iconURL = feed.iconURL { + icon(forURL: iconURL, feed: feed) { (image) in + if let image = image { + self.postFeedIconDidBecomeAvailableNotification(feed) + self.cache[feed] = IconImage(image) + } else { + checkHomePageURL() + } } + } else { + checkHomePageURL() } } - else { - checkHomePageURL() - } + if let components = URLComponents(string: feed.url), let feedProvider = ExtensionPointManager.shared.bestFeedProvider(for: components, with: nil) { + feedProvider.iconURL(components) { result in + if case .success(let url) = result { + self.icon(forURL: url, feed: feed) { (image) in + if let image = image { + self.postFeedIconDidBecomeAvailableNotification(feed) + self.cache[feed] = IconImage(image) + } else { + checkFeedIconURL() + } + } + } + } + } else { + checkFeedIconURL() + } return nil }