Add download subscriptions and faviconURLs
This commit is contained in:
parent
a5a066dd49
commit
7f9055fe78
@ -382,21 +382,24 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
||||
}
|
||||
}
|
||||
|
||||
public func createFeed(with name: String?, editedName: String?, url: String) -> Feed? {
|
||||
public func createFeed(with name: String?, editedName: String?, url: String) -> Feed {
|
||||
|
||||
// For syncing, this may need to be an async method with a callback,
|
||||
// since it will likely need to call the server.
|
||||
|
||||
let metadata = feedMetadata(feedID: url)
|
||||
let feed = Feed(account: self, url: url, feedID: url, metadata: metadata)
|
||||
if let name = name, feed.name == nil {
|
||||
feed.name = name
|
||||
}
|
||||
if let editedName = editedName, feed.editedName == nil {
|
||||
feed.editedName = editedName
|
||||
}
|
||||
|
||||
return createFeed(with: name, editedName: editedName, url: url, feedId: url, homePageURL: nil)
|
||||
|
||||
}
|
||||
|
||||
func createFeed(with name: String?, editedName: String?, url: String, feedId: String, homePageURL: String?) -> Feed {
|
||||
|
||||
let metadata = feedMetadata(feedID: feedId)
|
||||
let feed = Feed(account: self, url: url, feedID: feedId, metadata: metadata)
|
||||
feed.name = name
|
||||
feed.editedName = editedName
|
||||
feed.homePageURL = homePageURL
|
||||
|
||||
return feed
|
||||
|
||||
}
|
||||
|
||||
public func canAddFolder(_ folder: Folder, to containingFolder: Folder?) -> Bool {
|
||||
|
@ -10,6 +10,11 @@
|
||||
5107A099227DE42E00C7C3C5 /* AccountCredentialsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5107A098227DE42E00C7C3C5 /* AccountCredentialsTest.swift */; };
|
||||
5107A09B227DE49500C7C3C5 /* TestAccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5107A09A227DE49500C7C3C5 /* TestAccountManager.swift */; };
|
||||
5107A09D227DE77700C7C3C5 /* TestTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5107A09C227DE77700C7C3C5 /* TestTransport.swift */; };
|
||||
513323082281070D00C30F19 /* AccountFeedSyncTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513323072281070C00C30F19 /* AccountFeedSyncTest.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 */; };
|
||||
5133230E2281089500C30F19 /* icons.json in Resources */ = {isa = PBXBuildFile; fileRef = 5133230D2281089500C30F19 /* icons.json */; };
|
||||
5133231122810EB200C30F19 /* FeedbinIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5133230F22810E5700C30F19 /* FeedbinIcon.swift */; };
|
||||
5144EA49227B497600D19003 /* FeedbinAPICaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5144EA48227B497600D19003 /* FeedbinAPICaller.swift */; };
|
||||
5144EA4E227B829A00D19003 /* FeedbinAccountDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5144EA4D227B829A00D19003 /* FeedbinAccountDelegate.swift */; };
|
||||
51D58755227F53BE00900287 /* FeedbinTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D58754227F53BE00900287 /* FeedbinTag.swift */; };
|
||||
@ -23,7 +28,7 @@
|
||||
841974251F6DDCE4006346C4 /* AccountDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841974241F6DDCE4006346C4 /* AccountDelegate.swift */; };
|
||||
841D4D702106B40400DD04E6 /* ArticlesDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 841D4D6F2106B40400DD04E6 /* ArticlesDatabase.framework */; };
|
||||
841D4D722106B40A00DD04E6 /* Articles.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 841D4D712106B40A00DD04E6 /* Articles.framework */; };
|
||||
84245C851FDDD8CB0074AFBB /* FeedbinFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84245C841FDDD8CB0074AFBB /* FeedbinFeed.swift */; };
|
||||
84245C851FDDD8CB0074AFBB /* FeedbinSubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84245C841FDDD8CB0074AFBB /* FeedbinSubscription.swift */; };
|
||||
844B297D2106C7EC004020B3 /* Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844B297C2106C7EC004020B3 /* Feed.swift */; };
|
||||
844B297F210CE37E004020B3 /* UnreadCountProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844B297E210CE37E004020B3 /* UnreadCountProvider.swift */; };
|
||||
844B2981210CE3BF004020B3 /* RSWeb.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 844B2980210CE3BF004020B3 /* RSWeb.framework */; };
|
||||
@ -37,10 +42,8 @@
|
||||
84B99C9F1FAE8D3200ECDEDB /* ContainerPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B99C9E1FAE8D3200ECDEDB /* ContainerPath.swift */; };
|
||||
84C3654A1F899F3B001EC85C /* CombinedRefreshProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C365491F899F3B001EC85C /* CombinedRefreshProgress.swift */; };
|
||||
84C8B3F41F89DE430053CCA6 /* DataExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C8B3F31F89DE430053CCA6 /* DataExtensions.swift */; };
|
||||
84CAD7161FDF2E22000F0755 /* FeedbinArticle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CAD7151FDF2E22000F0755 /* FeedbinArticle.swift */; };
|
||||
84D096212174169100D77525 /* FeedbinArticleIDArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D096202174169100D77525 /* FeedbinArticleIDArray.swift */; };
|
||||
84CAD7161FDF2E22000F0755 /* FeedbinEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CAD7151FDF2E22000F0755 /* FeedbinEntry.swift */; };
|
||||
84D09623217418DC00D77525 /* FeedbinTagging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D09622217418DC00D77525 /* FeedbinTagging.swift */; };
|
||||
84D0962521741B8500D77525 /* FeedbinSavedSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D0962421741B8500D77525 /* FeedbinSavedSearch.swift */; };
|
||||
84EAC4822148CC6300F154AB /* RSDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84EAC4812148CC6300F154AB /* RSDatabase.framework */; };
|
||||
84F1F06E2243524700DA0616 /* AccountMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AF4EA3222CFDD100F6A800 /* AccountMetadata.swift */; };
|
||||
84F73CF1202788D90000BCEF /* ArticleFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F73CF0202788D80000BCEF /* ArticleFetcher.swift */; };
|
||||
@ -95,6 +98,11 @@
|
||||
5107A098227DE42E00C7C3C5 /* AccountCredentialsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCredentialsTest.swift; sourceTree = "<group>"; };
|
||||
5107A09A227DE49500C7C3C5 /* TestAccountManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestAccountManager.swift; sourceTree = "<group>"; };
|
||||
5107A09C227DE77700C7C3C5 /* TestTransport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestTransport.swift; sourceTree = "<group>"; };
|
||||
513323072281070C00C30F19 /* AccountFeedSyncTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFeedSyncTest.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>"; };
|
||||
5133230D2281089500C30F19 /* icons.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = icons.json; sourceTree = "<group>"; };
|
||||
5133230F22810E5700C30F19 /* FeedbinIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinIcon.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>"; };
|
||||
51D58754227F53BE00900287 /* FeedbinTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinTag.swift; sourceTree = "<group>"; };
|
||||
@ -111,7 +119,7 @@
|
||||
8419742D1F6DDE96006346C4 /* LocalAccountRefresher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAccountRefresher.swift; sourceTree = "<group>"; };
|
||||
841D4D6F2106B40400DD04E6 /* ArticlesDatabase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ArticlesDatabase.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
841D4D712106B40A00DD04E6 /* Articles.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Articles.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
84245C841FDDD8CB0074AFBB /* FeedbinFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinFeed.swift; sourceTree = "<group>"; };
|
||||
84245C841FDDD8CB0074AFBB /* FeedbinSubscription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinSubscription.swift; sourceTree = "<group>"; };
|
||||
844B297C2106C7EC004020B3 /* Feed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Feed.swift; sourceTree = "<group>"; };
|
||||
844B297E210CE37E004020B3 /* UnreadCountProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnreadCountProvider.swift; sourceTree = "<group>"; };
|
||||
844B2980210CE3BF004020B3 /* RSWeb.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RSWeb.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -126,10 +134,8 @@
|
||||
84B99C9E1FAE8D3200ECDEDB /* ContainerPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerPath.swift; sourceTree = "<group>"; };
|
||||
84C365491F899F3B001EC85C /* CombinedRefreshProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombinedRefreshProgress.swift; sourceTree = "<group>"; };
|
||||
84C8B3F31F89DE430053CCA6 /* DataExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataExtensions.swift; sourceTree = "<group>"; };
|
||||
84CAD7151FDF2E22000F0755 /* FeedbinArticle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinArticle.swift; sourceTree = "<group>"; };
|
||||
84D096202174169100D77525 /* FeedbinArticleIDArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinArticleIDArray.swift; sourceTree = "<group>"; };
|
||||
84CAD7151FDF2E22000F0755 /* FeedbinEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinEntry.swift; sourceTree = "<group>"; };
|
||||
84D09622217418DC00D77525 /* FeedbinTagging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinTagging.swift; sourceTree = "<group>"; };
|
||||
84D0962421741B8500D77525 /* FeedbinSavedSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinSavedSearch.swift; sourceTree = "<group>"; };
|
||||
84EAC4812148CC6300F154AB /* RSDatabase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RSDatabase.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
84F73CF0202788D80000BCEF /* ArticleFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleFetcher.swift; sourceTree = "<group>"; };
|
||||
D511EEB5202422BB00712EC3 /* Account_project_debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_debug.xcconfig; sourceTree = "<group>"; };
|
||||
@ -170,6 +176,9 @@
|
||||
51D58758227F630B00900287 /* tags_add.json */,
|
||||
51D58757227F630B00900287 /* tags_delete.json */,
|
||||
51D58759227F630B00900287 /* tags_initial.json */,
|
||||
513323092281082F00C30F19 /* subscriptions_initial.json */,
|
||||
5133230B2281088A00C30F19 /* subscriptions_add.json */,
|
||||
5133230D2281089500C30F19 /* icons.json */,
|
||||
);
|
||||
path = JSON;
|
||||
sourceTree = "<group>";
|
||||
@ -207,10 +216,9 @@
|
||||
children = (
|
||||
5144EA4D227B829A00D19003 /* FeedbinAccountDelegate.swift */,
|
||||
5144EA48227B497600D19003 /* FeedbinAPICaller.swift */,
|
||||
84CAD7151FDF2E22000F0755 /* FeedbinArticle.swift */,
|
||||
84D096202174169100D77525 /* FeedbinArticleIDArray.swift */,
|
||||
84245C841FDDD8CB0074AFBB /* FeedbinFeed.swift */,
|
||||
84D0962421741B8500D77525 /* FeedbinSavedSearch.swift */,
|
||||
84CAD7151FDF2E22000F0755 /* FeedbinEntry.swift */,
|
||||
5133230F22810E5700C30F19 /* FeedbinIcon.swift */,
|
||||
84245C841FDDD8CB0074AFBB /* FeedbinSubscription.swift */,
|
||||
51D58754227F53BE00900287 /* FeedbinTag.swift */,
|
||||
84D09622217418DC00D77525 /* FeedbinTagging.swift */,
|
||||
);
|
||||
@ -271,6 +279,7 @@
|
||||
children = (
|
||||
5107A098227DE42E00C7C3C5 /* AccountCredentialsTest.swift */,
|
||||
51D5875D227F643C00900287 /* AccountFolderSyncTest.swift */,
|
||||
513323072281070C00C30F19 /* AccountFeedSyncTest.swift */,
|
||||
5107A09C227DE77700C7C3C5 /* TestTransport.swift */,
|
||||
5107A09A227DE49500C7C3C5 /* TestAccountManager.swift */,
|
||||
51D58756227F62E300900287 /* JSON */,
|
||||
@ -439,9 +448,12 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5133230E2281089500C30F19 /* icons.json in Resources */,
|
||||
51D5875B227F630B00900287 /* tags_add.json in Resources */,
|
||||
5133230C2281088A00C30F19 /* subscriptions_add.json in Resources */,
|
||||
51D5875C227F630B00900287 /* tags_initial.json in Resources */,
|
||||
51D5875A227F630B00900287 /* tags_delete.json in Resources */,
|
||||
5133230A2281082F00C30F19 /* subscriptions_initial.json in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -455,7 +467,6 @@
|
||||
84C8B3F41F89DE430053CCA6 /* DataExtensions.swift in Sources */,
|
||||
84C3654A1F899F3B001EC85C /* CombinedRefreshProgress.swift in Sources */,
|
||||
8469F81C1F6DD15E0084783E /* Account.swift in Sources */,
|
||||
84D096212174169100D77525 /* FeedbinArticleIDArray.swift in Sources */,
|
||||
5144EA4E227B829A00D19003 /* FeedbinAccountDelegate.swift in Sources */,
|
||||
846E77451F6EF9B900A165E2 /* Container.swift in Sources */,
|
||||
84F73CF1202788D90000BCEF /* ArticleFetcher.swift in Sources */,
|
||||
@ -465,16 +476,16 @@
|
||||
84B2D4D02238CD8A00498ADA /* FeedMetadata.swift in Sources */,
|
||||
5144EA49227B497600D19003 /* FeedbinAPICaller.swift in Sources */,
|
||||
84B99C9F1FAE8D3200ECDEDB /* ContainerPath.swift in Sources */,
|
||||
5133231122810EB200C30F19 /* FeedbinIcon.swift in Sources */,
|
||||
846E77501F6EF9C400A165E2 /* LocalAccountRefresher.swift in Sources */,
|
||||
51D58755227F53BE00900287 /* FeedbinTag.swift in Sources */,
|
||||
84D09623217418DC00D77525 /* FeedbinTagging.swift in Sources */,
|
||||
84CAD7161FDF2E22000F0755 /* FeedbinArticle.swift in Sources */,
|
||||
84D0962521741B8500D77525 /* FeedbinSavedSearch.swift in Sources */,
|
||||
84CAD7161FDF2E22000F0755 /* FeedbinEntry.swift in Sources */,
|
||||
841974011F6DD1EC006346C4 /* Folder.swift in Sources */,
|
||||
846E774F1F6EF9C000A165E2 /* LocalAccountDelegate.swift in Sources */,
|
||||
844B297F210CE37E004020B3 /* UnreadCountProvider.swift in Sources */,
|
||||
84F1F06E2243524700DA0616 /* AccountMetadata.swift in Sources */,
|
||||
84245C851FDDD8CB0074AFBB /* FeedbinFeed.swift in Sources */,
|
||||
84245C851FDDD8CB0074AFBB /* FeedbinSubscription.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -484,6 +495,7 @@
|
||||
files = (
|
||||
51D5875E227F643C00900287 /* AccountFolderSyncTest.swift in Sources */,
|
||||
5107A09B227DE49500C7C3C5 /* TestAccountManager.swift in Sources */,
|
||||
513323082281070D00C30F19 /* AccountFeedSyncTest.swift in Sources */,
|
||||
5107A09D227DE77700C7C3C5 /* TestTransport.swift in Sources */,
|
||||
5107A099227DE42E00C7C3C5 /* AccountCredentialsTest.swift in Sources */,
|
||||
);
|
||||
|
@ -19,6 +19,7 @@ final class AccountMetadata: Codable {
|
||||
static let subscriptions = "subscriptions"
|
||||
static let tags = "tags"
|
||||
static let taggings = "taggings"
|
||||
static let icons = "icons"
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
|
65
Frameworks/Account/AccountTests/AccountFeedSyncTest.swift
Normal file
65
Frameworks/Account/AccountTests/AccountFeedSyncTest.swift
Normal file
@ -0,0 +1,65 @@
|
||||
//
|
||||
// AccountFullSyncTest.swift
|
||||
// AccountTests
|
||||
//
|
||||
// Created by Maurice Parker on 5/6/19.
|
||||
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import Account
|
||||
|
||||
class AccountFeedSyncTest: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
}
|
||||
|
||||
func testDownloadSync() {
|
||||
|
||||
let testTransport = TestTransport()
|
||||
testTransport.testFiles["https://api.feedbin.com/v2/tags.json"] = "tags_add.json"
|
||||
testTransport.testFiles["https://api.feedbin.com/v2/subscriptions.json"] = "subscriptions_initial.json"
|
||||
testTransport.testFiles["https://api.feedbin.com/v2/icons.json"] = "icons.json"
|
||||
let account = TestAccountManager.shared.createAccount(type: .feedbin, transport: testTransport)
|
||||
|
||||
// Test initial folders
|
||||
let initialExpection = self.expectation(description: "Initial feeds")
|
||||
account.refreshAll() {
|
||||
initialExpection.fulfill()
|
||||
}
|
||||
waitForExpectations(timeout: 5, handler: nil)
|
||||
|
||||
XCTAssertEqual(224, account.flattenedFeeds().count)
|
||||
|
||||
let daringFireball = account.idToFeedDictionary["1296379"]
|
||||
XCTAssertEqual("Daring Fireball", daringFireball!.name)
|
||||
XCTAssertEqual("https://daringfireball.net/feeds/json", daringFireball!.url)
|
||||
XCTAssertEqual("https://daringfireball.net/", daringFireball!.homePageURL)
|
||||
XCTAssertEqual("https://favicons.feedbinusercontent.com/6ac/6acc098f35ed2bcc0915ca89d50a97e5793eda45.png", daringFireball!.faviconURL)
|
||||
|
||||
// Test Adding a Feed
|
||||
testTransport.testFiles["https://api.feedbin.com/v2/subscriptions.json"] = "subscriptions_add.json"
|
||||
|
||||
// Test initial folders
|
||||
let addExpection = self.expectation(description: "Add feeds")
|
||||
account.refreshAll() {
|
||||
addExpection.fulfill()
|
||||
}
|
||||
waitForExpectations(timeout: 5, handler: nil)
|
||||
|
||||
XCTAssertEqual(225, account.flattenedFeeds().count)
|
||||
|
||||
let bPixels = account.idToFeedDictionary["1096623"]
|
||||
XCTAssertEqual("Beautiful Pixels", bPixels!.name)
|
||||
XCTAssertEqual("https://feedpress.me/beautifulpixels", bPixels!.url)
|
||||
XCTAssertEqual("https://beautifulpixels.com/", bPixels!.homePageURL)
|
||||
XCTAssertEqual("https://favicons.feedbinusercontent.com/ea0/ea010c658d6e356e49ab239b793dc415af707b05.png", bPixels?.faviconURL)
|
||||
|
||||
TestAccountManager.shared.deleteAccount(account)
|
||||
|
||||
}
|
||||
|
||||
}
|
1
Frameworks/Account/AccountTests/JSON/icons.json
Normal file
1
Frameworks/Account/AccountTests/JSON/icons.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -24,10 +24,17 @@ final class TestTransport: Transport {
|
||||
return
|
||||
}
|
||||
|
||||
let testFileName = testFiles[urlString]!
|
||||
let testFileURL = Bundle(for: TestTransport.self).resourceURL!.appendingPathComponent(testFileName)
|
||||
let data = try! Data(contentsOf: testFileURL)
|
||||
completion(.success((HTTPHeaders(), data)))
|
||||
if let testFileName = testFiles[urlString] {
|
||||
let testFileURL = Bundle(for: TestTransport.self).resourceURL!.appendingPathComponent(testFileName)
|
||||
let data = try! Data(contentsOf: testFileURL)
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
completion(.success((HTTPHeaders(), data)))
|
||||
}
|
||||
} else {
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
completion(.success((HTTPHeaders(), nil)))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,46 @@ final class FeedbinAPICaller: NSObject {
|
||||
|
||||
}
|
||||
|
||||
func retrieveSubscriptions(completionHandler completion: @escaping (Result<[FeedbinSubscription]?, Error>) -> Void) {
|
||||
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("subscriptions.json")
|
||||
let conditionalGet = accountMetadata?.conditionalGetInfo[AccountMetadata.ConditionalGetKeys.subscriptions]
|
||||
let request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet)
|
||||
|
||||
transport.send(request: request, resultType: [FeedbinSubscription].self) { [weak self] result in
|
||||
|
||||
switch result {
|
||||
case .success(let (headers, subscriptions)):
|
||||
self?.storeConditionalGet(metadata: self?.accountMetadata, key: AccountMetadata.ConditionalGetKeys.subscriptions, headers: headers)
|
||||
completion(.success(subscriptions))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func retrieveIcons(completionHandler completion: @escaping (Result<[FeedbinIcon]?, Error>) -> Void) {
|
||||
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("icons.json")
|
||||
let conditionalGet = accountMetadata?.conditionalGetInfo[AccountMetadata.ConditionalGetKeys.icons]
|
||||
let request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet)
|
||||
|
||||
transport.send(request: request, resultType: [FeedbinIcon].self) { [weak self] result in
|
||||
|
||||
switch result {
|
||||
case .success(let (headers, icons)):
|
||||
self?.storeConditionalGet(metadata: self?.accountMetadata, key: AccountMetadata.ConditionalGetKeys.icons, headers: headers)
|
||||
completion(.success(icons))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
@ -39,12 +39,17 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
||||
var refreshProgress = DownloadProgress(numberOfTasks: 0)
|
||||
|
||||
func refreshAll(for account: Account, completion: (() -> Void)? = nil) {
|
||||
refreshAll(account) { [weak self] result in
|
||||
refreshFolders(account) { [weak self] result in
|
||||
switch result {
|
||||
case .success():
|
||||
completion?()
|
||||
DispatchQueue.main.async {
|
||||
completion?()
|
||||
}
|
||||
case .failure(let error):
|
||||
self?.handleError(error)
|
||||
DispatchQueue.main.async {
|
||||
completion?()
|
||||
// self?.handleError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -54,10 +59,14 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
||||
caller.renameTag(oldName: folder.name ?? "", newName: name) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
folder.name = name
|
||||
completion(.success(()))
|
||||
DispatchQueue.main.async {
|
||||
folder.name = name
|
||||
completion(.success(()))
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
DispatchQueue.main.async {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,12 +83,17 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
||||
caller.deleteTag(name: folder.name ?? "") { result in
|
||||
switch result {
|
||||
case .success:
|
||||
account.deleteFolder(folder)
|
||||
// TODO: Take the serialized taggings and reestablish the folder to feed relationships. Deleting
|
||||
// a tag on Feedbin doesn't any feeds.
|
||||
completion(.success(()))
|
||||
DispatchQueue.main.async {
|
||||
|
||||
account.deleteFolder(folder)
|
||||
// TODO: Take the serialized taggings and reestablish the folder to feed relationships. Deleting
|
||||
// a tag on Feedbin doesn't any feeds.
|
||||
completion(.success(()))
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
DispatchQueue.main.async {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,7 +109,9 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
||||
let caller = FeedbinAPICaller(transport: transport)
|
||||
caller.credentials = credentials
|
||||
caller.validateCredentials() { result in
|
||||
completion(result)
|
||||
DispatchQueue.main.async {
|
||||
completion(result)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -108,8 +124,7 @@ private extension FeedbinAccountDelegate {
|
||||
|
||||
func handleError(_ error: Error) {
|
||||
// TODO: We should do a better job of error handling here.
|
||||
// We need to prompt for credentials and provide user friendly
|
||||
// errors.
|
||||
// We need to prompt for credentials if they are expired.
|
||||
#if os(macOS)
|
||||
NSApplication.shared.presentError(error)
|
||||
#else
|
||||
@ -117,13 +132,13 @@ private extension FeedbinAccountDelegate {
|
||||
#endif
|
||||
}
|
||||
|
||||
func refreshAll(_ account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
func refreshFolders(_ account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
caller.retrieveTags { [weak self] result in
|
||||
switch result {
|
||||
case .success(let tags):
|
||||
self?.syncFolders(account, tags)
|
||||
completion(.success(()))
|
||||
self?.refreshFeeds(account, completion: completion)
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
@ -141,7 +156,9 @@ private extension FeedbinAccountDelegate {
|
||||
if let folders = account.folders {
|
||||
folders.forEach { folder in
|
||||
if !tagNames.contains(folder.name ?? "") {
|
||||
account.deleteFolder(folder)
|
||||
DispatchQueue.main.sync {
|
||||
account.deleteFolder(folder)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -157,10 +174,81 @@ private extension FeedbinAccountDelegate {
|
||||
// Make any folders Feedbin has, but we don't
|
||||
tagNames.forEach { tagName in
|
||||
if !folderNames.contains(tagName) {
|
||||
account.ensureFolder(with: tagName)
|
||||
DispatchQueue.main.sync {
|
||||
_ = account.ensureFolder(with: tagName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func refreshFeeds(_ account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
caller.retrieveSubscriptions { [weak self] result in
|
||||
switch result {
|
||||
case .success(let subscriptions):
|
||||
self?.syncFeeds(account, subscriptions)
|
||||
self?.refreshFavicons(account, completion: completion)
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func syncFeeds(_ account: Account, _ subscriptions: [FeedbinSubscription]?) {
|
||||
guard let subscriptions = subscriptions else { return }
|
||||
subscriptions.forEach { subscription in
|
||||
syncFeed(account, subscription)
|
||||
}
|
||||
}
|
||||
|
||||
func syncFeed(_ account: Account, _ subscription: FeedbinSubscription) {
|
||||
|
||||
let subFeedId = String(subscription.feedID)
|
||||
|
||||
DispatchQueue.main.sync {
|
||||
if let feed = account.idToFeedDictionary[subFeedId] {
|
||||
feed.name = subscription.name
|
||||
feed.homePageURL = subscription.homePageURL
|
||||
} else {
|
||||
let feed = account.createFeed(with: subscription.name, editedName: nil, url: subscription.url, feedId: subFeedId, homePageURL: subscription.homePageURL)
|
||||
account.addFeed(feed, to: nil)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func refreshFavicons(_ account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
caller.retrieveIcons { [weak self] result in
|
||||
switch result {
|
||||
case .success(let icons):
|
||||
self?.syncIcons(account, icons)
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func syncIcons(_ account: Account, _ icons: [FeedbinIcon]?) {
|
||||
|
||||
guard let icons = icons else { return }
|
||||
|
||||
let iconDict = Dictionary(uniqueKeysWithValues: icons.map { ($0.host, $0.url) } )
|
||||
|
||||
for feed in account.flattenedFeeds() {
|
||||
for (key, value) in iconDict {
|
||||
if feed.homePageURL?.contains(key) ?? false {
|
||||
DispatchQueue.main.sync {
|
||||
feed.faviconURL = value
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,106 +0,0 @@
|
||||
//
|
||||
// FeedbinArticle.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Brent Simmons on 12/11/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSParser
|
||||
import RSCore
|
||||
|
||||
struct FeedbinArticle {
|
||||
|
||||
// https://github.com/feedbin/feedbin-api/blob/master/content/entries.md
|
||||
// https://github.com/feedbin/feedbin-api/blob/master/content/updated-entries.md
|
||||
//
|
||||
// "id": 2077,
|
||||
// "feed_id": 135,
|
||||
// "title": "Objective-C Runtime Releases",
|
||||
// "url": "http:\/\/mjtsai.com\/blog\/2013\/02\/02\/objective-c-runtime-releases\/",
|
||||
// "author": "Michael Tsai",
|
||||
// "content": "<p><a href=\"https:\/\/twitter.com\/bavarious\/status\/297851496945577984\">Bavarious<\/a> created a <a href=\"https:\/\/github.com\/bavarious\/objc4\/commits\/master\">GitHub repository<\/a> that shows the differences between versions of <a href=\"http:\/\/www.opensource.apple.com\/source\/objc4\/\">Apple\u2019s Objective-C runtime<\/a> that shipped with different versions of Mac OS X.<\/p>",
|
||||
// "summary": "Bavarious created a GitHub repository that shows the differences between versions of Apple\u2019s Objective-C runtime that shipped with different versions of Mac OS X.",
|
||||
// "published": "2013-02-03T01:00:19.000000Z",
|
||||
// "created_at": "2013-02-04T01:00:19.127893Z"
|
||||
|
||||
let articleID: Int
|
||||
let feedID: Int
|
||||
let title: String?
|
||||
let url: String?
|
||||
let authorName: String?
|
||||
let contentHTML: String?
|
||||
let contentDiffHTML: String?
|
||||
let summary: String?
|
||||
let datePublished: Date?
|
||||
let dateArrived: Date?
|
||||
|
||||
struct Key {
|
||||
static let articleID = "id"
|
||||
static let feedID = "feed_id"
|
||||
static let title = "title"
|
||||
static let url = "url"
|
||||
static let authorName = "author"
|
||||
static let contentHTML = "content"
|
||||
static let contentDiffHTML = "content_diff"
|
||||
static let summary = "summary"
|
||||
static let datePublished = "published"
|
||||
static let dateArrived = "created_at"
|
||||
}
|
||||
|
||||
init?(jsonDictionary: JSONDictionary) {
|
||||
guard let articleID = jsonDictionary[Key.articleID] as? Int else {
|
||||
return nil
|
||||
}
|
||||
guard let feedID = jsonDictionary[Key.feedID] as? Int else {
|
||||
return nil
|
||||
}
|
||||
self.articleID = articleID
|
||||
self.feedID = feedID
|
||||
|
||||
self.title = jsonDictionary[Key.title] as? String
|
||||
self.url = jsonDictionary[Key.url] as? String
|
||||
self.authorName = jsonDictionary[Key.authorName] as? String
|
||||
|
||||
if let contentHTML = jsonDictionary[Key.contentHTML] as? String, !contentHTML.isEmpty {
|
||||
self.contentHTML = contentHTML
|
||||
}
|
||||
else {
|
||||
self.contentHTML = nil
|
||||
}
|
||||
|
||||
if let contentDiffHTML = jsonDictionary[Key.contentDiffHTML] as? String, !contentDiffHTML.isEmpty {
|
||||
self.contentDiffHTML = contentDiffHTML
|
||||
}
|
||||
else {
|
||||
self.contentDiffHTML = nil
|
||||
}
|
||||
|
||||
if let summary = jsonDictionary[Key.summary] as? String, !summary.isEmpty {
|
||||
self.summary = summary
|
||||
}
|
||||
else {
|
||||
self.summary = nil
|
||||
}
|
||||
|
||||
if let datePublishedString = jsonDictionary[Key.datePublished] as? String {
|
||||
self.datePublished = RSDateWithString(datePublishedString)
|
||||
}
|
||||
else {
|
||||
self.datePublished = nil
|
||||
}
|
||||
|
||||
if let dateArrivedString = jsonDictionary[Key.dateArrived] as? String {
|
||||
self.dateArrived = RSDateWithString(dateArrivedString)
|
||||
}
|
||||
else {
|
||||
self.dateArrived = nil
|
||||
}
|
||||
}
|
||||
|
||||
static func articles(with array: JSONArray) -> [FeedbinArticle]? {
|
||||
let articlesArray = array.compactMap { FeedbinArticle(jsonDictionary: $0) }
|
||||
return articlesArray.isEmpty ? nil : articlesArray
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
//
|
||||
// FeedbinArticleIDArray.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Brent Simmons on 10/14/18.
|
||||
// Copyright © 2018 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct FeedbinArticleIDArray {
|
||||
|
||||
// https://github.com/feedbin/feedbin-api/blob/master/content/unread-entries.md
|
||||
//
|
||||
// [4087,4088,4089,4090,4091,4092,4093,4094,4095,4096,4097]
|
||||
|
||||
let articleIDs: [Int]
|
||||
|
||||
init(jsonArray: [Any]) {
|
||||
self.articleIDs = jsonArray.compactMap { $0 as? Int }
|
||||
}
|
||||
}
|
39
Frameworks/Account/Feedbin/FeedbinEntry.swift
Normal file
39
Frameworks/Account/Feedbin/FeedbinEntry.swift
Normal file
@ -0,0 +1,39 @@
|
||||
//
|
||||
// FeedbinArticle.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Brent Simmons on 12/11/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSParser
|
||||
import RSCore
|
||||
|
||||
struct FeedbinEntry: Codable {
|
||||
|
||||
let articleID: Int
|
||||
let feedID: Int
|
||||
let title: String?
|
||||
let url: String?
|
||||
let authorName: String?
|
||||
let contentHTML: String?
|
||||
let contentDiffHTML: String?
|
||||
let summary: String?
|
||||
let datePublished: Date?
|
||||
let dateArrived: Date?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case articleID = "id"
|
||||
case feedID = "feed_id"
|
||||
case title = "title"
|
||||
case url = "url"
|
||||
case authorName = "author"
|
||||
case contentHTML = "content"
|
||||
case contentDiffHTML = "content_diff"
|
||||
case summary = "summary"
|
||||
case datePublished = "published"
|
||||
case dateArrived = "created_at"
|
||||
}
|
||||
|
||||
}
|
21
Frameworks/Account/Feedbin/FeedbinIcon.swift
Normal file
21
Frameworks/Account/Feedbin/FeedbinIcon.swift
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// FeedbinIcon.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Maurice Parker on 5/6/19.
|
||||
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct FeedbinIcon: Codable {
|
||||
|
||||
let host: String
|
||||
let url: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case host
|
||||
case url
|
||||
}
|
||||
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
//
|
||||
// FeedbinSavedSearch.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Brent Simmons on 10/14/18.
|
||||
// Copyright © 2018 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct FeedbinSavedSearch: Hashable {
|
||||
|
||||
// https://github.com/feedbin/feedbin-api/blob/master/content/saved-searches.md
|
||||
//
|
||||
// [
|
||||
// {
|
||||
// "id": 1,
|
||||
// "name": "JavaScript",
|
||||
// "query": "javascript is:unread"
|
||||
// }
|
||||
// ]
|
||||
|
||||
let uniqueID: Int
|
||||
let name: String
|
||||
let query: String
|
||||
|
||||
private struct Key {
|
||||
static let uniqueID = "id"
|
||||
static let name = "name"
|
||||
static let query = "query"
|
||||
}
|
||||
|
||||
init?(jsonDictionary: [String: Any]) {
|
||||
guard let uniqueID = jsonDictionary[Key.uniqueID] as? Int else {
|
||||
return nil
|
||||
}
|
||||
guard let name = jsonDictionary[Key.name] as? String else {
|
||||
return nil
|
||||
}
|
||||
guard let query = jsonDictionary[Key.query] as? String else {
|
||||
return nil
|
||||
}
|
||||
self.uniqueID = uniqueID
|
||||
self.name = name
|
||||
self.query = query
|
||||
}
|
||||
|
||||
static func savedSearches(with jsonArray: [Any]) -> Set<FeedbinSavedSearch> {
|
||||
let searches = jsonArray.compactMap { (oneSearch) -> FeedbinSavedSearch? in
|
||||
if let oneSearch = oneSearch as? [String: Any] {
|
||||
return FeedbinSavedSearch(jsonDictionary: oneSearch)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return Set(searches)
|
||||
}
|
||||
}
|
@ -10,20 +10,10 @@ import Foundation
|
||||
import RSCore
|
||||
import RSParser
|
||||
|
||||
struct FeedbinFeed: Codable {
|
||||
|
||||
// https://github.com/feedbin/feedbin-api/blob/master/content/feeds.md
|
||||
//
|
||||
// "id": 525,
|
||||
// "created_at": "2013-03-12T11:30:25.209432Z",
|
||||
// "feed_id": 47,
|
||||
// "title": "Daring Fireball",
|
||||
// "feed_url": "http://daringfireball.net/index.xml",
|
||||
// "site_url": "http://daringfireball.net/"
|
||||
struct FeedbinSubscription: Codable {
|
||||
|
||||
let subscriptionID: Int
|
||||
let feedID: Int
|
||||
let creationDate: Date?
|
||||
let name: String?
|
||||
let url: String
|
||||
let homePageURL: String?
|
||||
@ -31,7 +21,6 @@ struct FeedbinFeed: Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case subscriptionID = "id"
|
||||
case feedID = "feed_id"
|
||||
case creationDate = "created_at"
|
||||
case name = "title"
|
||||
case url = "feed_url"
|
||||
case homePageURL = "site_url"
|
@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct FeedbinTag: Codable, Equatable, Hashable {
|
||||
struct FeedbinTag: Codable {
|
||||
|
||||
let tagID: Int
|
||||
let name: String
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct FeedbinTagging: Codable, Equatable, Hashable {
|
||||
struct FeedbinTagging: Codable {
|
||||
|
||||
let taggingID: Int
|
||||
let feedID: Int
|
||||
|
@ -170,9 +170,7 @@ private extension AddFeedController {
|
||||
return
|
||||
}
|
||||
|
||||
guard let feed = account.createFeed(with: titleFromFeed, editedName: userEnteredTitle, url: feedURLString) else {
|
||||
return
|
||||
}
|
||||
let feed = account.createFeed(with: titleFromFeed, editedName: userEnteredTitle, url: feedURLString)
|
||||
|
||||
if let parsedFeed = parsedFeed {
|
||||
account.update(feed, with: parsedFeed, {})
|
||||
|
@ -114,10 +114,7 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
|
||||
if let parsedFeed = parsedFeedOptional {
|
||||
let titleFromFeed = parsedFeed.title
|
||||
|
||||
guard let feed = account.createFeed(with: titleFromFeed, editedName: titleFromArgs, url: url) else {
|
||||
command.resumeExecution(withResult:nil)
|
||||
return
|
||||
}
|
||||
let feed = account.createFeed(with: titleFromFeed, editedName: titleFromArgs, url: url)
|
||||
account.update(feed, with:parsedFeed, {})
|
||||
|
||||
// add the feed, putting it in a folder if needed
|
||||
|
@ -220,10 +220,7 @@ private extension AddFeedViewController {
|
||||
return
|
||||
}
|
||||
|
||||
guard let feed = account.createFeed(with: titleFromFeed, editedName: userEnteredTitle, url: feedURLString) else {
|
||||
delegate?.processingDidEnd()
|
||||
return
|
||||
}
|
||||
let feed = account.createFeed(with: titleFromFeed, editedName: userEnteredTitle, url: feedURLString)
|
||||
|
||||
if let parsedFeed = parsedFeed {
|
||||
account.update(feed, with: parsedFeed, {})
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit d1d5eba957eefec54b9a8c8648024a389c2271f0
|
||||
Subproject commit 4cf5b71a292573c71ca212997a453f9158e95db2
|
Loading…
x
Reference in New Issue
Block a user