Add initial tweet retrieval.

This commit is contained in:
Maurice Parker 2020-04-16 20:18:47 -05:00
parent 660cf29305
commit 9be641c094
5 changed files with 103 additions and 4 deletions

View File

@ -39,6 +39,7 @@
5132AAC42448BAD90077840A /* FeedProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5132AAC12448BAD90077840A /* FeedProvider.swift */; };
5132AAC52448BAD90077840A /* TwitterFeedProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5132AAC32448BAD90077840A /* TwitterFeedProvider.swift */; };
5132DE812449159100806ADE /* TwitterUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5132DE802449159100806ADE /* TwitterUser.swift */; };
5132DE832449306F00806ADE /* Tweet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5132DE822449306F00806ADE /* Tweet.swift */; };
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 */; };
@ -278,6 +279,7 @@
5132AAC12448BAD90077840A /* FeedProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedProvider.swift; sourceTree = "<group>"; };
5132AAC32448BAD90077840A /* TwitterFeedProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TwitterFeedProvider.swift; sourceTree = "<group>"; };
5132DE802449159100806ADE /* TwitterUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwitterUser.swift; sourceTree = "<group>"; };
5132DE822449306F00806ADE /* Tweet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tweet.swift; sourceTree = "<group>"; };
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>"; };
@ -566,6 +568,7 @@
children = (
5132AAC32448BAD90077840A /* TwitterFeedProvider.swift */,
5132DE802449159100806ADE /* TwitterUser.swift */,
5132DE822449306F00806ADE /* Tweet.swift */,
);
path = Twitter;
sourceTree = "<group>";
@ -1127,6 +1130,7 @@
552032FD229D5D5A009559E0 /* ReaderAPITagging.swift in Sources */,
9EAEC62A23331EE70085D7C9 /* FeedlyOrigin.swift in Sources */,
9E5EC15B23E01DEF00A4E503 /* FeedlyRTLTextSanitizer.swift in Sources */,
5132DE832449306F00806ADE /* Tweet.swift in Sources */,
511B9804237CD4270028BCAA /* FeedIdentifier.swift in Sources */,
84F73CF1202788D90000BCEF /* ArticleFetcher.swift in Sources */,
841974251F6DDCE4006346C4 /* AccountDelegate.swift in Sources */,

View File

@ -0,0 +1,35 @@
//
// Tweet.swift
// Account
//
// Created by Maurice Parker on 4/16/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
struct Tweet: Codable {
let createdAt: Date?
let idStr: String?
let text: String?
let user: TwitterUser
let truncated: Bool
let extendedTweet: ExtendedTweet?
enum CodingKeys: String, CodingKey {
case createdAt = "created_at"
case idStr = "id_str"
case text = "text"
case user = "user"
case truncated = "truncated"
case extendedTweet = "extended_tweet"
}
}
struct ExtendedTweet: Codable {
let full_text: String?
}

View File

@ -87,7 +87,7 @@ public struct TwitterFeedProvider: FeedProvider {
public func iconURL(_ urlComponents: URLComponents, completion: @escaping (Result<String, Error>) -> Void) {
if let screenName = deriveScreenName(urlComponents) {
fetchUser(screenName: screenName) { result in
retrieveUser(screenName: screenName) { result in
switch result {
case .success(let user):
if let avatarURL = user.avatarURL {
@ -127,7 +127,7 @@ public struct TwitterFeedProvider: FeedProvider {
default:
if let screenName = deriveScreenName(urlComponents) {
fetchUser(screenName: screenName) { result in
retrieveUser(screenName: screenName) { result in
switch result {
case .success(let user):
if let userName = user.name {
@ -149,7 +149,41 @@ public struct TwitterFeedProvider: FeedProvider {
}
public func refresh(_ webFeed: WebFeed, completion: @escaping (Result<Set<ParsedItem>, Error>) -> Void) {
// TODO: Finish implementation
let api = "statuses/user_timeline.json"
retrieveTweets(api: api) { result in
switch result {
case .success(let tweets):
var parsedItems = Set<ParsedItem>()
for tweet in tweets {
guard let idStr = tweet.idStr, let userScreenName = tweet.user.screenName else { continue }
let parsedItem = ParsedItem(syncServiceID: idStr,
uniqueID: idStr,
feedURL: webFeed.url,
url: "https://twitter.com/\(userScreenName)/status/\(idStr)",
externalURL: nil,
title: nil,
language: nil,
contentHTML: tweet.text,
contentText: tweet.text,
summary: tweet.text,
imageURL: nil,
bannerImageURL: nil,
datePublished: tweet.createdAt,
dateModified: nil,
authors: nil,
tags: nil,
attachments: nil)
parsedItems.insert(parsedItem)
}
completion(.success(parsedItems))
case .failure(let error):
completion(.failure(error))
}
}
}
}
@ -185,7 +219,7 @@ private extension TwitterFeedProvider {
}
}
func fetchUser(screenName: String, completion: @escaping (Result<TwitterUser, Error>) -> Void) {
func retrieveUser(screenName: String, completion: @escaping (Result<TwitterUser, Error>) -> Void) {
let url = "\(Self.apiBase)users/show.json"
let parameters = ["screen_name": screenName]
@ -205,4 +239,27 @@ private extension TwitterFeedProvider {
}
}
func retrieveTweets(api: String, completion: @escaping (Result<[Tweet], Error>) -> Void) {
let url = "\(Self.apiBase)\(api)"
let parameters = [String: Any]()
client.get(url, parameters: parameters) { result in
switch result {
case .success(let response):
let decoder = JSONDecoder()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEE MMM dd HH:mm:ss Z yyyy"
decoder.dateDecodingStrategy = .formatted(dateFormatter)
do {
let tweets = try decoder.decode([Tweet].self, from: response.data)
completion(.success(tweets))
} catch {
completion(.failure(error))
}
case .failure(let error):
completion(.failure(error))
}
}
}
}

View File

@ -11,10 +11,12 @@ import Foundation
struct TwitterUser: Codable {
let name: String?
let screenName: String?
let avatarURL: String?
enum CodingKeys: String, CodingKey {
case name = "name"
case screenName = "screen_name"
case avatarURL = "profile_image_url_https"
}

View File

@ -141,6 +141,7 @@ final class LocalAccountDelegate: AccountDelegate {
case .success(let parsedItems):
account.update(urlString, with: parsedItems) { _ in
container.addWebFeed(feed)
completion(.success(feed))
}
case .failure:
completion(.failure(AccountError.createErrorNotFound))