diff --git a/Evergreen.xcodeproj/project.pbxproj b/Evergreen.xcodeproj/project.pbxproj index f7a18e105..3bfec97ba 100644 --- a/Evergreen.xcodeproj/project.pbxproj +++ b/Evergreen.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 842E45E31ED8C681000A8B52 /* KeyboardDelegateProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E45E21ED8C681000A8B52 /* KeyboardDelegateProtocol.swift */; }; 842E45E51ED8C6B7000A8B52 /* MainWindowSplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E45E41ED8C6B7000A8B52 /* MainWindowSplitView.swift */; }; 842E45E71ED8C747000A8B52 /* DB5.plist in Resources */ = {isa = PBXBuildFile; fileRef = 842E45E61ED8C747000A8B52 /* DB5.plist */; }; + 846A7BEC1F872C5600FEFD30 /* BatchUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846A7BEB1F872C5600FEFD30 /* BatchUpdate.swift */; }; 846E773D1F6EF67A00A165E2 /* Account.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846E773A1F6EF5D700A165E2 /* Account.framework */; }; 846E773E1F6EF67A00A165E2 /* Account.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 846E773A1F6EF5D700A165E2 /* Account.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 846E77411F6EF6A100A165E2 /* Database.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846E77211F6EF5D100A165E2 /* Database.framework */; }; @@ -378,6 +379,7 @@ 842E45E21ED8C681000A8B52 /* KeyboardDelegateProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardDelegateProtocol.swift; sourceTree = ""; }; 842E45E41ED8C6B7000A8B52 /* MainWindowSplitView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowSplitView.swift; sourceTree = ""; }; 842E45E61ED8C747000A8B52 /* DB5.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = DB5.plist; path = Evergreen/Resources/DB5.plist; sourceTree = ""; }; + 846A7BEB1F872C5600FEFD30 /* BatchUpdate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BatchUpdate.swift; path = Evergreen/BatchUpdate.swift; sourceTree = ""; }; 846E77161F6EF5D000A165E2 /* Database.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Database.xcodeproj; path = Frameworks/Database/Database.xcodeproj; sourceTree = ""; }; 846E77301F6EF5D600A165E2 /* Account.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Account.xcodeproj; path = Frameworks/Account/Account.xcodeproj; sourceTree = ""; }; 849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddFolderWindowController.swift; sourceTree = ""; }; @@ -673,6 +675,7 @@ 842E45CD1ED8C308000A8B52 /* AppNotifications.swift */, 84DAEE311F870B390058304B /* DockBadge.swift */, 842E45DC1ED8C54B000A8B52 /* Browser.swift */, + 846A7BEB1F872C5600FEFD30 /* BatchUpdate.swift */, 842E45E11ED8C681000A8B52 /* MainWindow */, 842E45E01ED8C587000A8B52 /* Preferences */, 849A97861ED9ECEF007D329B /* Article Styles */, @@ -1174,6 +1177,7 @@ 849A979F1ED9F130007D329B /* SidebarCell.swift in Sources */, 849A97651ED9EB96007D329B /* SidebarTreeControllerDelegate.swift in Sources */, 849A97671ED9EB96007D329B /* UnreadCountView.swift in Sources */, + 846A7BEC1F872C5600FEFD30 /* BatchUpdate.swift in Sources */, 849A975B1ED9EB0D007D329B /* ArticleUtilities.swift in Sources */, 84DAEE301F86CAFE0058304B /* OPMLImporter.swift in Sources */, 849A975C1ED9EB0D007D329B /* DefaultFeedsImporter.swift in Sources */, diff --git a/Evergreen/AppDelegate.swift b/Evergreen/AppDelegate.swift index f6cd38a7d..9bedb93fc 100644 --- a/Evergreen/AppDelegate.swift +++ b/Evergreen/AppDelegate.swift @@ -16,6 +16,7 @@ import Account let appName = "Evergreen" var currentTheme: VSTheme! +let batchUpdate = BatchUpdate() @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations { diff --git a/Evergreen/BatchUpdate.swift b/Evergreen/BatchUpdate.swift new file mode 100644 index 000000000..0019cd01a --- /dev/null +++ b/Evergreen/BatchUpdate.swift @@ -0,0 +1,63 @@ +// +// BatchUpdates.swift +// DataModel +// +// Created by Brent Simmons on 9/12/16. +// Copyright © 2016 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +public typealias BatchUpdateBlock = () -> Void + +public extension Notification.Name { + + public static let BatchUpdateDidPerform = Notification.Name(rawValue: "BatchUpdateDidPerform") +} + +final class BatchUpdate { + + private var count = 0 + + var isPerforming: Bool { + get { + return count > 0 + } + } + + func perform(_ batchUpdateBlock: BatchUpdateBlock) { + + incrementCount() + batchUpdateBlock() + decrementCount() + } +} + +private extension BatchUpdate { + + func incrementCount() { + + count = count + 1 + } + + func decrementCount() { + + count = count - 1 + + if count < 1 { + + if count < 0 { + assertionFailure("Batch updates count should never be below 0.") + count = 0 + } + + count = 0 + postBatchUpdateDidPerform() + } + } + + func postBatchUpdateDidPerform() { + + NotificationCenter.default.post(name: .BatchUpdateDidPerform, object: nil, userInfo: nil) + } +} diff --git a/Evergreen/MainWindow/MainWindowSplitView.swift b/Evergreen/MainWindow/MainWindowSplitView.swift index 1794f8588..beb4b8ac0 100644 --- a/Evergreen/MainWindow/MainWindowSplitView.swift +++ b/Evergreen/MainWindow/MainWindowSplitView.swift @@ -8,10 +8,10 @@ import Cocoa -private let splitViewDividerColor = NSColor(calibratedWhite: 0.8, alpha: 1.0) - class MainWindowSplitView: NSSplitView { + private let splitViewDividerColor = NSColor(calibratedWhite: 0.8, alpha: 1.0) + override var dividerColor: NSColor { get { return splitViewDividerColor diff --git a/Evergreen/MainWindow/Sidebar/SidebarViewController.swift b/Evergreen/MainWindow/Sidebar/SidebarViewController.swift index 6b0146e08..8944fa768 100644 --- a/Evergreen/MainWindow/Sidebar/SidebarViewController.swift +++ b/Evergreen/MainWindow/Sidebar/SidebarViewController.swift @@ -73,7 +73,7 @@ import Account outlineView.removeItems(at: selectedRows, inParent: nil, withAnimation: [.slideDown]) outlineView.endUpdates() - performDataModelBatchUpdates { + batchUpdate.perform { deleteItemsForNodes(nodesToDelete) } @@ -164,7 +164,7 @@ private extension SidebarViewController { func rebuildTreeAndReloadDataIfNeeded() { - if !dataModelIsPerformingBatchUpdates() { + if !batchUpdate.isPerforming { treeController.rebuild() outlineView.reloadData() } diff --git a/Frameworks/Data/BatchUpdates.swift b/Frameworks/Data/BatchUpdates.swift deleted file mode 100644 index 8fce575db..000000000 --- a/Frameworks/Data/BatchUpdates.swift +++ /dev/null @@ -1,76 +0,0 @@ -// -// 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() -} - diff --git a/Frameworks/Data/Data.xcodeproj/project.pbxproj b/Frameworks/Data/Data.xcodeproj/project.pbxproj index def1871ae..e0f08e543 100644 --- a/Frameworks/Data/Data.xcodeproj/project.pbxproj +++ b/Frameworks/Data/Data.xcodeproj/project.pbxproj @@ -19,7 +19,6 @@ 844BEE811F0AB4D0004AB7CD /* Author.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BEE801F0AB4D0004AB7CD /* Author.swift */; }; 844BEE831F0AB4D6004AB7CD /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BEE821F0AB4D6004AB7CD /* Attachment.swift */; }; 844BEE851F0AB4DB004AB7CD /* ArticleStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BEE841F0AB4DB004AB7CD /* ArticleStatus.swift */; }; - 844BEE871F0AB4E3004AB7CD /* BatchUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BEE861F0AB4E3004AB7CD /* BatchUpdates.swift */; }; 844BEE891F0AB4E7004AB7CD /* Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BEE881F0AB4E7004AB7CD /* Notifications.swift */; }; 84C490F41F705D5F003131D2 /* RSWeb.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84C490F51F705D5F003131D2 /* RSWeb.framework */; }; /* End PBXBuildFile section */ @@ -70,7 +69,6 @@ 844BEE801F0AB4D0004AB7CD /* Author.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Author.swift; sourceTree = ""; }; 844BEE821F0AB4D6004AB7CD /* Attachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = ""; }; 844BEE841F0AB4DB004AB7CD /* ArticleStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleStatus.swift; sourceTree = ""; }; - 844BEE861F0AB4E3004AB7CD /* BatchUpdates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatchUpdates.swift; sourceTree = ""; }; 844BEE881F0AB4E7004AB7CD /* Notifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Notifications.swift; sourceTree = ""; }; 844BEE9C1F0AB512004AB7CD /* RSCore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSCore.xcodeproj; path = ../RSCore/RSCore.xcodeproj; sourceTree = ""; }; 84C490F51F705D5F003131D2 /* RSWeb.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = RSWeb.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -106,7 +104,6 @@ 844BEE821F0AB4D6004AB7CD /* Attachment.swift */, 844BEE841F0AB4DB004AB7CD /* ArticleStatus.swift */, 840405C91F1A8E4300DF0296 /* DatabaseID.swift */, - 844BEE861F0AB4E3004AB7CD /* BatchUpdates.swift */, 844BEE881F0AB4E7004AB7CD /* Notifications.swift */, 8419741B1F6DD613006346C4 /* UnreadCountProvider.swift */, 8419741F1F6DD672006346C4 /* DisplayNameProvider.swift */, @@ -304,7 +301,6 @@ 844BEE831F0AB4D6004AB7CD /* Attachment.swift in Sources */, 8419741C1F6DD613006346C4 /* UnreadCountProvider.swift in Sources */, 841974201F6DD672006346C4 /* DisplayNameProvider.swift in Sources */, - 844BEE871F0AB4E3004AB7CD /* BatchUpdates.swift in Sources */, 844BEE811F0AB4D0004AB7CD /* Author.swift in Sources */, 840405CA1F1A8E4300DF0296 /* DatabaseID.swift in Sources */, 844BEE851F0AB4DB004AB7CD /* ArticleStatus.swift in Sources */, diff --git a/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj b/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj index 472ed023e..a42a99dfe 100755 --- a/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj +++ b/Frameworks/RSDatabase/RSDatabase.xcodeproj/project.pbxproj @@ -587,7 +587,7 @@ INFOPLIST_FILE = RSDatabase/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=200"; + OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=300"; PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.RSDatabase; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -608,7 +608,7 @@ INFOPLIST_FILE = RSDatabase/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=200"; + OTHER_SWIFT_FLAGS = "-Xfrontend -warn-long-function-bodies=300"; PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.RSDatabase; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; diff --git a/Importers/DefaultFeedsImporter.swift b/Importers/DefaultFeedsImporter.swift index ac55de3ca..0b1bf68db 100644 --- a/Importers/DefaultFeedsImporter.swift +++ b/Importers/DefaultFeedsImporter.swift @@ -41,13 +41,16 @@ struct FeedsImporter { static func importFeeds(_ feedDictionaries: [DiskFeedDictionary], account: Account) { let feedsToImport = feeds(with: feedDictionaries, accountID: account.accountID) - for feed in feedsToImport { - if !account.hasFeed(with: feed.feedID) { - let _ = account.addFeed(feed, to: nil) + + batchUpdate.perform { + for feed in feedsToImport { + if !account.hasFeed(with: feed.feedID) { + let _ = account.addFeed(feed, to: nil) + } } } } - + private static func feeds(with feedDictionaries: [DiskFeedDictionary], accountID: String) -> Set { let feedArray = feedDictionaries.flatMap { Feed(accountID: accountID, dictionary: $0) } diff --git a/Importers/OPMLImporter.swift b/Importers/OPMLImporter.swift index 6a702d8e6..ce22ab01f 100644 --- a/Importers/OPMLImporter.swift +++ b/Importers/OPMLImporter.swift @@ -38,7 +38,9 @@ struct OPMLImporter { } if let opmlDocument = opmlDocument { - account.importOPML(opmlDocument) + batchUpdate.perform { + account.importOPML(opmlDocument) + } } } }