mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2024-12-18 12:28:37 +01:00
Add DataModel framework.
This commit is contained in:
parent
dec10ad0a1
commit
91d81831e9
@ -7,6 +7,8 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
8471A2C41ED4CEBF008F099E /* DataModel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8471A2B71ED4CEAD008F099E /* DataModel.framework */; };
|
||||
8471A2C51ED4CEBF008F099E /* DataModel.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8471A2B71ED4CEAD008F099E /* DataModel.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
849C64641ED37A5D003D8FC0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849C64631ED37A5D003D8FC0 /* AppDelegate.swift */; };
|
||||
849C64661ED37A5D003D8FC0 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849C64651ED37A5D003D8FC0 /* ViewController.swift */; };
|
||||
849C64681ED37A5D003D8FC0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 849C64671ED37A5D003D8FC0 /* Assets.xcassets */; };
|
||||
@ -31,6 +33,20 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
8471A2B61ED4CEAD008F099E /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 8471A2B21ED4CEAD008F099E /* DataModel.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 84C7AE921D68C558009FB883;
|
||||
remoteInfo = DataModel;
|
||||
};
|
||||
8471A2C61ED4CEBF008F099E /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 8471A2B21ED4CEAD008F099E /* DataModel.xcodeproj */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 84C7AE911D68C558009FB883;
|
||||
remoteInfo = DataModel;
|
||||
};
|
||||
849C64721ED37A5D003D8FC0 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 849C64581ED37A5D003D8FC0 /* Project object */;
|
||||
@ -249,6 +265,7 @@
|
||||
84B06FFE1ED3818D00F0B54B /* RSTree.framework in Embed Frameworks */,
|
||||
84B06FAF1ED37DBD00F0B54B /* RSCore.framework in Embed Frameworks */,
|
||||
84B06FD01ED37F7D00F0B54B /* DB5.framework in Embed Frameworks */,
|
||||
8471A2C51ED4CEBF008F099E /* DataModel.framework in Embed Frameworks */,
|
||||
84B06FC31ED37E9600F0B54B /* RSWeb.framework in Embed Frameworks */,
|
||||
84B06F831ED37BDD00F0B54B /* RSXML.framework in Embed Frameworks */,
|
||||
);
|
||||
@ -258,6 +275,7 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
8471A2B21ED4CEAD008F099E /* DataModel.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = DataModel.xcodeproj; path = Frameworks/DataModel/DataModel.xcodeproj; sourceTree = "<group>"; };
|
||||
849C64601ED37A5D003D8FC0 /* Evergreen.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Evergreen.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
849C64631ED37A5D003D8FC0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Evergreen/AppDelegate.swift; sourceTree = "<group>"; };
|
||||
849C64651ED37A5D003D8FC0 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = Evergreen/ViewController.swift; sourceTree = "<group>"; };
|
||||
@ -288,6 +306,7 @@
|
||||
84B06FFD1ED3818D00F0B54B /* RSTree.framework in Frameworks */,
|
||||
84B06FAE1ED37DBD00F0B54B /* RSCore.framework in Frameworks */,
|
||||
84B06FCF1ED37F7D00F0B54B /* DB5.framework in Frameworks */,
|
||||
8471A2C41ED4CEBF008F099E /* DataModel.framework in Frameworks */,
|
||||
84B06FC21ED37E9600F0B54B /* RSWeb.framework in Frameworks */,
|
||||
84B06F821ED37BDD00F0B54B /* RSXML.framework in Frameworks */,
|
||||
);
|
||||
@ -303,6 +322,14 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
8471A2B31ED4CEAD008F099E /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8471A2B71ED4CEAD008F099E /* DataModel.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
849C64571ED37A5D003D8FC0 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -313,6 +340,7 @@
|
||||
849C646C1ED37A5D003D8FC0 /* Info.plist */,
|
||||
849C64741ED37A5D003D8FC0 /* EvergreenTests */,
|
||||
849C64611ED37A5D003D8FC0 /* Products */,
|
||||
8471A2B21ED4CEAD008F099E /* DataModel.xcodeproj */,
|
||||
84B06FC61ED37F7200F0B54B /* DB5.xcodeproj */,
|
||||
84B06FA21ED37DAC00F0B54B /* RSCore.xcodeproj */,
|
||||
84B06F961ED37DA000F0B54B /* RSDatabase.xcodeproj */,
|
||||
@ -441,6 +469,7 @@
|
||||
84B06FEC1ED3803A00F0B54B /* PBXTargetDependency */,
|
||||
84B070001ED3818D00F0B54B /* PBXTargetDependency */,
|
||||
84B0700D1ED3822600F0B54B /* PBXTargetDependency */,
|
||||
8471A2C71ED4CEBF008F099E /* PBXTargetDependency */,
|
||||
);
|
||||
name = Evergreen;
|
||||
productName = Evergreen;
|
||||
@ -500,6 +529,10 @@
|
||||
productRefGroup = 849C64611ED37A5D003D8FC0 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectReferences = (
|
||||
{
|
||||
ProductGroup = 8471A2B31ED4CEAD008F099E /* Products */;
|
||||
ProjectRef = 8471A2B21ED4CEAD008F099E /* DataModel.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 84B06FC71ED37F7200F0B54B /* Products */;
|
||||
ProjectRef = 84B06FC61ED37F7200F0B54B /* DB5.xcodeproj */;
|
||||
@ -542,6 +575,13 @@
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXReferenceProxy section */
|
||||
8471A2B71ED4CEAD008F099E /* DataModel.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
path = DataModel.framework;
|
||||
remoteRef = 8471A2B61ED4CEAD008F099E /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
84B06F7D1ED37BCA00F0B54B /* RSXML.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
@ -724,6 +764,11 @@
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
8471A2C71ED4CEBF008F099E /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = DataModel;
|
||||
targetProxy = 8471A2C61ED4CEBF008F099E /* PBXContainerItemProxy */;
|
||||
};
|
||||
849C64731ED37A5D003D8FC0 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 849C645F1ED37A5D003D8FC0 /* Evergreen */;
|
||||
|
44
Frameworks/DataModel/AccountProtocol.swift
Normal file
44
Frameworks/DataModel/AccountProtocol.swift
Normal file
@ -0,0 +1,44 @@
|
||||
//
|
||||
// AccountProtocol.swift
|
||||
// Rainier
|
||||
//
|
||||
// Created by Brent Simmons on 4/17/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol Account: class, Folder {
|
||||
|
||||
var identifier: String {get}
|
||||
var type: String {get}
|
||||
var refreshInProgress: Bool {get}
|
||||
|
||||
init(settingsFile: String, dataFolder: String, identifier: String)
|
||||
|
||||
func refreshAll()
|
||||
|
||||
func markArticles(_ articles: NSSet, statusKey: ArticleStatusKey, flag: Bool)
|
||||
|
||||
func hasFeedWithURLString(_: String) -> Bool
|
||||
|
||||
func importOPML(_: Any)
|
||||
|
||||
func fetchArticles(for: [AnyObject]) -> [Article]
|
||||
}
|
||||
|
||||
public extension Account {
|
||||
|
||||
func hasFeedWithURLString(_ urlString: String) -> Bool {
|
||||
|
||||
if let _ = existingFeedWithURL(urlString) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public func postArticleStatusesDidChangeNotification(_ articles: NSSet) {
|
||||
|
||||
NotificationCenter.default.post(name: .ArticleStatusesDidChange, object: self, userInfo: [articlesKey: articles])
|
||||
}
|
||||
}
|
68
Frameworks/DataModel/ArticleProtocol.swift
Normal file
68
Frameworks/DataModel/ArticleProtocol.swift
Normal file
@ -0,0 +1,68 @@
|
||||
//
|
||||
// ArticleProtocol.swift
|
||||
// Rainier
|
||||
//
|
||||
// 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
|
||||
}
|
||||
|
43
Frameworks/DataModel/ArticleStatusProtocol.swift
Normal file
43
Frameworks/DataModel/ArticleStatusProtocol.swift
Normal file
@ -0,0 +1,43 @@
|
||||
//
|
||||
// ArticleStatusProtocol.swift
|
||||
// Rainier
|
||||
//
|
||||
// Created by Brent Simmons on 4/23/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum ArticleStatusKey: String {
|
||||
|
||||
case read = "read"
|
||||
case starred = "starred"
|
||||
case userDeleted = "userDeleted"
|
||||
}
|
||||
|
||||
public protocol ArticleStatus {
|
||||
|
||||
var read: Bool {get set}
|
||||
var starred: Bool {get set}
|
||||
var userDeleted: Bool {get set}
|
||||
var dateArrived: Date {get}
|
||||
|
||||
func boolStatusForKey(_ articleStatusKey: ArticleStatusKey) -> Bool
|
||||
func setBoolStatusForKey(_ status: Bool, articleStatusKey: ArticleStatusKey)
|
||||
}
|
||||
|
||||
public extension ArticleStatus {
|
||||
|
||||
func boolStatusForKey(_ articleStatusKey: ArticleStatusKey) -> Bool {
|
||||
|
||||
switch articleStatusKey {
|
||||
|
||||
case .read:
|
||||
return read
|
||||
case .starred:
|
||||
return starred
|
||||
case .userDeleted:
|
||||
return userDeleted
|
||||
}
|
||||
}
|
||||
}
|
76
Frameworks/DataModel/BatchUpdates.swift
Normal file
76
Frameworks/DataModel/BatchUpdates.swift
Normal file
@ -0,0 +1,76 @@
|
||||
//
|
||||
// BatchUpdates.swift
|
||||
// DataModel
|
||||
//
|
||||
// Created by Brent Simmons on 9/12/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
private final class BatchUpdatesTracker {
|
||||
|
||||
private var batchUpdatesCount = 0
|
||||
|
||||
var isPerformingBatchUpdates: Bool {
|
||||
get {
|
||||
return batchUpdatesCount > 0
|
||||
}
|
||||
}
|
||||
|
||||
func incrementBatchUpdatesCount() {
|
||||
|
||||
batchUpdatesCount = batchUpdatesCount + 1
|
||||
}
|
||||
|
||||
func decrementBatchUpdatesCount() {
|
||||
|
||||
batchUpdatesCount = batchUpdatesCount - 1
|
||||
|
||||
if batchUpdatesCount < 1 {
|
||||
|
||||
if batchUpdatesCount < 0 {
|
||||
assertionFailure("Batch updates count should never be below 0.")
|
||||
batchUpdatesCount = 0
|
||||
}
|
||||
|
||||
batchUpdatesCount = 0
|
||||
postDataModelDidPerformBatchUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
func postDataModelDidPerformBatchUpdates() {
|
||||
|
||||
NotificationCenter.default.post(name: .DataModelDidPerformBatchUpdates, object: nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate let batchUpdatesTracker = BatchUpdatesTracker()
|
||||
|
||||
public func dataModelIsPerformingBatchUpdates() -> Bool {
|
||||
|
||||
return batchUpdatesTracker.isPerformingBatchUpdates
|
||||
}
|
||||
|
||||
public typealias BatchUpdatesBlock = () -> Void
|
||||
|
||||
public func performDataModelBatchUpdates(_ batchUpdatesBlock: BatchUpdatesBlock) {
|
||||
|
||||
startDataModelBatchUpdates()
|
||||
|
||||
batchUpdatesBlock()
|
||||
|
||||
endDataModelBatchUpdates()
|
||||
}
|
||||
|
||||
private func startDataModelBatchUpdates() {
|
||||
|
||||
batchUpdatesTracker.incrementBatchUpdatesCount()
|
||||
}
|
||||
|
||||
private func endDataModelBatchUpdates() {
|
||||
|
||||
batchUpdatesTracker.decrementBatchUpdatesCount()
|
||||
}
|
||||
|
340
Frameworks/DataModel/DataModel.xcodeproj/project.pbxproj
Normal file
340
Frameworks/DataModel/DataModel.xcodeproj/project.pbxproj
Normal file
@ -0,0 +1,340 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* 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 */; };
|
||||
8471A2D81ED4CEFA008F099E /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8471A2D31ED4CEFA008F099E /* Notifications.swift */; };
|
||||
8471A2D91ED4CEFA008F099E /* UnreadCountProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8471A2D41ED4CEFA008F099E /* UnreadCountProviderProtocol.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>"; };
|
||||
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; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
84C7AE8E1D68C558009FB883 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8439773E1DA07F2400F0FCBD /* RSCore.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
8439773C1DA07F2400F0FCBD /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8439773D1DA07F2400F0FCBD /* RSCore.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84C7AE881D68C558009FB883 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8471A2C81ED4CEEE008F099E /* AccountProtocol.swift */,
|
||||
8471A2C91ED4CEEE008F099E /* ArticleProtocol.swift */,
|
||||
8471A2CA1ED4CEEE008F099E /* ArticleStatusProtocol.swift */,
|
||||
8471A2CB1ED4CEEE008F099E /* BatchUpdates.swift */,
|
||||
8471A2D01ED4CEFA008F099E /* DisplayNameProviderProtocol.swift */,
|
||||
8471A2D11ED4CEFA008F099E /* FeedProtocol.swift */,
|
||||
8471A2D21ED4CEFA008F099E /* FolderProtocol.swift */,
|
||||
8471A2D31ED4CEFA008F099E /* Notifications.swift */,
|
||||
8471A2D41ED4CEFA008F099E /* UnreadCountProviderProtocol.swift */,
|
||||
8471A2DA1ED4CF01008F099E /* Info.plist */,
|
||||
84C7AE931D68C558009FB883 /* Products */,
|
||||
8439773C1DA07F2400F0FCBD /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84C7AE931D68C558009FB883 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84C7AE921D68C558009FB883 /* DataModel.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
84C7AE8F1D68C558009FB883 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
84C7AE911D68C558009FB883 /* DataModel */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 84C7AE9A1D68C558009FB883 /* Build configuration list for PBXNativeTarget "DataModel" */;
|
||||
buildPhases = (
|
||||
84C7AE8D1D68C558009FB883 /* Sources */,
|
||||
84C7AE8E1D68C558009FB883 /* Frameworks */,
|
||||
84C7AE8F1D68C558009FB883 /* Headers */,
|
||||
84C7AE901D68C558009FB883 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = DataModel;
|
||||
productName = DataModel;
|
||||
productReference = 84C7AE921D68C558009FB883 /* DataModel.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
84C7AE891D68C558009FB883 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0800;
|
||||
ORGANIZATIONNAME = "Ranchero Software, LLC";
|
||||
TargetAttributes = {
|
||||
84C7AE911D68C558009FB883 = {
|
||||
CreatedOnToolsVersion = 8.0;
|
||||
LastSwiftMigration = 0800;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 84C7AE8C1D68C558009FB883 /* Build configuration list for PBXProject "DataModel" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 84C7AE881D68C558009FB883;
|
||||
productRefGroup = 84C7AE931D68C558009FB883 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
84C7AE911D68C558009FB883 /* DataModel */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
84C7AE901D68C558009FB883 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
84C7AE8D1D68C558009FB883 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8471A2CC1ED4CEEE008F099E /* AccountProtocol.swift in Sources */,
|
||||
8471A2D61ED4CEFA008F099E /* FeedProtocol.swift in Sources */,
|
||||
8471A2CF1ED4CEEE008F099E /* BatchUpdates.swift in Sources */,
|
||||
8471A2CE1ED4CEEE008F099E /* ArticleStatusProtocol.swift in Sources */,
|
||||
8471A2D91ED4CEFA008F099E /* UnreadCountProviderProtocol.swift in Sources */,
|
||||
8471A2CD1ED4CEEE008F099E /* ArticleProtocol.swift in Sources */,
|
||||
8471A2D51ED4CEFA008F099E /* DisplayNameProviderProtocol.swift in Sources */,
|
||||
8471A2D81ED4CEFA008F099E /* Notifications.swift in Sources */,
|
||||
8471A2D71ED4CEFA008F099E /* FolderProtocol.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
84C7AE981D68C558009FB883 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
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_MOVES = 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.11;
|
||||
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;
|
||||
};
|
||||
84C7AE991D68C558009FB883 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
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_MOVES = 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.11;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
84C7AE9B1D68C558009FB883 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_VERSION = A;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.DataModel;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
84C7AE9C1D68C558009FB883 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
FRAMEWORK_VERSION = A;
|
||||
INFOPLIST_FILE = Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.DataModel;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 3.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
84C7AE8C1D68C558009FB883 /* Build configuration list for PBXProject "DataModel" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
84C7AE981D68C558009FB883 /* Debug */,
|
||||
84C7AE991D68C558009FB883 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
84C7AE9A1D68C558009FB883 /* Build configuration list for PBXNativeTarget "DataModel" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
84C7AE9B1D68C558009FB883 /* Debug */,
|
||||
84C7AE9C1D68C558009FB883 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 84C7AE891D68C558009FB883 /* Project object */;
|
||||
}
|
7
Frameworks/DataModel/DataModel.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
Frameworks/DataModel/DataModel.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:DataModel.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
15
Frameworks/DataModel/DisplayNameProviderProtocol.swift
Normal file
15
Frameworks/DataModel/DisplayNameProviderProtocol.swift
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// DisplayNameProviderProtocol.swift
|
||||
// DataModel
|
||||
//
|
||||
// Created by Brent Simmons on 7/28/16.
|
||||
// Copyright © 2016 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol DisplayNameProvider {
|
||||
|
||||
var nameForDisplay: String {get}
|
||||
}
|
||||
|
45
Frameworks/DataModel/FeedProtocol.swift
Normal file
45
Frameworks/DataModel/FeedProtocol.swift
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// FeedProtocol.swift
|
||||
// Rainier
|
||||
//
|
||||
// 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
|
||||
}
|
||||
}
|
171
Frameworks/DataModel/FolderProtocol.swift
Normal file
171
Frameworks/DataModel/FolderProtocol.swift
Normal file
@ -0,0 +1,171 @@
|
||||
//
|
||||
// FolderProtocol.swift
|
||||
// Rainier
|
||||
//
|
||||
// Created by Brent Simmons on 4/17/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
|
||||
public typealias FolderVisitBlock = (_ obj: AnyObject) -> Bool
|
||||
|
||||
public let FolderChildrenDidChangeNotification = "FolderChildNodesDidChangeNotification"
|
||||
|
||||
public func FolderPostChildrenDidChangeNotification(_ folder: Folder) {
|
||||
|
||||
NotificationCenter.default.post(name: NSNotification.Name(rawValue: FolderChildrenDidChangeNotification), object: folder)
|
||||
}
|
||||
|
||||
public protocol Folder: class, UnreadCountProvider, DisplayNameProvider {
|
||||
|
||||
// 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
|
||||
var flattenedFeeds: NSSet {get}
|
||||
|
||||
func objectIsChild(_ obj: AnyObject) -> Bool
|
||||
func objectIsDescendant(_ obj: AnyObject) -> Bool
|
||||
|
||||
func fetchArticles() -> [Article]
|
||||
|
||||
// visitBlock should return true to stop visiting.
|
||||
// visitObjects returns true if a visitBlock returned true.
|
||||
func visitObjects(_ recurse: Bool, visitBlock: FolderVisitBlock) -> Bool
|
||||
func visitChildren(visitBlock: FolderVisitBlock) -> Bool // Above with recurse = false
|
||||
|
||||
func findObject(_ recurse: Bool, visitBlock: FolderVisitBlock) -> AnyObject?
|
||||
|
||||
func canAddItem(_ item: AnyObject) -> Bool
|
||||
func addItem(_ item: AnyObject) -> Bool // Return true even if item already exists.
|
||||
func addItems(_ items: [AnyObject]) -> Bool // Return true even if some items already exist.
|
||||
|
||||
func canAddFolderWithName(_ folderName: String) -> Bool // Special case: folder with name exists. Return true in that case.
|
||||
func ensureFolderWithName(_ folderName: String) -> Folder? // Return folder even if item already exists.
|
||||
|
||||
// Recurses
|
||||
func existingFeedWithID(_ feedID: String) -> Feed?
|
||||
func existingFeedWithURL(_ urlString: String) -> Feed?
|
||||
|
||||
// Does not recurse.
|
||||
func existingFolderWithName(_ name: String) -> Folder?
|
||||
|
||||
// Doesn't add feed. Just creates instance.
|
||||
func createFeedWithName(_ name: String?, editedName: String?, urlString: String) -> Feed?
|
||||
|
||||
func deleteItems(_ items: [AnyObject])
|
||||
|
||||
// Exporting OPML.
|
||||
func opmlString(indentLevel: Int) -> String
|
||||
}
|
||||
|
||||
public extension Folder {
|
||||
|
||||
var hasAtLeastOneFeed: Bool {
|
||||
get {
|
||||
return visitObjects(true) { (oneObject) in
|
||||
|
||||
return oneObject is Feed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func visitChildren(visitBlock: FolderVisitBlock) -> Bool {
|
||||
|
||||
return visitObjects(false, visitBlock: visitBlock)
|
||||
}
|
||||
|
||||
func findObject(_ recurse: Bool, visitBlock: FolderVisitBlock) -> AnyObject? {
|
||||
|
||||
var foundObject: AnyObject?
|
||||
|
||||
let _ = visitObjects(recurse) { (oneObject) in
|
||||
|
||||
if let _ = foundObject {
|
||||
return true
|
||||
}
|
||||
|
||||
if visitBlock(oneObject) {
|
||||
foundObject = oneObject
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return foundObject
|
||||
}
|
||||
|
||||
func objectIsChild(_ obj: AnyObject) -> Bool {
|
||||
|
||||
return visitObjects(false) { (oneObject) in
|
||||
return obj === oneObject
|
||||
}
|
||||
}
|
||||
|
||||
func objectIsDescendant(_ obj: AnyObject) -> Bool {
|
||||
|
||||
return visitObjects(true) { (oneObject) in
|
||||
return obj === oneObject
|
||||
}
|
||||
}
|
||||
|
||||
func existingFolderWithName(_ name: String) -> Folder? {
|
||||
|
||||
let foundObject = findObject(false) { (oneObject) in
|
||||
if let oneFolder = oneObject as? Folder, oneFolder.nameForDisplay == name {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return foundObject as! Folder?
|
||||
}
|
||||
|
||||
func addItems(_ items: [AnyObject]) -> Bool {
|
||||
|
||||
var atLeastOneItemAdded = false
|
||||
items.forEach { (oneItem) in
|
||||
if addItem(oneItem) {
|
||||
atLeastOneItemAdded = true
|
||||
}
|
||||
}
|
||||
return atLeastOneItemAdded
|
||||
}
|
||||
|
||||
func opmlString(indentLevel: Int) -> String {
|
||||
|
||||
let escapedTitle = nameForDisplay.rs_stringByEscapingSpecialXMLCharacters()
|
||||
var s = "<outline text=\"\(escapedTitle)\" title=\"\(escapedTitle)\">\n"
|
||||
s = s.rs_string(byPrependingNumberOfTabs: indentLevel)
|
||||
|
||||
var hasAtLeastOneChild = false
|
||||
|
||||
let _ = visitChildren { (oneChild) -> Bool in
|
||||
|
||||
hasAtLeastOneChild = true
|
||||
if let oneFolder = oneChild as? Folder {
|
||||
s = s + oneFolder.opmlString(indentLevel: indentLevel + 1)
|
||||
}
|
||||
else if let oneFeed = oneChild as? Feed {
|
||||
s = s + oneFeed.opmlString(indentLevel: indentLevel + 1)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
if !hasAtLeastOneChild {
|
||||
s = "<outline text=\"\(escapedTitle)\" title=\"\(escapedTitle)\"/>\n"
|
||||
s = s.rs_string(byPrependingNumberOfTabs: indentLevel)
|
||||
return s
|
||||
}
|
||||
|
||||
s = s + NSString.rs_string(withNumberOfTabs: indentLevel) + "</outline>\n"
|
||||
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
26
Frameworks/DataModel/Info.plist
Normal file
26
Frameworks/DataModel/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 © 2016 Ranchero Software, LLC. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
21
Frameworks/DataModel/Notifications.swift
Normal file
21
Frameworks/DataModel/Notifications.swift
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// Notifications.swift
|
||||
// DataModel
|
||||
//
|
||||
// Created by Brent Simmons on 9/10/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension Notification.Name {
|
||||
|
||||
public static let ArticleStatusesDidChange = Notification.Name(rawValue: "ArticleStatusesDidChange")
|
||||
public static let UnreadCountDidChange = Notification.Name(rawValue: "UnreadCountDidChangeNotification")
|
||||
public static let DataModelDidPerformBatchUpdates = Notification.Name(rawValue: "DataModelDidPerformBatchUpdatesDidPerformBatchUpdatesNotification")
|
||||
public static let AccountRefreshProgressDidChange = Notification.Name(rawValue: "AccountRefreshProgressDidChangeNotification")
|
||||
}
|
||||
|
||||
public let articlesKey = "articles"
|
||||
public let unreadCountKey = "unreadCount"
|
||||
public let progressKey = "progress" //RSProgress
|
38
Frameworks/DataModel/UnreadCountProviderProtocol.swift
Normal file
38
Frameworks/DataModel/UnreadCountProviderProtocol.swift
Normal file
@ -0,0 +1,38 @@
|
||||
//
|
||||
// UnreadCountProtocol.swift
|
||||
// Rainier
|
||||
//
|
||||
// Created by Brent Simmons on 4/8/16.
|
||||
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol UnreadCountProvider {
|
||||
|
||||
var unreadCount: Int {get}
|
||||
|
||||
func updateUnreadCount()
|
||||
}
|
||||
|
||||
public func calculateUnreadCount<T: Collection>(_ children: T) -> Int {
|
||||
|
||||
var updatedUnreadCount = 0
|
||||
|
||||
children.forEach { (oneChild) in
|
||||
if let oneUnreadCountProvider = oneChild as? UnreadCountProvider {
|
||||
updatedUnreadCount += oneUnreadCountProvider.unreadCount
|
||||
}
|
||||
}
|
||||
|
||||
return updatedUnreadCount
|
||||
}
|
||||
|
||||
public extension UnreadCountProvider {
|
||||
|
||||
public func postUnreadCountDidChangeNotification() {
|
||||
|
||||
NotificationCenter.default.post(name: .UnreadCountDidChange, object: self, userInfo: [unreadCountKey: unreadCount])
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user