Commit major surgery but leave it unfinished. Everything is broken.

This commit is contained in:
Brent Simmons
2017-07-01 17:22:19 -07:00
parent 683d5aaf71
commit 944f05c71e
38 changed files with 1241 additions and 212 deletions

View 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
}
}

View 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 {
}

View 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
}
}
}

View File

@@ -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
}

View 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
}
}

View 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
}
}

View File

@@ -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

View File

@@ -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;
};

View 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
}
}

View File

@@ -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
}
}

View 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 {
}

View 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
}