Commit major surgery but leave it unfinished. Everything is broken.
This commit is contained in:
parent
683d5aaf71
commit
944f05c71e
@ -11,7 +11,7 @@ import DB5
|
||||
import DataModel
|
||||
import RSTextDrawing
|
||||
import RSTree
|
||||
import RSXML
|
||||
import RSParser
|
||||
import RSWeb
|
||||
|
||||
var currentTheme: VSTheme!
|
||||
@ -353,7 +353,7 @@ private extension AppDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
let xmlData = RSXMLData(data: opmlData, urlString: url.absoluteString)
|
||||
let parserData = ParserData(data: opmlData, urlString: url.absoluteString)
|
||||
RSParseOPML(xmlData) { (opmlDocument, error) in
|
||||
|
||||
if let error = error {
|
||||
|
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSXML
|
||||
import RSParser
|
||||
import RSWeb
|
||||
|
||||
func downloadTitleForFeed(_ url: URL, _ completionHandler: @escaping (_ title: String?) -> ()) {
|
||||
@ -19,9 +19,8 @@ func downloadTitleForFeed(_ url: URL, _ completionHandler: @escaping (_ title: S
|
||||
return
|
||||
}
|
||||
|
||||
let xmlData = RSXMLData(data: data, urlString: url.absoluteString)
|
||||
RSParseFeed(xmlData) { (parsedFeed : RSParsedFeed?, error: Error?) in
|
||||
|
||||
let parserData = ParserData(url: url.absoluteString, data: data)
|
||||
FeedParser.parse(parserData) { (parsedFeed, error) in
|
||||
completionHandler(parsedFeed?.title)
|
||||
}
|
||||
}
|
||||
|
35
Frameworks/DataModel/Account.swift
Normal file
35
Frameworks/DataModel/Account.swift
Normal file
@ -0,0 +1,35 @@
|
||||
//
|
||||
// Account.swift
|
||||
// DataModel
|
||||
//
|
||||
// Created by Brent Simmons on 7/1/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum AccountType: Int {
|
||||
|
||||
case onMyMac = 1
|
||||
case feedly = 16
|
||||
case feedbin
|
||||
case feedWrangler
|
||||
case newsBlur
|
||||
}
|
||||
|
||||
public final class Account: Container, PlistProvider {
|
||||
|
||||
public let identifier: String
|
||||
public let type: AccountType
|
||||
public var nameForDisplay: String
|
||||
public weak var delegate: AccountDelegate
|
||||
|
||||
init(settingsFile: String, type: AccountType, dataFolder: String, identifier: String, delegate: AccountDelegate) {
|
||||
|
||||
self.identifier = identifier
|
||||
self.type = type
|
||||
self.settingsFile = settingsFile
|
||||
self.dataFolder = dataFolder
|
||||
self.delegate = delegate
|
||||
}
|
||||
}
|
16
Frameworks/DataModel/AccountDelegate.swift
Normal file
16
Frameworks/DataModel/AccountDelegate.swift
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// AccountDelegate.swift
|
||||
// DataModel
|
||||
//
|
||||
// Created by Brent Simmons on 7/1/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol AccountDelegate {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
77
Frameworks/DataModel/Article.swift
Normal file
77
Frameworks/DataModel/Article.swift
Normal file
@ -0,0 +1,77 @@
|
||||
//
|
||||
// Article.swift
|
||||
// DataModel
|
||||
//
|
||||
// Created by Brent Simmons on 7/1/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class Article: Hashable {
|
||||
|
||||
weak var account: Account?
|
||||
let feedID: String
|
||||
let articleID: String //Calculated: unique per account
|
||||
|
||||
var uniqueID: String //guid: unique per feed
|
||||
var title: String?
|
||||
var contentHTML: String?
|
||||
var contentText: String?
|
||||
var url: String?
|
||||
var externalURL: String?
|
||||
var summary: String?
|
||||
var imageURL: String?
|
||||
var bannerImageURL: String?
|
||||
var datePublished: Date?
|
||||
var dateModified: Date?
|
||||
var authors: [Authors]?
|
||||
var tags: [String]?
|
||||
var attachments: [Attachment]?
|
||||
var status: ArticleStatus?
|
||||
|
||||
public var accountInfo: [String: Any]? //If account needs to store more data
|
||||
|
||||
var feed: Feed? {
|
||||
get {
|
||||
return account?.existingFeed(with: feedID)
|
||||
}
|
||||
}
|
||||
|
||||
init(account: Account, feedID: String, uniqueID: String, title: String?, contentHTML: String?, contentText: String?, url: String?, externalURL: String?, summary: String?, imageURL: String?, bannerImageURL: String?, datePublished: Date?, dateModified: Date?, authors: [Authors]?, tags: [String]?, attachments: [Attachment]?) {
|
||||
|
||||
self.account = account
|
||||
self.feedID = feedID
|
||||
self.uniqueID = uniqueID
|
||||
self.title = title
|
||||
self.contentHTML = contentHTML
|
||||
self.contentText = contentText
|
||||
self.url = url
|
||||
self.externalURL = externalURL
|
||||
self.summary = summary
|
||||
self.imageURL = imageURL
|
||||
self.bannerImageURL = bannerImageURL
|
||||
self.datePublished = datePublished
|
||||
self.dateModified = dateModified
|
||||
self.dateArrived = dateArrived
|
||||
self.authors = authors
|
||||
self.tags = tags
|
||||
self.attachments = attachments
|
||||
|
||||
self.hashValue = account.hashValue + feedID.hashValue + uniqueID.hashValue
|
||||
}
|
||||
|
||||
public class func ==(lhs: Article, rhs: Article) -> Bool {
|
||||
|
||||
return lhs === rhs
|
||||
}
|
||||
}
|
||||
|
||||
public extension Article {
|
||||
|
||||
public var logicalDatePublished: Date? {
|
||||
get {
|
||||
return (datePublished ?? dateModified) && status?.dateArrived
|
||||
}
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
//
|
||||
// ArticleProtocol.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 4/23/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol Article: class {
|
||||
|
||||
var account: Account? {get}
|
||||
var feedID: String {get}
|
||||
var feed: Feed? {get}
|
||||
var articleID: String {get}
|
||||
var status: ArticleStatus! {get}
|
||||
|
||||
var guid: String? {get}
|
||||
var title: String? {get}
|
||||
var body: String? {get}
|
||||
var link: String? {get}
|
||||
var permalink: String? {get}
|
||||
var author: String? {get}
|
||||
|
||||
var datePublished: Date? {get}
|
||||
var logicalDatePublished: Date {get} //datePublished or something reasonable.
|
||||
var dateModified: Date? {get}
|
||||
}
|
||||
|
||||
public extension Article {
|
||||
|
||||
var feed: Feed? {
|
||||
get {
|
||||
return account?.existingFeedWithID(feedID)
|
||||
}
|
||||
}
|
||||
|
||||
var logicalDatePublished: Date {
|
||||
get {
|
||||
if let d = datePublished {
|
||||
return d
|
||||
}
|
||||
if let d = dateModified {
|
||||
return d
|
||||
}
|
||||
return status.dateArrived as Date
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func articleArraysAreIdentical(array1: [Article], array2: [Article]) -> Bool {
|
||||
|
||||
if array1.count != array2.count {
|
||||
return false
|
||||
}
|
||||
|
||||
var index = 0
|
||||
for oneItem in array1 {
|
||||
if oneItem !== array2[index] {
|
||||
return false
|
||||
}
|
||||
index = index + 1
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
32
Frameworks/DataModel/Attachment.swift
Normal file
32
Frameworks/DataModel/Attachment.swift
Normal file
@ -0,0 +1,32 @@
|
||||
//
|
||||
// Attachment.swift
|
||||
// DataModel
|
||||
//
|
||||
// Created by Brent Simmons on 7/1/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct Attachment: Equatable {
|
||||
|
||||
public let url: String?
|
||||
public let mimeType: String?
|
||||
public let title: String?
|
||||
public let sizeInBytes: Int?
|
||||
public let durationInSeconds: Int?
|
||||
|
||||
init(url: String?, mimeType: String?, title: String?, sizeInBytes: Int?, durationInSeconds: Int?) {
|
||||
|
||||
self.url = url
|
||||
self.mimeType = mimeType
|
||||
self.title = title
|
||||
self.sizeInBytes = sizeInBytes
|
||||
self.durationInSeconds = durationInSeconds
|
||||
}
|
||||
|
||||
public class func ==(lhs: Attachment, rhs: Attachment) -> Bool {
|
||||
|
||||
return lhs.url == rhs.url && lhs.mimeType == rhs.mimeType && lhs.title == rhs.title && lhs.sizeInBytes == rhs.sizeInBytes && lhs.durationInSeconds == rhs.durationInSeconds
|
||||
}
|
||||
}
|
39
Frameworks/DataModel/Author.swift
Normal file
39
Frameworks/DataModel/Author.swift
Normal file
@ -0,0 +1,39 @@
|
||||
//
|
||||
// Author.swift
|
||||
// DataModel
|
||||
//
|
||||
// Created by Brent Simmons on 7/1/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct Author: Hashable {
|
||||
|
||||
public let name: String?
|
||||
public let url: String?
|
||||
public let avatarURL: String?
|
||||
public let emailAddress: String?
|
||||
|
||||
public init?(name: String?, url: String?, avatarURL: String?, emailAddress: String?) {
|
||||
|
||||
if name == nil && url == nil && emailAddress == nil {
|
||||
return nil
|
||||
}
|
||||
self.name = name
|
||||
self.url = url
|
||||
self.avatarURL = avatarURL
|
||||
self.emailAddress = emailAddress
|
||||
|
||||
let s = name ?? ""
|
||||
s += url ?? ""
|
||||
s += avatarURL ?? ""
|
||||
s += emailAddres ?? ""
|
||||
self.hashValue = s.hashValue
|
||||
}
|
||||
|
||||
public class func ==(lhs: Author, rhs: Author) {
|
||||
|
||||
return lhs.hashValue == rhs.hashValue && lhs.name == rhs.name && lhs.url == rhs.url && lhs.avatarURL == rhs.avatarURL && lhs.emailAddress == rhs.emailAddress
|
||||
}
|
||||
}
|
@ -18,11 +18,8 @@ public func FolderPostChildrenDidChangeNotification(_ folder: Folder) {
|
||||
NotificationCenter.default.post(name: NSNotification.Name(rawValue: FolderChildrenDidChangeNotification), object: folder)
|
||||
}
|
||||
|
||||
public protocol Folder: class, UnreadCountProvider, DisplayNameProvider {
|
||||
public protocol Container: class {
|
||||
|
||||
// TODO: get rid of children, flattenedFeeds, and some default implementations in favor of faster, specific ones.
|
||||
|
||||
// var children: NSSet {get}
|
||||
var account: Account? {get}
|
||||
|
||||
var hasAtLeastOneFeed: Bool {get} //Recursive
|
@ -9,29 +9,41 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
8439773E1DA07F2400F0FCBD /* RSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8439773D1DA07F2400F0FCBD /* RSCore.framework */; };
|
||||
8471A2CC1ED4CEEE008F099E /* AccountProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8471A2C81ED4CEEE008F099E /* AccountProtocol.swift */; };
|
||||
8471A2CD1ED4CEEE008F099E /* ArticleProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8471A2C91ED4CEEE008F099E /* ArticleProtocol.swift */; };
|
||||
8471A2CE1ED4CEEE008F099E /* ArticleStatusProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8471A2CA1ED4CEEE008F099E /* ArticleStatusProtocol.swift */; };
|
||||
8471A2CF1ED4CEEE008F099E /* BatchUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8471A2CB1ED4CEEE008F099E /* BatchUpdates.swift */; };
|
||||
8471A2D51ED4CEFA008F099E /* DisplayNameProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8471A2D01ED4CEFA008F099E /* DisplayNameProviderProtocol.swift */; };
|
||||
8471A2D61ED4CEFA008F099E /* FeedProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8471A2D11ED4CEFA008F099E /* FeedProtocol.swift */; };
|
||||
8471A2D71ED4CEFA008F099E /* FolderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8471A2D21ED4CEFA008F099E /* FolderProtocol.swift */; };
|
||||
8471A2D71ED4CEFA008F099E /* Container.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8471A2D21ED4CEFA008F099E /* Container.swift */; };
|
||||
8471A2D81ED4CEFA008F099E /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8471A2D31ED4CEFA008F099E /* Notifications.swift */; };
|
||||
8471A2D91ED4CEFA008F099E /* UnreadCountProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8471A2D41ED4CEFA008F099E /* UnreadCountProviderProtocol.swift */; };
|
||||
84F466A61F08389400225386 /* Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F466A51F08389400225386 /* Account.swift */; };
|
||||
84F466A81F083A2A00225386 /* Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F466A71F083A2A00225386 /* Feed.swift */; };
|
||||
84F466AA1F083B7A00225386 /* OPMLRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F466A91F083B7A00225386 /* OPMLRepresentable.swift */; };
|
||||
84F466AC1F083F2600225386 /* Article.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F466AB1F083F2600225386 /* Article.swift */; };
|
||||
84F466AE1F08435800225386 /* Author.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F466AD1F08435800225386 /* Author.swift */; };
|
||||
84F466B01F0862AF00225386 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F466AF1F0862AF00225386 /* Attachment.swift */; };
|
||||
84F466B21F0863D700225386 /* AccountDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F466B11F0863D700225386 /* AccountDelegate.swift */; };
|
||||
84F466B41F08725700225386 /* Folder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F466B31F08725700225386 /* Folder.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
8439773D1DA07F2400F0FCBD /* RSCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSCore.framework; path = "../../../../../Library/Developer/Xcode/DerivedData/Rainier-cidsoqwawkdqqphkdtrqrojskege/Build/Products/Debug/RSCore.framework"; sourceTree = "<group>"; };
|
||||
8471A2C81ED4CEEE008F099E /* AccountProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountProtocol.swift; sourceTree = "<group>"; };
|
||||
8471A2C91ED4CEEE008F099E /* ArticleProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleProtocol.swift; sourceTree = "<group>"; };
|
||||
8471A2CA1ED4CEEE008F099E /* ArticleStatusProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleStatusProtocol.swift; sourceTree = "<group>"; };
|
||||
8471A2CB1ED4CEEE008F099E /* BatchUpdates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatchUpdates.swift; sourceTree = "<group>"; };
|
||||
8471A2D01ED4CEFA008F099E /* DisplayNameProviderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayNameProviderProtocol.swift; sourceTree = "<group>"; };
|
||||
8471A2D11ED4CEFA008F099E /* FeedProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedProtocol.swift; sourceTree = "<group>"; };
|
||||
8471A2D21ED4CEFA008F099E /* FolderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FolderProtocol.swift; sourceTree = "<group>"; };
|
||||
8471A2D21ED4CEFA008F099E /* Container.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Container.swift; sourceTree = "<group>"; };
|
||||
8471A2D31ED4CEFA008F099E /* Notifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = "<group>"; };
|
||||
8471A2D41ED4CEFA008F099E /* UnreadCountProviderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnreadCountProviderProtocol.swift; sourceTree = "<group>"; };
|
||||
8471A2DA1ED4CF01008F099E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
84C7AE921D68C558009FB883 /* DataModel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DataModel.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
84F466A51F08389400225386 /* Account.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Account.swift; sourceTree = "<group>"; };
|
||||
84F466A71F083A2A00225386 /* Feed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Feed.swift; sourceTree = "<group>"; };
|
||||
84F466A91F083B7A00225386 /* OPMLRepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OPMLRepresentable.swift; sourceTree = "<group>"; };
|
||||
84F466AB1F083F2600225386 /* Article.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Article.swift; sourceTree = "<group>"; };
|
||||
84F466AD1F08435800225386 /* Author.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Author.swift; sourceTree = "<group>"; };
|
||||
84F466AF1F0862AF00225386 /* Attachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = "<group>"; };
|
||||
84F466B11F0863D700225386 /* AccountDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountDelegate.swift; sourceTree = "<group>"; };
|
||||
84F466B31F08725700225386 /* Folder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Folder.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -57,13 +69,19 @@
|
||||
84C7AE881D68C558009FB883 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84F466A51F08389400225386 /* Account.swift */,
|
||||
84F466B11F0863D700225386 /* AccountDelegate.swift */,
|
||||
84F466B31F08725700225386 /* Folder.swift */,
|
||||
84F466A71F083A2A00225386 /* Feed.swift */,
|
||||
84F466AB1F083F2600225386 /* Article.swift */,
|
||||
84F466AD1F08435800225386 /* Author.swift */,
|
||||
84F466AF1F0862AF00225386 /* Attachment.swift */,
|
||||
8471A2C81ED4CEEE008F099E /* AccountProtocol.swift */,
|
||||
8471A2C91ED4CEEE008F099E /* ArticleProtocol.swift */,
|
||||
8471A2CA1ED4CEEE008F099E /* ArticleStatusProtocol.swift */,
|
||||
8471A2CB1ED4CEEE008F099E /* BatchUpdates.swift */,
|
||||
8471A2D01ED4CEFA008F099E /* DisplayNameProviderProtocol.swift */,
|
||||
8471A2D11ED4CEFA008F099E /* FeedProtocol.swift */,
|
||||
8471A2D21ED4CEFA008F099E /* FolderProtocol.swift */,
|
||||
84F466A91F083B7A00225386 /* OPMLRepresentable.swift */,
|
||||
8471A2D21ED4CEFA008F099E /* Container.swift */,
|
||||
8471A2D31ED4CEFA008F099E /* Notifications.swift */,
|
||||
8471A2D41ED4CEFA008F099E /* UnreadCountProviderProtocol.swift */,
|
||||
8471A2DA1ED4CF01008F099E /* Info.plist */,
|
||||
@ -159,15 +177,21 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84F466AC1F083F2600225386 /* Article.swift in Sources */,
|
||||
8471A2CC1ED4CEEE008F099E /* AccountProtocol.swift in Sources */,
|
||||
8471A2D61ED4CEFA008F099E /* FeedProtocol.swift in Sources */,
|
||||
84F466A81F083A2A00225386 /* Feed.swift in Sources */,
|
||||
84F466A61F08389400225386 /* Account.swift in Sources */,
|
||||
8471A2CF1ED4CEEE008F099E /* BatchUpdates.swift in Sources */,
|
||||
8471A2CE1ED4CEEE008F099E /* ArticleStatusProtocol.swift in Sources */,
|
||||
84F466B01F0862AF00225386 /* Attachment.swift in Sources */,
|
||||
8471A2D91ED4CEFA008F099E /* UnreadCountProviderProtocol.swift in Sources */,
|
||||
8471A2CD1ED4CEEE008F099E /* ArticleProtocol.swift in Sources */,
|
||||
84F466B21F0863D700225386 /* AccountDelegate.swift in Sources */,
|
||||
8471A2D51ED4CEFA008F099E /* DisplayNameProviderProtocol.swift in Sources */,
|
||||
84F466AE1F08435800225386 /* Author.swift in Sources */,
|
||||
84F466B41F08725700225386 /* Folder.swift in Sources */,
|
||||
8471A2D81ED4CEFA008F099E /* Notifications.swift in Sources */,
|
||||
8471A2D71ED4CEFA008F099E /* FolderProtocol.swift in Sources */,
|
||||
84F466AA1F083B7A00225386 /* OPMLRepresentable.swift in Sources */,
|
||||
8471A2D71ED4CEFA008F099E /* Container.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
66
Frameworks/DataModel/Feed.swift
Normal file
66
Frameworks/DataModel/Feed.swift
Normal file
@ -0,0 +1,66 @@
|
||||
//
|
||||
// Feed.swift
|
||||
// DataModel
|
||||
//
|
||||
// Created by Brent Simmons on 7/1/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
|
||||
public final class Feed: UnreadCountProvider, DisplayNameProvider, Hashable {
|
||||
|
||||
public let account: Account
|
||||
public let url: String
|
||||
public let feedID: String
|
||||
|
||||
public var homePageURL: String?
|
||||
public var name: String?
|
||||
public var editedName: String?
|
||||
public var nameForDisplay: String {
|
||||
get {
|
||||
return (editedName ?? name) ?? NSLocalizedString("Untitled", comment: "Feed name")
|
||||
}
|
||||
}
|
||||
|
||||
public var articles = Set<Article>()
|
||||
public var accountInfo: [String: Any]? //If account needs to store more data
|
||||
|
||||
public init(account: Account, url: String, feedID: String) {
|
||||
|
||||
self.account = account
|
||||
self.url = url
|
||||
self.feedID = feedID
|
||||
self.hashValue = account.hashValue + url.hashValue + feedID.hashValue
|
||||
}
|
||||
|
||||
public fetchArticles() -> Set<Article> {
|
||||
|
||||
articles = account.fetchArticles(self)
|
||||
return articles
|
||||
}
|
||||
|
||||
public class func ==(lhs: Feed, rhs: Feed) -> Bool {
|
||||
|
||||
return lhs === rhs
|
||||
}
|
||||
}
|
||||
|
||||
public extension Feed: OPMLRepresentable {
|
||||
|
||||
func OPMLString(indentLevel: Int) -> String {
|
||||
|
||||
let escapedName = nameForDisplay.rs_stringByEscapingSpecialXMLCharacters()
|
||||
var escapedHomePageURL = ""
|
||||
if let homePageURL = homePageURL {
|
||||
escapedHomePageURL = homePageURL.rs_stringByEscapingSpecialXMLCharacters()
|
||||
}
|
||||
let escapedFeedURL = url.rs_stringByEscapingSpecialXMLCharacters()
|
||||
|
||||
var s = "<outline text=\"\(escapedName)\" title=\"\(escapedName)\" description=\"\" type=\"rss\" version=\"RSS\" htmlUrl=\"\(escapedHomePageURL)\" xmlUrl=\"\(escapedFeedURL)\"/>\n"
|
||||
s = s.rs_string(byPrependingNumberOfTabs: indentLevel)
|
||||
|
||||
return s
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
//
|
||||
// FeedProtocol.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 4/23/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
|
||||
public protocol Feed: class, UnreadCountProvider, DisplayNameProvider {
|
||||
|
||||
var account: Account {get}
|
||||
var url: String {get}
|
||||
var feedID: String {get}
|
||||
var homePageURL: String? {get}
|
||||
var name: String? {get}
|
||||
var editedName: String? {get}
|
||||
var nameForDisplay: String {get}
|
||||
// var articles: NSSet {get}
|
||||
|
||||
init(account: Account, url: String, feedID: String)
|
||||
|
||||
// Exporting OPML.
|
||||
func opmlString(indentLevel: Int) -> String
|
||||
}
|
||||
|
||||
public extension Feed {
|
||||
|
||||
func opmlString(indentLevel: Int) -> String {
|
||||
|
||||
let escapedName = nameForDisplay.rs_stringByEscapingSpecialXMLCharacters()
|
||||
var escapedHomePageURL = ""
|
||||
if let homePageURL = homePageURL {
|
||||
escapedHomePageURL = homePageURL.rs_stringByEscapingSpecialXMLCharacters()
|
||||
}
|
||||
let escapedFeedURL = url.rs_stringByEscapingSpecialXMLCharacters()
|
||||
|
||||
var s = "<outline text=\"\(escapedName)\" title=\"\(escapedName)\" description=\"\" type=\"rss\" version=\"RSS\" htmlUrl=\"\(escapedHomePageURL)\" xmlUrl=\"\(escapedFeedURL)\"/>\n"
|
||||
s = s.rs_string(byPrependingNumberOfTabs: indentLevel)
|
||||
|
||||
return s
|
||||
}
|
||||
}
|
14
Frameworks/DataModel/Folder.swift
Normal file
14
Frameworks/DataModel/Folder.swift
Normal file
@ -0,0 +1,14 @@
|
||||
//
|
||||
// Folder.swift
|
||||
// DataModel
|
||||
//
|
||||
// Created by Brent Simmons on 7/1/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class Folder: Container {
|
||||
|
||||
|
||||
}
|
14
Frameworks/DataModel/OPMLRepresentable.swift
Normal file
14
Frameworks/DataModel/OPMLRepresentable.swift
Normal file
@ -0,0 +1,14 @@
|
||||
//
|
||||
// OPMLRepresentable.swift
|
||||
// DataModel
|
||||
//
|
||||
// Created by Brent Simmons on 7/1/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol OPMLRepresentable {
|
||||
|
||||
func OPMLString(indentLevel: Int) -> String
|
||||
}
|
585
Frameworks/Feedbin/Feedbin.xcodeproj/project.pbxproj
Normal file
585
Frameworks/Feedbin/Feedbin.xcodeproj/project.pbxproj
Normal file
@ -0,0 +1,585 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
84F466721F08255100225386 /* Feedbin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F466681F08255100225386 /* Feedbin.framework */; };
|
||||
84F466771F08255100225386 /* FeedbinTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F466761F08255100225386 /* FeedbinTests.swift */; };
|
||||
84F466791F08255100225386 /* Feedbin.h in Headers */ = {isa = PBXBuildFile; fileRef = 84F4666B1F08255100225386 /* Feedbin.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84F466A01F0825A000225386 /* DataModel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F4669F1F08258300225386 /* DataModel.framework */; };
|
||||
84F466A11F0825A000225386 /* RSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F466891F08256F00225386 /* RSCore.framework */; };
|
||||
84F466A21F0825A000225386 /* RSDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F466951F08257700225386 /* RSDatabase.framework */; };
|
||||
84F466A41F0825B400225386 /* FeedbinAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F466A31F0825B400225386 /* FeedbinAccount.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
84F466731F08255100225386 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 84F4665F1F08255100225386 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 84F466671F08255100225386;
|
||||
remoteInfo = Feedbin;
|
||||
};
|
||||
84F466881F08256F00225386 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 84F466821F08256F00225386 /* RSCore.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 84CFF4F41AC3C69700CEA6C8;
|
||||
remoteInfo = RSCore;
|
||||
};
|
||||
84F4668A1F08256F00225386 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 84F466821F08256F00225386 /* RSCore.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 84CFF4FF1AC3C69700CEA6C8;
|
||||
remoteInfo = RSCoreTests;
|
||||
};
|
||||
84F4668C1F08256F00225386 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 84F466821F08256F00225386 /* RSCore.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 842DD7BC1E14993900E061EB;
|
||||
remoteInfo = RSCoreiOS;
|
||||
};
|
||||
84F466941F08257700225386 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 84F4668E1F08257700225386 /* RSDatabase.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 84F22C551B52E0D9000060CE;
|
||||
remoteInfo = RSDatabase;
|
||||
};
|
||||
84F466961F08257700225386 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 84F4668E1F08257700225386 /* RSDatabase.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 84F22C5F1B52E0D9000060CE;
|
||||
remoteInfo = RSDatabaseTests;
|
||||
};
|
||||
84F466981F08257700225386 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 84F4668E1F08257700225386 /* RSDatabase.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 8400ABF71E0CFBD800AA7C57;
|
||||
remoteInfo = RSDatabaseiOS;
|
||||
};
|
||||
84F4669E1F08258300225386 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 84F4669A1F08258300225386 /* DataModel.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 84C7AE921D68C558009FB883;
|
||||
remoteInfo = DataModel;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
84F466681F08255100225386 /* Feedbin.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Feedbin.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
84F4666B1F08255100225386 /* Feedbin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Feedbin.h; sourceTree = "<group>"; };
|
||||
84F4666C1F08255100225386 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
84F466711F08255100225386 /* FeedbinTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FeedbinTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
84F466761F08255100225386 /* FeedbinTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinTests.swift; sourceTree = "<group>"; };
|
||||
84F466781F08255100225386 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
84F466821F08256F00225386 /* RSCore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSCore.xcodeproj; path = ../RSCore/RSCore.xcodeproj; sourceTree = "<group>"; };
|
||||
84F4668E1F08257700225386 /* RSDatabase.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSDatabase.xcodeproj; path = ../RSDatabase/RSDatabase.xcodeproj; sourceTree = "<group>"; };
|
||||
84F4669A1F08258300225386 /* DataModel.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = DataModel.xcodeproj; path = ../DataModel/DataModel.xcodeproj; sourceTree = "<group>"; };
|
||||
84F466A31F0825B400225386 /* FeedbinAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbinAccount.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
84F466641F08255100225386 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84F466A01F0825A000225386 /* DataModel.framework in Frameworks */,
|
||||
84F466A11F0825A000225386 /* RSCore.framework in Frameworks */,
|
||||
84F466A21F0825A000225386 /* RSDatabase.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
84F4666E1F08255100225386 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84F466721F08255100225386 /* Feedbin.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
84F4665E1F08255100225386 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84F4666A1F08255100225386 /* Feedbin */,
|
||||
84F466751F08255100225386 /* FeedbinTests */,
|
||||
84F466691F08255100225386 /* Products */,
|
||||
84F466821F08256F00225386 /* RSCore.xcodeproj */,
|
||||
84F4668E1F08257700225386 /* RSDatabase.xcodeproj */,
|
||||
84F4669A1F08258300225386 /* DataModel.xcodeproj */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84F466691F08255100225386 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84F466681F08255100225386 /* Feedbin.framework */,
|
||||
84F466711F08255100225386 /* FeedbinTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84F4666A1F08255100225386 /* Feedbin */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84F4666B1F08255100225386 /* Feedbin.h */,
|
||||
84F466A31F0825B400225386 /* FeedbinAccount.swift */,
|
||||
84F4666C1F08255100225386 /* Info.plist */,
|
||||
);
|
||||
path = Feedbin;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84F466751F08255100225386 /* FeedbinTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84F466761F08255100225386 /* FeedbinTests.swift */,
|
||||
84F466781F08255100225386 /* Info.plist */,
|
||||
);
|
||||
path = FeedbinTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84F466831F08256F00225386 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84F466891F08256F00225386 /* RSCore.framework */,
|
||||
84F4668B1F08256F00225386 /* RSCoreTests.xctest */,
|
||||
84F4668D1F08256F00225386 /* RSCore.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84F4668F1F08257700225386 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84F466951F08257700225386 /* RSDatabase.framework */,
|
||||
84F466971F08257700225386 /* RSDatabaseTests.xctest */,
|
||||
84F466991F08257700225386 /* RSDatabase.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84F4669B1F08258300225386 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84F4669F1F08258300225386 /* DataModel.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
84F466651F08255100225386 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84F466791F08255100225386 /* Feedbin.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
84F466671F08255100225386 /* Feedbin */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 84F4667C1F08255100225386 /* Build configuration list for PBXNativeTarget "Feedbin" */;
|
||||
buildPhases = (
|
||||
84F466631F08255100225386 /* Sources */,
|
||||
84F466641F08255100225386 /* Frameworks */,
|
||||
84F466651F08255100225386 /* Headers */,
|
||||
84F466661F08255100225386 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Feedbin;
|
||||
productName = Feedbin;
|
||||
productReference = 84F466681F08255100225386 /* Feedbin.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
84F466701F08255100225386 /* FeedbinTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 84F4667F1F08255100225386 /* Build configuration list for PBXNativeTarget "FeedbinTests" */;
|
||||
buildPhases = (
|
||||
84F4666D1F08255100225386 /* Sources */,
|
||||
84F4666E1F08255100225386 /* Frameworks */,
|
||||
84F4666F1F08255100225386 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
84F466741F08255100225386 /* PBXTargetDependency */,
|
||||
);
|
||||
name = FeedbinTests;
|
||||
productName = FeedbinTests;
|
||||
productReference = 84F466711F08255100225386 /* FeedbinTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
84F4665F1F08255100225386 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0830;
|
||||
LastUpgradeCheck = 0830;
|
||||
ORGANIZATIONNAME = "Ranchero Software, LLC";
|
||||
TargetAttributes = {
|
||||
84F466671F08255100225386 = {
|
||||
CreatedOnToolsVersion = 8.3;
|
||||
DevelopmentTeam = M8L2WTLA8W;
|
||||
LastSwiftMigration = 0830;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
84F466701F08255100225386 = {
|
||||
CreatedOnToolsVersion = 8.3;
|
||||
DevelopmentTeam = M8L2WTLA8W;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 84F466621F08255100225386 /* Build configuration list for PBXProject "Feedbin" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 84F4665E1F08255100225386;
|
||||
productRefGroup = 84F466691F08255100225386 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectReferences = (
|
||||
{
|
||||
ProductGroup = 84F4669B1F08258300225386 /* Products */;
|
||||
ProjectRef = 84F4669A1F08258300225386 /* DataModel.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 84F466831F08256F00225386 /* Products */;
|
||||
ProjectRef = 84F466821F08256F00225386 /* RSCore.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 84F4668F1F08257700225386 /* Products */;
|
||||
ProjectRef = 84F4668E1F08257700225386 /* RSDatabase.xcodeproj */;
|
||||
},
|
||||
);
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
84F466671F08255100225386 /* Feedbin */,
|
||||
84F466701F08255100225386 /* FeedbinTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXReferenceProxy section */
|
||||
84F466891F08256F00225386 /* RSCore.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
path = RSCore.framework;
|
||||
remoteRef = 84F466881F08256F00225386 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
84F4668B1F08256F00225386 /* RSCoreTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = RSCoreTests.xctest;
|
||||
remoteRef = 84F4668A1F08256F00225386 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
84F4668D1F08256F00225386 /* RSCore.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
path = RSCore.framework;
|
||||
remoteRef = 84F4668C1F08256F00225386 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
84F466951F08257700225386 /* RSDatabase.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
path = RSDatabase.framework;
|
||||
remoteRef = 84F466941F08257700225386 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
84F466971F08257700225386 /* RSDatabaseTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = RSDatabaseTests.xctest;
|
||||
remoteRef = 84F466961F08257700225386 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
84F466991F08257700225386 /* RSDatabase.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
path = RSDatabase.framework;
|
||||
remoteRef = 84F466981F08257700225386 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
84F4669F1F08258300225386 /* DataModel.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
path = DataModel.framework;
|
||||
remoteRef = 84F4669E1F08258300225386 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
/* End PBXReferenceProxy section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
84F466661F08255100225386 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
84F4666F1F08255100225386 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
84F466631F08255100225386 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84F466A41F0825B400225386 /* FeedbinAccount.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
84F4666D1F08255100225386 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84F466771F08255100225386 /* FeedbinTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
84F466741F08255100225386 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 84F466671F08255100225386 /* Feedbin */;
|
||||
targetProxy = 84F466731F08255100225386 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
84F4667A1F08255100225386 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
84F4667B1F08255100225386 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
84F4667D1F08255100225386 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = M8L2WTLA8W;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_VERSION = A;
|
||||
INFOPLIST_FILE = Feedbin/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.Feedbin;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
84F4667E1F08255100225386 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = M8L2WTLA8W;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_VERSION = A;
|
||||
INFOPLIST_FILE = Feedbin/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.Feedbin;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
84F466801F08255100225386 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = M8L2WTLA8W;
|
||||
INFOPLIST_FILE = FeedbinTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.FeedbinTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
84F466811F08255100225386 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEVELOPMENT_TEAM = M8L2WTLA8W;
|
||||
INFOPLIST_FILE = FeedbinTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.FeedbinTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
84F466621F08255100225386 /* Build configuration list for PBXProject "Feedbin" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
84F4667A1F08255100225386 /* Debug */,
|
||||
84F4667B1F08255100225386 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
84F4667C1F08255100225386 /* Build configuration list for PBXNativeTarget "Feedbin" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
84F4667D1F08255100225386 /* Debug */,
|
||||
84F4667E1F08255100225386 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
};
|
||||
84F4667F1F08255100225386 /* Build configuration list for PBXNativeTarget "FeedbinTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
84F466801F08255100225386 /* Debug */,
|
||||
84F466811F08255100225386 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 84F4665F1F08255100225386 /* Project object */;
|
||||
}
|
7
Frameworks/Feedbin/Feedbin.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
Frameworks/Feedbin/Feedbin.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:Feedbin.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
19
Frameworks/Feedbin/Feedbin/Feedbin.h
Normal file
19
Frameworks/Feedbin/Feedbin/Feedbin.h
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Feedbin.h
|
||||
// Feedbin
|
||||
//
|
||||
// Created by Brent Simmons on 7/1/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
//! Project version number for Feedbin.
|
||||
FOUNDATION_EXPORT double FeedbinVersionNumber;
|
||||
|
||||
//! Project version string for Feedbin.
|
||||
FOUNDATION_EXPORT const unsigned char FeedbinVersionString[];
|
||||
|
||||
// In this header, you should import all the public headers of your framework using statements like #import <Feedbin/PublicHeader.h>
|
||||
|
||||
|
71
Frameworks/Feedbin/Feedbin/FeedbinAccount.swift
Normal file
71
Frameworks/Feedbin/Feedbin/FeedbinAccount.swift
Normal file
@ -0,0 +1,71 @@
|
||||
//
|
||||
// FeedbinAccount.swift
|
||||
// Feedbin
|
||||
//
|
||||
// Created by Brent Simmons on 7/1/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import DataModel
|
||||
|
||||
public let feedbinAccountType = "Feedbin"
|
||||
|
||||
public final class FeedbinAccount: Account, PlistProvider {
|
||||
|
||||
public let identifier: String
|
||||
public let type = feedbinAccountType
|
||||
public var nameForDisplay = "Feedbin"
|
||||
|
||||
private let settingsFile: String
|
||||
private let dataFolder: String
|
||||
private let diskSaver: DiskSaver
|
||||
fileprivate let database: FeedbinDatabase
|
||||
fileprivate var feeds = Set<FeedbinFeed>()
|
||||
|
||||
public var account: Account? {
|
||||
get {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
public var unreadCount = 0 {
|
||||
didSet {
|
||||
postUnreadCountDidChangeNotification()
|
||||
}
|
||||
}
|
||||
|
||||
required public init(settingsFile: String, dataFolder: String, identifier: String) {
|
||||
|
||||
self.settingsFile = settingsFile
|
||||
self.dataFolder = dataFolder
|
||||
self.identifier = identifier
|
||||
|
||||
let databaseFile = (dataFolder as NSString).appendingPathComponent("FeedbinArticles0.db")
|
||||
self.localDatabase = FeedbinDatabase(databaseFile: databaseFile)
|
||||
self.diskSaver = DiskSaver(path: settingsFile)
|
||||
|
||||
self.localDatabase.account = self
|
||||
self.diskSaver.delegate = self
|
||||
self.refresher.account = self
|
||||
|
||||
pullSettingsAndTopLevelItemsFromFile()
|
||||
|
||||
self.database.startup()
|
||||
|
||||
updateUnreadCountsForTopLevelFolders()
|
||||
updateUnreadCount()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(articleStatusesDidChange(_:)), name: .ArticleStatusesDidChange, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(refreshProgressDidChange(_:)), name: .DownloadProgressDidChange, object: nil)
|
||||
|
||||
DispatchQueue.main.async() { () -> Void in
|
||||
self.updateUnreadCounts(feedIDs: self.flattenedFeedIDs)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
26
Frameworks/Feedbin/Feedbin/Info.plist
Normal file
26
Frameworks/Feedbin/Feedbin/Info.plist
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2017 Ranchero Software, LLC. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
36
Frameworks/Feedbin/FeedbinTests/FeedbinTests.swift
Normal file
36
Frameworks/Feedbin/FeedbinTests/FeedbinTests.swift
Normal file
@ -0,0 +1,36 @@
|
||||
//
|
||||
// FeedbinTests.swift
|
||||
// FeedbinTests
|
||||
//
|
||||
// Created by Brent Simmons on 7/1/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import Feedbin
|
||||
|
||||
class FeedbinTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testExample() {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measure {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
22
Frameworks/Feedbin/FeedbinTests/Info.plist
Normal file
22
Frameworks/Feedbin/FeedbinTests/Info.plist
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import RSXML
|
||||
import RSParser
|
||||
import RSWeb
|
||||
import DataModel
|
||||
|
||||
@ -130,7 +130,7 @@ public final class LocalAccount: Account, PlistProvider {
|
||||
|
||||
public func importOPML(_ opmlDocument: Any) {
|
||||
|
||||
if let opmlItems = (opmlDocument as? RSOPMLDocument)?.children as? [RSOPMLItem] {
|
||||
if let opmlItems = (opmlDocument as? RSOPMLDocument)?.children {
|
||||
performDataModelBatchUpdates {
|
||||
importOPMLItems(opmlItems)
|
||||
}
|
||||
@ -369,7 +369,7 @@ public final class LocalAccount: Account, PlistProvider {
|
||||
|
||||
// MARK: Updating
|
||||
|
||||
func update(_ feed: LocalFeed, parsedFeed: RSParsedFeed, completionHandler: @escaping RSVoidCompletionBlock) {
|
||||
func update(_ feed: LocalFeed, parsedFeed: ParsedFeed, completionHandler: @escaping RSVoidCompletionBlock) {
|
||||
|
||||
if let titleFromFeed = parsedFeed.title {
|
||||
if feed.name != titleFromFeed {
|
||||
@ -377,7 +377,7 @@ public final class LocalAccount: Account, PlistProvider {
|
||||
self.diskSaver.dirty = true
|
||||
}
|
||||
}
|
||||
if let linkFromFeed = parsedFeed.link {
|
||||
if let linkFromFeed = parsedFeed.homePageURL {
|
||||
if feed.homePageURL != linkFromFeed {
|
||||
feed.homePageURL = linkFromFeed
|
||||
self.diskSaver.dirty = true
|
||||
@ -562,7 +562,7 @@ private extension LocalAccount {
|
||||
|
||||
for oneItem in items {
|
||||
|
||||
if oneItem.isFolder, let childItems = oneItem.children as? [RSOPMLItem] {
|
||||
if oneItem.isFolder, let childItems = oneItem.children {
|
||||
importOPMLTopLevelFolder(oneItem, childItems)
|
||||
}
|
||||
}
|
||||
@ -592,7 +592,7 @@ private extension LocalAccount {
|
||||
|
||||
for oneItem in items {
|
||||
|
||||
if oneItem.isFolder, let childItems = oneItem.children as? [RSOPMLItem] {
|
||||
if oneItem.isFolder, let childItems = oneItem.children {
|
||||
importOPMLItemsIntoFolder(childItems, folder)
|
||||
continue
|
||||
}
|
||||
|
@ -23,8 +23,9 @@
|
||||
84D37AD81D68CFD500110870 /* DataModel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D37AD71D68CFD500110870 /* DataModel.framework */; };
|
||||
84D37ADA1D68CFEE00110870 /* RSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D37AD91D68CFEE00110870 /* RSCore.framework */; };
|
||||
84D37ADC1D68CFFC00110870 /* RSDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D37ADB1D68CFFC00110870 /* RSDatabase.framework */; };
|
||||
84D37ADE1D68D00700110870 /* RSXML.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D37ADD1D68D00700110870 /* RSXML.framework */; };
|
||||
84D37AE21D68D07700110870 /* RSWeb.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84D37AE11D68D07700110870 /* RSWeb.framework */; };
|
||||
84F466591F0352F000225386 /* RSParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F466581F0352F000225386 /* RSParser.framework */; };
|
||||
84F4665B1F07321400225386 /* LocalAuthor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F4665A1F07321400225386 /* LocalAuthor.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -59,6 +60,8 @@
|
||||
84D37ADB1D68CFFC00110870 /* RSDatabase.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSDatabase.framework; path = "../../../../../Library/Developer/Xcode/DerivedData/Rainier-cidsoqwawkdqqphkdtrqrojskege/Build/Products/Debug/RSDatabase.framework"; sourceTree = "<group>"; };
|
||||
84D37ADD1D68D00700110870 /* RSXML.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSXML.framework; path = "../../../../../Library/Developer/Xcode/DerivedData/Rainier-cidsoqwawkdqqphkdtrqrojskege/Build/Products/Debug/RSXML.framework"; sourceTree = "<group>"; };
|
||||
84D37AE11D68D07700110870 /* RSWeb.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSWeb.framework; path = "../../../../../Library/Developer/Xcode/DerivedData/Rainier-cidsoqwawkdqqphkdtrqrojskege/Build/Products/Debug/RSWeb.framework"; sourceTree = "<group>"; };
|
||||
84F466581F0352F000225386 /* RSParser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSParser.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Evergreen-edcdqyvewhytnmcfaiesnqiqeynn/Build/Products/Debug/RSParser.framework"; sourceTree = "<group>"; };
|
||||
84F4665A1F07321400225386 /* LocalAuthor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalAuthor.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -66,8 +69,8 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84F466591F0352F000225386 /* RSParser.framework in Frameworks */,
|
||||
84D37AE21D68D07700110870 /* RSWeb.framework in Frameworks */,
|
||||
84D37ADE1D68D00700110870 /* RSXML.framework in Frameworks */,
|
||||
84D37ADC1D68CFFC00110870 /* RSDatabase.framework in Frameworks */,
|
||||
84D37ADA1D68CFEE00110870 /* RSCore.framework in Frameworks */,
|
||||
84D37AD81D68CFD500110870 /* DataModel.framework in Frameworks */,
|
||||
@ -104,6 +107,7 @@
|
||||
8471A2E01ED4CFF3008F099E /* LocalFolder.swift */,
|
||||
8471A2E21ED4CFFB008F099E /* LocalFeed.swift */,
|
||||
8471A2E41ED4D007008F099E /* LocalArticle.swift */,
|
||||
84F4665A1F07321400225386 /* LocalAuthor.swift */,
|
||||
8471A2E61ED4D012008F099E /* LocalArticleStatus.swift */,
|
||||
8471A2E81ED4D01B008F099E /* DiskDictionaryConstants.swift */,
|
||||
8442EB3F1D68C8B200D709AE /* Database */,
|
||||
@ -135,6 +139,7 @@
|
||||
84D37AD61D68CFD500110870 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84F466581F0352F000225386 /* RSParser.framework */,
|
||||
84D37AE11D68D07700110870 /* RSWeb.framework */,
|
||||
84D37ADD1D68D00700110870 /* RSXML.framework */,
|
||||
84D37ADB1D68CFFC00110870 /* RSDatabase.framework */,
|
||||
@ -265,6 +270,7 @@
|
||||
8471A2FE1ED4D0AD008F099E /* LocalStatusesManager.swift in Sources */,
|
||||
8471A2DD1ED4CFE4008F099E /* LocalAccount.swift in Sources */,
|
||||
8471A2DF1ED4CFEB008F099E /* LocalAccountRefresher.swift in Sources */,
|
||||
84F4665B1F07321400225386 /* LocalAuthor.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import RSXML
|
||||
import RSParser
|
||||
import RSWeb
|
||||
|
||||
final class LocalAccountRefresher: DownloadSessionDelegate {
|
||||
@ -67,8 +67,8 @@ final class LocalAccountRefresher: DownloadSessionDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
let xmlData = RSXMLData(data: data, urlString: feed.url)
|
||||
RSParseFeed(xmlData) { (parsedFeed, error) in
|
||||
let parserData = ParserData(url: feed.url, data: data)
|
||||
FeedParser.parse(parserData) { (parsedFeed, error) in
|
||||
|
||||
guard let account = self.account, let parsedFeed = parsedFeed, error == nil else {
|
||||
return
|
||||
@ -101,8 +101,8 @@ final class LocalAccountRefresher: DownloadSessionDelegate {
|
||||
}
|
||||
|
||||
if data.count > 4096 {
|
||||
let xmlData = RSXMLData(data: data, urlString: feed.url)
|
||||
return RSCanParseFeed(xmlData)
|
||||
let parserData = ParserData(url: feed.url, data: data)
|
||||
return FeedParser.canParse(parserData)
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -8,8 +8,9 @@
|
||||
|
||||
import Foundation
|
||||
import DataModel
|
||||
import RSParser
|
||||
|
||||
public final class LocalArticle: NSObject, Article {
|
||||
public final class LocalArticle: Article, Hashable {
|
||||
|
||||
public let account: Account?
|
||||
public let feedID: String
|
||||
@ -46,18 +47,12 @@ public final class LocalArticle: NSObject, Article {
|
||||
|
||||
private let _hash: Int
|
||||
|
||||
public override var hashValue: Int {
|
||||
get {
|
||||
return _hash
|
||||
}
|
||||
}
|
||||
|
||||
public init(account: Account, feedID: String, articleID: String) {
|
||||
|
||||
self.account = account
|
||||
self.feedID = feedID
|
||||
self.articleID = articleID
|
||||
self._hash = articleID.hashValue
|
||||
self._hash = databaseID.hashValue
|
||||
}
|
||||
|
||||
public override func isEqual(_ object: Any?) -> Bool {
|
||||
@ -84,7 +79,7 @@ public final class LocalArticle: NSObject, Article {
|
||||
|
||||
import RSCore
|
||||
import RSDatabase
|
||||
import RSXML
|
||||
import RSParser
|
||||
|
||||
// Database columns *and* value keys.
|
||||
|
||||
@ -103,18 +98,18 @@ private let mergeablePropertyNames = [articleGuidKey, articleTitleKey, articleBo
|
||||
|
||||
public extension LocalArticle {
|
||||
|
||||
convenience init(account: Account, feedID: String, parsedArticle: RSParsedArticle) {
|
||||
convenience init(account: Account, feedID: String, parsedItem: ParsedItem) {
|
||||
|
||||
self.init(account: account, feedID: feedID, articleID: parsedArticle.articleID)
|
||||
self.init(account: account, feedID: feedID, articleID: parsedItem.databaseID)
|
||||
|
||||
self.guid = parsedArticle.guid
|
||||
self.title = parsedArticle.title
|
||||
self.body = parsedArticle.body
|
||||
self.datePublished = parsedArticle.datePublished
|
||||
self.dateModified = parsedArticle.dateModified
|
||||
self.link = parsedArticle.link
|
||||
self.permalink = parsedArticle.permalink
|
||||
self.author = parsedArticle.author
|
||||
self.guid = parsedItem.uniqueID
|
||||
self.title = parsedItem.title
|
||||
self.body = (parsedItem.contentHTML ?? parsedItem.contentText) ?? ""
|
||||
self.datePublished = parsedItem.datePublished
|
||||
self.dateModified = parsedItem.dateModified
|
||||
self.link = parsedItem.externalURL
|
||||
self.permalink = parsedItem.url
|
||||
self.authors = parsedItem.authors
|
||||
}
|
||||
|
||||
private enum ColumnIndex: Int {
|
||||
@ -145,7 +140,7 @@ public extension LocalArticle {
|
||||
}
|
||||
}
|
||||
|
||||
func updateWithParsedArticle(_ parsedArticle: RSParsedArticle) -> NSDictionary? {
|
||||
func updateWithParsedArticle(_ parsedArticle: ParsedItem) -> NSDictionary? {
|
||||
|
||||
let d: NSDictionary = rs_mergeValues(withObjectReturningChanges: parsedArticle, propertyNames: mergeablePropertyNames) as NSDictionary
|
||||
if d.count < 1 {
|
||||
|
16
Frameworks/LocalAccount/LocalAuthor.swift
Normal file
16
Frameworks/LocalAccount/LocalAuthor.swift
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// LocalAuthor.swift
|
||||
// LocalAccount
|
||||
//
|
||||
// Created by Brent Simmons on 6/30/17.
|
||||
// Copyright © 2017 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSParser
|
||||
import DataModel
|
||||
|
||||
extension ParsedAuthor: Author {
|
||||
}
|
||||
|
||||
public typealias LocalAuthor = ParsedAuthor
|
@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import RSXML
|
||||
import RSParser
|
||||
import RSDatabase
|
||||
import DataModel
|
||||
|
||||
@ -182,7 +182,7 @@ final class LocalDatabase {
|
||||
unreadCounts[oneFeedID] = self.unreadCount(oneFeedID, database)
|
||||
}
|
||||
|
||||
DispatchQueue.main.async() { () -> Void in
|
||||
DispatchQueue.main.async() {
|
||||
completion(unreadCounts)
|
||||
}
|
||||
}
|
||||
@ -191,14 +191,14 @@ final class LocalDatabase {
|
||||
|
||||
// MARK: Updating Articles
|
||||
|
||||
func updateFeedWithParsedFeed(_ feed: LocalFeed, parsedFeed: RSParsedFeed, completionHandler: @escaping RSVoidCompletionBlock) {
|
||||
func updateFeedWithParsedFeed(_ feed: LocalFeed, parsedFeed: ParsedFeed, completionHandler: @escaping RSVoidCompletionBlock) {
|
||||
|
||||
if parsedFeed.articles.isEmpty {
|
||||
if parsedFeed.items.isEmpty {
|
||||
completionHandler()
|
||||
return
|
||||
}
|
||||
|
||||
let parsedArticlesDictionary = self.articlesDictionary(parsedFeed.articles as NSSet) as! [String: RSParsedArticle]
|
||||
let parsedArticlesDictionary = self.articlesDictionary(parsedFeed.articles as NSSet) as! [String: ParsedItem]
|
||||
|
||||
fetchArticlesForFeedAsync(feed) { (articles) -> Void in
|
||||
|
||||
@ -259,7 +259,7 @@ private extension LocalDatabase {
|
||||
|
||||
// MARK: Updating Articles
|
||||
|
||||
func updateArticles(_ articles: [String: LocalArticle], parsedArticles: [String: RSParsedArticle], feed: LocalFeed, completionHandler: @escaping RSVoidCompletionBlock) {
|
||||
func updateArticles(_ articles: [String: LocalArticle], parsedArticles: [String: ParsedItem], feed: LocalFeed, completionHandler: @escaping RSVoidCompletionBlock) {
|
||||
|
||||
statusesManager.ensureStatusesForParsedArticles(Set(parsedArticles.values)) {
|
||||
|
||||
@ -282,7 +282,7 @@ private extension LocalDatabase {
|
||||
return d
|
||||
}
|
||||
|
||||
func updateExistingArticles(_ articles: [String: LocalArticle], _ parsedArticles: [String: RSParsedArticle]) -> Set<NSDictionary> {
|
||||
func updateExistingArticles(_ articles: [String: LocalArticle], _ parsedArticles: [String: ParsedItem]) -> Set<NSDictionary> {
|
||||
|
||||
var articleChanges = Set<NSDictionary>()
|
||||
|
||||
@ -299,12 +299,12 @@ private extension LocalDatabase {
|
||||
|
||||
// MARK: Creating Articles
|
||||
|
||||
func createNewArticlesWithParsedArticles(_ parsedArticles: Set<RSParsedArticle>, feedID: String) -> Set<LocalArticle> {
|
||||
func createNewArticlesWithParsedArticles(_ parsedArticles: Set<ParsedItem>, feedID: String) -> Set<LocalArticle> {
|
||||
|
||||
return Set(parsedArticles.map { LocalArticle(account: account, feedID: feedID, parsedArticle: $0) })
|
||||
}
|
||||
|
||||
func articlesWithParsedArticles(_ parsedArticles: Set<RSParsedArticle>, feedID: String) -> Set<LocalArticle> {
|
||||
func articlesWithParsedArticles(_ parsedArticles: Set<ParsedItem>, feedID: String) -> Set<LocalArticle> {
|
||||
|
||||
var localArticles = Set<LocalArticle>()
|
||||
|
||||
@ -316,7 +316,7 @@ private extension LocalDatabase {
|
||||
return localArticles
|
||||
}
|
||||
|
||||
func createNewArticles(_ existingArticles: [String: LocalArticle], parsedArticles: [String: RSParsedArticle], feedID: String) -> Set<LocalArticle> {
|
||||
func createNewArticles(_ existingArticles: [String: LocalArticle], parsedArticles: [String: ParsedItem], feedID: String) -> Set<LocalArticle> {
|
||||
|
||||
let newParsedArticles = parsedArticlesMinusExistingArticles(parsedArticles, existingArticles: existingArticles)
|
||||
let newArticles = createNewArticlesWithParsedArticles(newParsedArticles, feedID: feedID)
|
||||
@ -326,13 +326,13 @@ private extension LocalDatabase {
|
||||
return newArticles
|
||||
}
|
||||
|
||||
func parsedArticlesMinusExistingArticles(_ parsedArticles: [String: RSParsedArticle], existingArticles: [String: LocalArticle]) -> Set<RSParsedArticle> {
|
||||
func parsedArticlesMinusExistingArticles(_ parsedArticles: [String: ParsedItem], existingArticles: [String: LocalArticle]) -> Set<ParsedItem> {
|
||||
|
||||
var result = Set<RSParsedArticle>()
|
||||
var result = Set<ParsedItem>()
|
||||
|
||||
for oneParsedArticle in parsedArticles.values {
|
||||
|
||||
if let _ = existingArticles[oneParsedArticle.articleID] {
|
||||
if let _ = existingArticles[oneParsedArticle.databaseID] {
|
||||
continue
|
||||
}
|
||||
result.insert(oneParsedArticle)
|
||||
|
@ -9,7 +9,7 @@
|
||||
import Foundation
|
||||
import RSCore
|
||||
import RSDatabase
|
||||
import RSXML
|
||||
import RSParser
|
||||
import DataModel
|
||||
|
||||
final class LocalStatusesManager {
|
||||
@ -43,9 +43,9 @@ final class LocalStatusesManager {
|
||||
}
|
||||
}
|
||||
|
||||
func ensureStatusesForParsedArticles(_ parsedArticles: Set<RSParsedArticle>, _ callback: @escaping RSVoidCompletionBlock) {
|
||||
func ensureStatusesForParsedArticles(_ parsedArticles: [ParsedItem], _ callback: @escaping RSVoidCompletionBlock) {
|
||||
|
||||
var articleIDs = Set(parsedArticles.map { $0.articleID })
|
||||
var articleIDs = Set(parsedArticles.map { $0.databaseID })
|
||||
articleIDs = articleIDsMissingStatuses(articleIDs)
|
||||
if articleIDs.isEmpty {
|
||||
callback()
|
||||
@ -56,7 +56,7 @@ final class LocalStatusesManager {
|
||||
|
||||
let statuses = self.fetchStatusesForArticleIDs(articleIDs, database: database)
|
||||
|
||||
DispatchQueue.main.async { () -> Void in
|
||||
DispatchQueue.main.async {
|
||||
|
||||
self.cacheStatuses(statuses)
|
||||
|
||||
@ -209,3 +209,11 @@ private extension LocalStatusesManager {
|
||||
}
|
||||
}
|
||||
|
||||
extension ParsedItem {
|
||||
|
||||
var databaseID: String {
|
||||
get {
|
||||
return "\(feedURL) \(uniqueID)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,11 @@
|
||||
8430C47C1D58033D00E02399 /* sixcolors.html in Resources */ = {isa = PBXBuildFile; fileRef = 8430C4791D58033D00E02399 /* sixcolors.html */; };
|
||||
8444267C1D5138A00092EDD4 /* FeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8444267B1D5138A00092EDD4 /* FeedFinder.swift */; };
|
||||
84B06FEF1ED3808F00F0B54B /* RSWeb.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06FEE1ED3808F00F0B54B /* RSWeb.framework */; };
|
||||
84B06FF11ED380A700F0B54B /* RSXML.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06FF01ED380A700F0B54B /* RSXML.framework */; };
|
||||
84B06FF31ED3812600F0B54B /* RSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06FF21ED3812600F0B54B /* RSCore.framework */; };
|
||||
84BAAE231C8E6B3B009F5239 /* RSFeedFinder.h in Headers */ = {isa = PBXBuildFile; fileRef = 84BAAE221C8E6B3B009F5239 /* RSFeedFinder.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84BAAE2A1C8E6B3B009F5239 /* RSFeedFinder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84BAAE1F1C8E6B3B009F5239 /* RSFeedFinder.framework */; };
|
||||
84BAAE2F1C8E6B3B009F5239 /* RSFeedFinderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 84BAAE2E1C8E6B3B009F5239 /* RSFeedFinderTests.m */; };
|
||||
84F466571F03523100225386 /* RSParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84F466561F03523100225386 /* RSParser.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -54,6 +54,7 @@
|
||||
84E697E51C8E6C10009C585A /* RSCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSCore.framework; path = ../RSCore/build/Debug/RSCore.framework; sourceTree = "<group>"; };
|
||||
84E697E71C8E6C16009C585A /* RSXML.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSXML.framework; path = ../RSXML/build/Debug/RSXML.framework; sourceTree = "<group>"; };
|
||||
84E697E91C8E6C20009C585A /* RSWeb.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSWeb.framework; path = ../RSWeb/build/Debug/RSWeb.framework; sourceTree = "<group>"; };
|
||||
84F466561F03523100225386 /* RSParser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RSParser.framework; path = ../RSParser/build/Debug/RSParser.framework; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -61,8 +62,8 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
84F466571F03523100225386 /* RSParser.framework in Frameworks */,
|
||||
84B06FF31ED3812600F0B54B /* RSCore.framework in Frameworks */,
|
||||
84B06FF11ED380A700F0B54B /* RSXML.framework in Frameworks */,
|
||||
84B06FEF1ED3808F00F0B54B /* RSWeb.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -81,6 +82,7 @@
|
||||
84B06FED1ED3808E00F0B54B /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84F466561F03523100225386 /* RSParser.framework */,
|
||||
84B06FF21ED3812600F0B54B /* RSCore.framework */,
|
||||
84B06FF01ED380A700F0B54B /* RSXML.framework */,
|
||||
84B06FEE1ED3808F00F0B54B /* RSWeb.framework */,
|
||||
|
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSXML
|
||||
import RSParser
|
||||
import RSWeb
|
||||
import RSCore
|
||||
|
||||
@ -89,8 +89,8 @@ private extension FeedFinder {
|
||||
|
||||
func possibleFeedsInHTMLPage(htmlData: Data, urlString: String) -> Set<FeedSpecifier> {
|
||||
|
||||
let xmlData = RSXMLData(data: htmlData, urlString: urlString)
|
||||
var feedSpecifiers = HTMLFeedFinder(xmlData: xmlData).feedSpecifiers
|
||||
let parserData = ParserData(url: urlString, data: htmlData)
|
||||
var feedSpecifiers = HTMLFeedFinder(parserData: parserData).feedSpecifiers
|
||||
|
||||
if feedSpecifiers.isEmpty {
|
||||
// Odds are decent it’s a WordPress site, and just adding /feed/ will work.
|
||||
@ -201,8 +201,8 @@ private extension FeedFinder {
|
||||
}
|
||||
|
||||
func isFeed(_ data: Data, _ urlString: String) -> Bool {
|
||||
|
||||
let xmlData = RSXMLData(data: data, urlString: urlString)
|
||||
return RSCanParseFeed(xmlData)
|
||||
|
||||
let parserData = ParserData(url: urlString, data: data)
|
||||
return FeedParser.canParse(parserData)
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSXML
|
||||
import RSParser
|
||||
|
||||
private let feedURLWordsToMatch = ["feed", "xml", "rss", "atom"]
|
||||
|
||||
@ -21,9 +21,9 @@ class HTMLFeedFinder {
|
||||
|
||||
fileprivate var feedSpecifiersDictionary = [String: FeedSpecifier]()
|
||||
|
||||
init(xmlData: RSXMLData) {
|
||||
init(parserData: ParserData) {
|
||||
|
||||
let metadata = RSHTMLMetadataParser(xmlData: xmlData).metadata
|
||||
let metadata = RSHTMLMetadataParser.htmlMetadata(with: parserData)
|
||||
|
||||
for oneFeedLink in metadata.feedLinks {
|
||||
if let oneURLString = oneFeedLink.urlString {
|
||||
@ -32,7 +32,7 @@ class HTMLFeedFinder {
|
||||
}
|
||||
}
|
||||
|
||||
if let bodyLinks = RSHTMLLinkParser.htmlLinks(with: xmlData) {
|
||||
if let bodyLinks = RSHTMLLinkParser.htmlLinks(with: parserData) {
|
||||
for oneBodyLink in bodyLinks {
|
||||
|
||||
if linkMightBeFeed(oneBodyLink) {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import XCTest
|
||||
@testable import RSFeedFinder
|
||||
import RSXML
|
||||
import RSParser
|
||||
|
||||
class HTMLFeedFinderTests: XCTestCase {
|
||||
|
||||
|
@ -11,8 +11,22 @@ import Foundation
|
||||
// FeedParser handles RSS, Atom, JSON Feed, and RSS-in-JSON.
|
||||
// You don’t need to know the type of feed.
|
||||
|
||||
public typealias FeedParserCallback = (_ parsedFeed: ParsedFeed?, _ error: Error?) -> Void
|
||||
|
||||
public struct FeedParser {
|
||||
|
||||
public static func canParse(_ parserData: ParserData) -> Bool {
|
||||
|
||||
let type = feedType(parserData)
|
||||
|
||||
switch type {
|
||||
case .jsonFeed, .rssInJSON, .rss, .atom:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse(_ parserData: ParserData) throws -> ParsedFeed? {
|
||||
|
||||
// This is generally fast enough to call on the main thread —
|
||||
@ -42,4 +56,21 @@ public struct FeedParser {
|
||||
}
|
||||
catch { throw error }
|
||||
}
|
||||
|
||||
public static func parse(_ parserData: ParserData, _ callback: @escaping FeedParserCallback) {
|
||||
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
do {
|
||||
let parsedFeed = try parse(parserData)
|
||||
DispatchQueue.main.async {
|
||||
callback(parsedFeed, nil)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
DispatchQueue.main.async {
|
||||
callback(nil, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ public struct JSONFeedParser {
|
||||
let expired = parsedObject["expired"] as? Bool ?? false
|
||||
let hubs = parseHubs(parsedObject)
|
||||
|
||||
let items = parseItems(itemsArray)
|
||||
let items = parseItems(itemsArray, parserData.url)
|
||||
|
||||
return ParsedFeed(type: .jsonFeed, title: title, homePageURL: homePageURL, feedURL: feedURL, feedDescription: feedDescription, nextURL: nextURL, iconURL: iconURL, faviconURL: faviconURL, authors: authors, expired: expired, hubs: hubs, items: items)
|
||||
|
||||
@ -81,14 +81,14 @@ private extension JSONFeedParser {
|
||||
return hubs.isEmpty ? nil : hubs
|
||||
}
|
||||
|
||||
static func parseItems(_ itemsArray: JSONArray) -> [ParsedItem] {
|
||||
static func parseItems(_ itemsArray: JSONArray, _ feedURL: String) -> [ParsedItem] {
|
||||
|
||||
return itemsArray.flatMap { (oneItemDictionary) -> ParsedItem? in
|
||||
return parseItem(oneItemDictionary)
|
||||
return parseItem(oneItemDictionary, feedURL)
|
||||
}
|
||||
}
|
||||
|
||||
static func parseItem(_ itemDictionary: JSONDictionary) -> ParsedItem? {
|
||||
static func parseItem(_ itemDictionary: JSONDictionary, _ feedURL: String) -> ParsedItem? {
|
||||
|
||||
guard let uniqueID = parseUniqueID(itemDictionary) else {
|
||||
return nil
|
||||
@ -114,7 +114,7 @@ private extension JSONFeedParser {
|
||||
let tags = itemDictionary["tags"] as? [String]
|
||||
let attachments = parseAttachments(itemDictionary)
|
||||
|
||||
return ParsedItem(uniqueID: uniqueID, url: url, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: contentText, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: tags, attachments: attachments)
|
||||
return ParsedItem(uniqueID: uniqueID, feedURL: feedURL, url: url, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: contentText, summary: summary, imageURL: imageURL, bannerImageURL: bannerImageURL, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: tags, attachments: attachments)
|
||||
}
|
||||
|
||||
static func parseUniqueID(_ itemDictionary: JSONDictionary) -> String? {
|
||||
|
@ -47,7 +47,7 @@ public struct RSSInJSONParser {
|
||||
let feedURL = parserData.url
|
||||
let feedDescription = channelObject["description"] as? String
|
||||
|
||||
let items = parseItems(itemsObject!)
|
||||
let items = parseItems(itemsObject!, parserData.url)
|
||||
|
||||
return ParsedFeed(type: .rssInJSON, title: title, homePageURL: homePageURL, feedURL: feedURL, feedDescription: feedDescription, nextURL: nil, iconURL: nil, faviconURL: nil, authors: nil, expired: false, hubs: nil, items: items)
|
||||
|
||||
@ -58,15 +58,15 @@ public struct RSSInJSONParser {
|
||||
|
||||
private extension RSSInJSONParser {
|
||||
|
||||
static func parseItems(_ itemsObject: JSONArray) -> [ParsedItem] {
|
||||
static func parseItems(_ itemsObject: JSONArray, _ feedURL: String) -> [ParsedItem] {
|
||||
|
||||
return itemsObject.flatMap{ (oneItemDictionary) -> ParsedItem? in
|
||||
|
||||
return parsedItemWithDictionary(oneItemDictionary)
|
||||
return parsedItemWithDictionary(oneItemDictionary, feedURL)
|
||||
}
|
||||
}
|
||||
|
||||
static func parsedItemWithDictionary(_ itemDictionary: JSONDictionary) -> ParsedItem? {
|
||||
static func parsedItemWithDictionary(_ itemDictionary: JSONDictionary, _ feedURL: String) -> ParsedItem? {
|
||||
|
||||
let externalURL = itemDictionary["link"] as? String
|
||||
let title = itemDictionary["title"] as? String
|
||||
@ -126,7 +126,10 @@ private extension RSSInJSONParser {
|
||||
uniqueID = (s as NSString).rsparser_md5Hash()
|
||||
}
|
||||
|
||||
return ParsedItem(uniqueID: uniqueID, url: nil, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: contentText, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: datePublished, dateModified: nil, authors: authors, tags: tags, attachments: attachments)
|
||||
if let uniqueID = uniqueID {
|
||||
return ParsedItem(uniqueID: uniqueID, feedURL: feedURL, url: nil, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: contentText, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: datePublished, dateModified: nil, authors: authors, tags: tags, attachments: attachments)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
static func parseAuthors(_ itemDictionary: JSONDictionary) -> [ParsedAuthor]? {
|
||||
|
@ -10,7 +10,8 @@ import Foundation
|
||||
|
||||
public struct ParsedItem {
|
||||
|
||||
public let uniqueID: String?
|
||||
public let uniqueID: String
|
||||
public let feedURL: String
|
||||
public let url: String?
|
||||
public let externalURL: String?
|
||||
public let title: String?
|
||||
@ -25,9 +26,10 @@ public struct ParsedItem {
|
||||
public let tags: [String]?
|
||||
public let attachments: [ParsedAttachment]?
|
||||
|
||||
init(uniqueID: String?, url: String?, externalURL: String?, title: String?, contentHTML: String?, contentText: String?, summary: String?, imageURL: String?, bannerImageURL: String?, datePublished: Date?, dateModified: Date?, authors: [ParsedAuthor]?, tags: [String]?, attachments: [ParsedAttachment]?) {
|
||||
init(uniqueID: String, feedURL: String, url: String?, externalURL: String?, title: String?, contentHTML: String?, contentText: String?, summary: String?, imageURL: String?, bannerImageURL: String?, datePublished: Date?, dateModified: Date?, authors: [ParsedAuthor]?, tags: [String]?, attachments: [ParsedAttachment]?) {
|
||||
|
||||
self.uniqueID = uniqueID
|
||||
self.feedURL = feedURL
|
||||
self.url = url
|
||||
self.externalURL = externalURL
|
||||
self.title = title
|
||||
|
@ -46,7 +46,7 @@ private extension RSParsedFeedTransformer {
|
||||
let dateModified = parsedArticle.dateModified
|
||||
let authors = parsedAuthors(parsedArticle.author)
|
||||
|
||||
return ParsedItem(uniqueID: uniqueID, url: url, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: nil, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: nil, attachments: nil)
|
||||
return ParsedItem(uniqueID: uniqueID, feedURL: parsedArticle.feedURL, url: url, externalURL: externalURL, title: title, contentHTML: contentHTML, contentText: nil, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: datePublished, dateModified: dateModified, authors: authors, tags: nil, attachments: nil)
|
||||
}
|
||||
|
||||
static func parsedAuthors(_ authorEmailAddress: String?) -> [ParsedAuthor]? {
|
||||
|
@ -1,6 +1,6 @@
|
||||
//
|
||||
// RSDateParserTests.m
|
||||
// RSXML
|
||||
// RSParser
|
||||
//
|
||||
// Created by Brent Simmons on 12/26/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
|
Loading…
x
Reference in New Issue
Block a user