Remove use of forEach.

This commit is contained in:
Brent Simmons 2024-11-18 15:11:18 -08:00
parent c1c4c8b1b6
commit ba0a2d3521
25 changed files with 156 additions and 98 deletions

View File

@ -491,9 +491,11 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
} else { } else {
if let title = item.titleFromAttributes, let folder = ensureFolder(with: title) { if let title = item.titleFromAttributes, let folder = ensureFolder(with: title) {
folder.externalID = item.attributes?["nnw_externalID"] as? String folder.externalID = item.attributes?["nnw_externalID"] as? String
item.items?.forEach { itemChild in if let items = item.items {
if let feedSpecifier = itemChild.feedSpecifier { for itemChild in items {
folder.addFeed(newFeed(with: feedSpecifier)) if let feedSpecifier = itemChild.feedSpecifier {
folder.addFeed(newFeed(with: feedSpecifier))
}
} }
} }
} }
@ -521,9 +523,11 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
if topLevelFeeds.contains(feed) { if topLevelFeeds.contains(feed) {
containers.append(self) containers.append(self)
} }
folders?.forEach { folder in if let folders {
if folder.topLevelFeeds.contains(feed) { for folder in folders {
containers.append(folder) if folder.topLevelFeeds.contains(feed) {
containers.append(folder)
}
} }
} }
return containers return containers
@ -925,7 +929,9 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
public func debugDropConditionalGetInfo() { public func debugDropConditionalGetInfo() {
#if DEBUG #if DEBUG
flattenedFeeds().forEach{ $0.dropConditionalGetInfo() } for feed in flattenedFeeds() {
feed.dropConditionalGetInfo()
}
#endif #endif
} }
@ -1169,7 +1175,7 @@ private extension Account {
for article in articles where !article.status.read { for article in articles where !article.status.read {
unreadCountStorage[article.feedID, default: 0] += 1 unreadCountStorage[article.feedID, default: 0] += 1
} }
feeds.forEach { (feed) in for feed in feeds {
let unreadCount = unreadCountStorage[feed.feedID, default: 0] let unreadCount = unreadCountStorage[feed.feedID, default: 0]
feed.unreadCount = unreadCount feed.unreadCount = unreadCount
} }
@ -1220,7 +1226,7 @@ private extension Account {
var idDictionary = [String: Feed]() var idDictionary = [String: Feed]()
var externalIDDictionary = [String: Feed]() var externalIDDictionary = [String: Feed]()
flattenedFeeds().forEach { (feed) in for feed in flattenedFeeds() {
idDictionary[feed.feedID] = feed idDictionary[feed.feedID] = feed
if let externalID = feed.externalID { if let externalID = feed.externalID {
externalIDDictionary[externalID] = feed externalIDDictionary[externalID] = feed

View File

@ -215,23 +215,31 @@ public final class AccountManager: UnreadCountProvider {
public func suspendNetworkAll() { public func suspendNetworkAll() {
isSuspended = true isSuspended = true
accounts.forEach { $0.suspendNetwork() } for account in accounts {
account.suspendNetwork()
}
} }
public func suspendDatabaseAll() { public func suspendDatabaseAll() {
accounts.forEach { $0.suspendDatabase() } for account in accounts {
account.suspendDatabase()
}
} }
public func resumeAll() { public func resumeAll() {
isSuspended = false isSuspended = false
accounts.forEach { $0.resumeDatabaseAndDelegate() } for account in accounts {
accounts.forEach { $0.resume() } account.resumeDatabaseAndDelegate()
}
for account in accounts {
account.resume()
}
} }
public func receiveRemoteNotification(userInfo: [AnyHashable : Any], completion: (() -> Void)? = nil) { public func receiveRemoteNotification(userInfo: [AnyHashable : Any], completion: (() -> Void)? = nil) {
let group = DispatchGroup() let group = DispatchGroup()
activeAccounts.forEach { account in for account in activeAccounts {
group.enter() group.enter()
account.receiveRemoteNotification(userInfo: userInfo) { account.receiveRemoteNotification(userInfo: userInfo) {
group.leave() group.leave()
@ -248,7 +256,7 @@ public final class AccountManager: UnreadCountProvider {
let group = DispatchGroup() let group = DispatchGroup()
activeAccounts.forEach { account in for account in activeAccounts {
group.enter() group.enter()
account.refreshAll() { result in account.refreshAll() { result in
group.leave() group.leave()
@ -272,7 +280,7 @@ public final class AccountManager: UnreadCountProvider {
var syncErrors = [AccountSyncError]() var syncErrors = [AccountSyncError]()
let group = DispatchGroup() let group = DispatchGroup()
activeAccounts.forEach { account in for account in activeAccounts {
group.enter() group.enter()
account.refreshAll() { result in account.refreshAll() { result in
group.leave() group.leave()
@ -297,9 +305,9 @@ public final class AccountManager: UnreadCountProvider {
public func sendArticleStatusAll(completion: (() -> Void)? = nil) { public func sendArticleStatusAll(completion: (() -> Void)? = nil) {
let group = DispatchGroup() let group = DispatchGroup()
activeAccounts.forEach { for account in activeAccounts {
group.enter() group.enter()
$0.sendArticleStatus() { _ in account.sendArticleStatus() { _ in
group.leave() group.leave()
} }
} }
@ -312,9 +320,9 @@ public final class AccountManager: UnreadCountProvider {
public func syncArticleStatusAll(completion: (() -> Void)? = nil) { public func syncArticleStatusAll(completion: (() -> Void)? = nil) {
let group = DispatchGroup() let group = DispatchGroup()
activeAccounts.forEach { for account in activeAccounts {
group.enter() group.enter()
$0.syncArticleStatus() { _ in account.syncArticleStatus() { _ in
group.leave() group.leave()
} }
} }
@ -325,7 +333,9 @@ public final class AccountManager: UnreadCountProvider {
} }
public func saveAll() { public func saveAll() {
accounts.forEach { $0.save() } for account in accounts {
account.save()
}
} }
public func anyAccountHasAtLeastOneFeed() -> Bool { public func anyAccountHasAtLeastOneFeed() -> Bool {
@ -481,13 +491,15 @@ private extension AccountManager {
filenames = filenames?.sorted() filenames = filenames?.sorted()
filenames?.forEach { (oneFilename) in if let filenames {
guard oneFilename != defaultAccountFolderName else { for oneFilename in filenames {
return guard oneFilename != defaultAccountFolderName else {
} continue
if let oneAccount = loadAccount(oneFilename) { }
if !duplicateServiceAccount(oneAccount) { if let oneAccount = loadAccount(oneFilename) {
accountsDictionary[oneAccount.accountID] = oneAccount if !duplicateServiceAccount(oneAccount) {
accountsDictionary[oneAccount.accountID] = oneAccount
}
} }
} }
} }

View File

@ -77,9 +77,11 @@ final class CloudKitAccountZone: CloudKitZone {
if let title = item.titleFromAttributes { if let title = item.titleFromAttributes {
let containerRecord = newContainerCKRecord(name: title) let containerRecord = newContainerCKRecord(name: title)
records.append(containerRecord) records.append(containerRecord)
item.items?.forEach { itemChild in if let items = item.items {
if let feedSpecifier = itemChild.feedSpecifier { for itemChild in items {
processFeed(feedSpecifier: feedSpecifier, containerExternalID: containerRecord.externalID) if let feedSpecifier = itemChild.feedSpecifier {
processFeed(feedSpecifier: feedSpecifier, containerExternalID: containerRecord.externalID)
}
} }
} }
} }

View File

@ -84,9 +84,9 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
func removeFeed(_ externalID: String) { func removeFeed(_ externalID: String) {
if let feed = account?.existingFeed(withExternalID: externalID), let containers = account?.existingContainers(withFeed: feed) { if let feed = account?.existingFeed(withExternalID: externalID), let containers = account?.existingContainers(withFeed: feed) {
containers.forEach { for container in containers {
feed.dropConditionalGetInfo() feed.dropConditionalGetInfo()
$0.removeFeed(feed) container.removeFeed(feed)
} }
} }
} }

View File

@ -38,7 +38,9 @@ final class FeedMetadataFile {
let decoder = PropertyListDecoder() let decoder = PropertyListDecoder()
account.feedMetadata = (try? decoder.decode(Account.FeedMetadataDictionary.self, from: fileData)) ?? Account.FeedMetadataDictionary() account.feedMetadata = (try? decoder.decode(Account.FeedMetadataDictionary.self, from: fileData)) ?? Account.FeedMetadataDictionary()
} }
account.feedMetadata.values.forEach { $0.delegate = account } for value in account.feedMetadata.values {
value.delegate = account
}
} }
func save() { func save() {

View File

@ -761,7 +761,7 @@ private extension FeedbinAccountDelegate {
// Feedbin has a tag that we don't have a folder for. We might not get a new // Feedbin has a tag that we don't have a folder for. We might not get a new
// taggings response for it if it is a folder rename. Force expire the tagging // taggings response for it if it is a folder rename. Force expire the tagging
// so that we will for sure get the new tagging information. // so that we will for sure get the new tagging information.
tags.forEach { tag in for tag in tags {
if !folderNames.contains(tag.name) { if !folderNames.contains(tag.name) {
accountMetadata?.conditionalGetInfo[FeedbinAPICaller.ConditionalGetKeys.taggings] = nil accountMetadata?.conditionalGetInfo[FeedbinAPICaller.ConditionalGetKeys.taggings] = nil
} }
@ -779,7 +779,7 @@ private extension FeedbinAccountDelegate {
// Delete any folders not at Feedbin // Delete any folders not at Feedbin
if let folders = account.folders { if let folders = account.folders {
folders.forEach { folder in for folder in folders {
if !tagNames.contains(folder.name ?? "") { if !tagNames.contains(folder.name ?? "") {
for feed in folder.topLevelFeeds { for feed in folder.topLevelFeeds {
account.addFeed(feed) account.addFeed(feed)
@ -799,7 +799,7 @@ private extension FeedbinAccountDelegate {
}() }()
// Make any folders Feedbin has, but we don't // Make any folders Feedbin has, but we don't
tagNames.forEach { tagName in for tagName in tagNames {
if !folderNames.contains(tagName) { if !folderNames.contains(tagName) {
_ = account.ensureFolder(with: tagName) _ = account.ensureFolder(with: tagName)
} }
@ -835,7 +835,7 @@ private extension FeedbinAccountDelegate {
// Add any feeds we don't have and update any we do // Add any feeds we don't have and update any we do
var subscriptionsToAdd = Set<FeedbinSubscription>() var subscriptionsToAdd = Set<FeedbinSubscription>()
subscriptions.forEach { subscription in for subscription in subscriptions {
let subFeedId = String(subscription.feedID) let subFeedId = String(subscription.feedID)
@ -854,7 +854,7 @@ private extension FeedbinAccountDelegate {
} }
// Actually add subscriptions all in one go, so we dont trigger various rebuilding things that Account does. // Actually add subscriptions all in one go, so we dont trigger various rebuilding things that Account does.
subscriptionsToAdd.forEach { subscription in for subscription in subscriptionsToAdd {
let feed = account.createFeed(with: subscription.name, url: subscription.url, feedID: String(subscription.feedID), homePageURL: subscription.homePageURL) let feed = account.createFeed(with: subscription.name, url: subscription.url, feedID: String(subscription.feedID), homePageURL: subscription.homePageURL)
feed.externalID = String(subscription.subscriptionID) feed.externalID = String(subscription.subscriptionID)
account.addFeed(feed) account.addFeed(feed)

View File

@ -95,7 +95,7 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation {
} }
os_log(.debug, log: log, "Processing %i feeds.", feedsAndFolders.count) os_log(.debug, log: log, "Processing %i feeds.", feedsAndFolders.count)
feedsAndFolders.forEach { (feed, folder) in for (feed, folder) in feedsAndFolders {
if !folder.has(feed) { if !folder.has(feed) {
folder.addFeed(feed) folder.addFeed(feed)
} }

View File

@ -46,7 +46,7 @@ extension NewsBlurAccountDelegate {
// Delete any folders not at NewsBlur // Delete any folders not at NewsBlur
if let folders = account.folders { if let folders = account.folders {
folders.forEach { folder in for folder in folders {
if !folderNames.contains(folder.name ?? "") { if !folderNames.contains(folder.name ?? "") {
for feed in folder.topLevelFeeds { for feed in folder.topLevelFeeds {
account.addFeed(feed) account.addFeed(feed)
@ -67,7 +67,7 @@ extension NewsBlurAccountDelegate {
// Make any folders NewsBlur has, but we don't // Make any folders NewsBlur has, but we don't
// Ignore account-level folder // Ignore account-level folder
folderNames.forEach { folderName in for folderName in folderNames {
if !accountFolderNames.contains(folderName) && folderName != " " { if !accountFolderNames.contains(folderName) && folderName != " " {
_ = account.ensureFolder(with: folderName) _ = account.ensureFolder(with: folderName)
} }
@ -118,8 +118,8 @@ extension NewsBlurAccountDelegate {
} }
// Actually add feeds all in one go, so we dont trigger various rebuilding things that Account does. // Actually add feeds all in one go, so we dont trigger various rebuilding things that Account does.
feedsToAdd.forEach { feed in for feedToAdd in feedsToAdd {
let feed = account.createFeed(with: feed.name, url: feed.feedURL, feedID: String(feed.feedID), homePageURL: feed.homePageURL) let feed = account.createFeed(with: feedToAdd.name, url: feedToAdd.feedURL, feedID: String(feedToAdd.feedID), homePageURL: feedToAdd.homePageURL)
feed.externalID = String(feed.feedID) feed.externalID = String(feed.feedID)
account.addFeed(feed) account.addFeed(feed)
} }

View File

@ -22,13 +22,13 @@ final class OPMLNormalizer {
private func normalize(_ items: [OPMLItem], parentFolder: OPMLItem? = nil) { private func normalize(_ items: [OPMLItem], parentFolder: OPMLItem? = nil) {
var feedsToAdd = [OPMLItem]() var feedsToAdd = [OPMLItem]()
items.forEach { (item) in for item in items {
if let _ = item.feedSpecifier { if let _ = item.feedSpecifier {
if !feedsToAdd.contains(where: { $0.feedSpecifier?.feedURL == item.feedSpecifier?.feedURL }) { if !feedsToAdd.contains(where: { $0.feedSpecifier?.feedURL == item.feedSpecifier?.feedURL }) {
feedsToAdd.append(item) feedsToAdd.append(item)
} }
return continue
} }
guard let _ = item.titleFromAttributes else { guard let _ = item.titleFromAttributes else {
@ -36,7 +36,7 @@ final class OPMLNormalizer {
if let itemChildren = item.items { if let itemChildren = item.items {
normalize(itemChildren, parentFolder: parentFolder) normalize(itemChildren, parentFolder: parentFolder)
} }
return continue
} }
feedsToAdd.append(item) feedsToAdd.append(item)
@ -60,7 +60,5 @@ final class OPMLNormalizer {
normalizedOPMLItems.append(feed) normalizedOPMLItems.append(feed)
} }
} }
} }
} }

View File

@ -718,7 +718,7 @@ private extension ReaderAPIAccountDelegate {
// Delete any folders not at Reader // Delete any folders not at Reader
if let folders = account.folders { if let folders = account.folders {
folders.forEach { folder in for folder in folders {
if !readerFolderExternalIDs.contains(folder.externalID ?? "") { if !readerFolderExternalIDs.contains(folder.externalID ?? "") {
for feed in folder.topLevelFeeds { for feed in folder.topLevelFeeds {
account.addFeed(feed) account.addFeed(feed)
@ -738,7 +738,7 @@ private extension ReaderAPIAccountDelegate {
}() }()
// Make any folders Reader has, but we don't // Make any folders Reader has, but we don't
folderTags.forEach { tag in for tag in folderTags {
if !folderExternalIDs.contains(tag.tagID) { if !folderExternalIDs.contains(tag.tagID) {
let folder = account.ensureFolder(with: tag.folderName ?? "None") let folder = account.ensureFolder(with: tag.folderName ?? "None")
folder?.externalID = tag.tagID folder?.externalID = tag.tagID
@ -775,7 +775,7 @@ private extension ReaderAPIAccountDelegate {
} }
// Add any feeds we don't have and update any we do // Add any feeds we don't have and update any we do
subscriptions.forEach { subscription in for subscription in subscriptions {
if let feed = account.existingFeed(withFeedID: subscription.feedID) { if let feed = account.existingFeed(withFeedID: subscription.feedID) {
feed.name = subscription.name feed.name = subscription.name
@ -801,14 +801,14 @@ private extension ReaderAPIAccountDelegate {
let taggingsDict = subscriptions.reduce([String: [ReaderAPISubscription]]()) { (dict, subscription) in let taggingsDict = subscriptions.reduce([String: [ReaderAPISubscription]]()) { (dict, subscription) in
var taggedFeeds = dict var taggedFeeds = dict
subscription.categories.forEach({ (category) in for category in subscription.categories {
if var taggedFeed = taggedFeeds[category.categoryId] { if var taggedFeed = taggedFeeds[category.categoryId] {
taggedFeed.append(subscription) taggedFeed.append(subscription)
taggedFeeds[category.categoryId] = taggedFeed taggedFeeds[category.categoryId] = taggedFeed
} else { } else {
taggedFeeds[category.categoryId] = [subscription] taggedFeeds[category.categoryId] = [subscription]
} }
}) }
return taggedFeeds return taggedFeeds
} }

View File

@ -132,7 +132,9 @@ final class SearchTable: DatabaseTable {
private extension SearchTable { private extension SearchTable {
func performInitialIndexForArticles(_ articles: Set<ArticleSearchInfo>, _ database: FMDatabase) { func performInitialIndexForArticles(_ articles: Set<ArticleSearchInfo>, _ database: FMDatabase) {
articles.forEach { performInitialIndex($0, database) } for article in articles {
performInitialIndex(article, database)
}
} }
func performInitialIndex(_ article: ArticleSearchInfo, _ database: FMDatabase) { func performInitialIndex(_ article: ArticleSearchInfo, _ database: FMDatabase) {
@ -177,7 +179,7 @@ private extension SearchTable {
let groupedSearchInfos = Dictionary(grouping: searchInfos, by: { $0.rowID }) let groupedSearchInfos = Dictionary(grouping: searchInfos, by: { $0.rowID })
let searchInfosDictionary = groupedSearchInfos.mapValues { $0.first! } let searchInfosDictionary = groupedSearchInfos.mapValues { $0.first! }
articles.forEach { (article) in for article in articles {
updateIndexForArticle(article, searchInfosDictionary, database) updateIndexForArticle(article, searchInfosDictionary, database)
} }
} }

View File

@ -59,7 +59,9 @@ struct QueueCall: Equatable {
guard !isPaused else { return } guard !isPaused else { return }
let callsToMake = calls // Make a copy in case calls are added to the queue while performing calls. let callsToMake = calls // Make a copy in case calls are added to the queue while performing calls.
resetCalls() resetCalls()
callsToMake.forEach { $0.perform() } for call in callsToMake {
call.perform()
}
} }
@objc func timerDidFire(_ sender: Any?) { @objc func timerDidFire(_ sender: Any?) {

View File

@ -68,7 +68,9 @@ public final class MainThreadOperationQueue {
/// Add multiple operations to the queue. /// Add multiple operations to the queue.
/// This has the same effect as calling addOperation one-by-one. /// This has the same effect as calling addOperation one-by-one.
public func addOperations(_ operations: [MainThreadOperation]) { public func addOperations(_ operations: [MainThreadOperation]) {
operations.forEach{ add($0) } for operation in operations {
add(operation)
}
} }
/// Add a dependency. Do this *before* calling addOperation, since addOperation might run the operation right away. /// Add a dependency. Do this *before* calling addOperation, since addOperation might run the operation right away.
@ -453,16 +455,22 @@ private extension MainThreadOperationDependencies {
} }
func removeDependencies(_ parentOperationIDs: [Int]) { func removeDependencies(_ parentOperationIDs: [Int]) {
parentOperationIDs.forEach { dependencies[$0] = nil } for parentOperationID in parentOperationIDs {
dependencies[parentOperationID] = nil
}
} }
func removeChildOperationIDs(_ operationIDs: [Int]) { func removeChildOperationIDs(_ operationIDs: [Int]) {
operationIDs.forEach{ removeChildOperationID($0) } for operationID in operationIDs {
removeChildOperationID(operationID)
}
removeEmptyDependencies() removeEmptyDependencies()
} }
func removeChildOperationID(_ operationID: Int) { func removeChildOperationID(_ operationID: Int) {
dependencies.values.forEach{ $0.remove(operationID) } for value in dependencies.values {
value.remove(operationID)
}
} }
func removeEmptyDependencies() { func removeEmptyDependencies() {

View File

@ -67,10 +67,12 @@ public extension UndoableCommandRunner {
// Otherwise things like Redo Mark Read are ambiguous. // Otherwise things like Redo Mark Read are ambiguous.
// (Do they apply to the previous articles or to the current articles?) // (Do they apply to the previous articles or to the current articles?)
guard let undoManager = undoManager else { guard let undoManager else {
return return
} }
undoableCommands.forEach { undoManager.removeAllActions(withTarget: $0) } for undoableCommand in undoableCommands {
undoManager.removeAllActions(withTarget: undoableCommand)
}
undoableCommands = [UndoableCommand]() undoableCommands = [UndoableCommand]()
} }
} }

View File

@ -19,8 +19,12 @@ extension UIViewController {
} }
public func replaceChildAndPinView(_ controller: UIViewController) { public func replaceChildAndPinView(_ controller: UIViewController) {
view.subviews.forEach { $0.removeFromSuperview() } for subview in view.subviews {
children.forEach { $0.removeFromParent() } subview.removeFromSuperview()
}
for child in children {
child.removeFromParent()
}
addChildAndPinView(controller) addChildAndPinView(controller)
} }

View File

@ -62,7 +62,7 @@ public extension DatabaseTable {
func insertRows(_ dictionaries: [DatabaseDictionary], insertType: RSDatabaseInsertType, in database: FMDatabase) { func insertRows(_ dictionaries: [DatabaseDictionary], insertType: RSDatabaseInsertType, in database: FMDatabase) {
dictionaries.forEach { (oneDictionary) in for oneDictionary in dictionaries {
let _ = database.rs_insertRow(with: oneDictionary, insertType: insertType, tableName: self.name) let _ = database.rs_insertRow(with: oneDictionary, insertType: insertType, tableName: self.name)
} }
} }

View File

@ -89,7 +89,7 @@ private extension TreeController {
func visitNode(_ node: Node, _ visitBlock: NodeVisitBlock) { func visitNode(_ node: Node, _ visitBlock: NodeVisitBlock) {
visitBlock(node) visitBlock(node)
node.childNodes.forEach{ (oneChildNode) in for oneChildNode in node.childNodes {
visitNode(oneChildNode, visitBlock) visitNode(oneChildNode, visitBlock)
} }
} }
@ -124,7 +124,7 @@ private extension TreeController {
node.childNodes = childNodes node.childNodes = childNodes
} }
childNodes.forEach{ (oneChildNode) in for oneChildNode in childNodes {
if rebuildChildNodes(node: oneChildNode) { if rebuildChildNodes(node: oneChildNode) {
childNodesDidChange = true childNodesDidChange = true
} }

View File

@ -64,9 +64,15 @@ public protocol DownloadSessionDelegate {
public func cancelAll() { public func cancelAll() {
urlSession.getTasksWithCompletionHandler { dataTasks, uploadTasks, downloadTasks in urlSession.getTasksWithCompletionHandler { dataTasks, uploadTasks, downloadTasks in
dataTasks.forEach { $0.cancel() } for dataTask in dataTasks {
uploadTasks.forEach { $0.cancel() } dataTask.cancel()
downloadTasks.forEach { $0.cancel() } }
for uploadTask in uploadTasks {
uploadTask.cancel()
}
for downloadTask in downloadTasks {
downloadTask.cancel()
}
} }
} }

View File

@ -28,11 +28,11 @@ public struct HTTPLinkPagingInfo {
let links = linkHeader.components(separatedBy: ",") let links = linkHeader.components(separatedBy: ",")
var dict: [String: String] = [:] var dict: [String: String] = [:]
links.forEach({ for link in links {
let components = $0.components(separatedBy:"; ") let components = link.components(separatedBy:"; ")
let page = components[0].trimmingCharacters(in: CharacterSet(charactersIn: " <>")) let page = components[0].trimmingCharacters(in: CharacterSet(charactersIn: " <>"))
dict[components[1]] = page dict[components[1]] = page
}) }
self.init(nextPage: dict["rel=\"next\""], lastPage: dict["rel=\"last\""]) self.init(nextPage: dict["rel=\"next\""], lastPage: dict["rel=\"last\""])

View File

@ -88,7 +88,7 @@ private final class WebCache {
func cleanup(_ cleanupInterval: TimeInterval) { func cleanup(_ cleanupInterval: TimeInterval) {
let cutoffDate = Date(timeInterval: -cleanupInterval, since: Date()) let cutoffDate = Date(timeInterval: -cleanupInterval, since: Date())
cache.keys.forEach { (key) in for key in cache.keys {
let cacheRecord = self[key]! let cacheRecord = self[key]!
if shouldDelete(cacheRecord, cutoffDate) { if shouldDelete(cacheRecord, cutoffDate) {
cache[key] = nil cache[key] = nil
@ -168,7 +168,7 @@ private final class DownloadWithCacheManager {
} }
var callbackCount = 0 var callbackCount = 0
self.pendingCallbacks.forEach{ (callbackRecord) in for callbackRecord in self.pendingCallbacks {
if url == callbackRecord.url { if url == callbackRecord.url {
callbackRecord.completion(data, response, error) callbackRecord.completion(data, response, error)
callbackCount += 1 callbackCount += 1

View File

@ -134,9 +134,15 @@ extension URLSession: Transport {
public func cancelAll() { public func cancelAll() {
getTasksWithCompletionHandler { dataTasks, uploadTasks, downloadTasks in getTasksWithCompletionHandler { dataTasks, uploadTasks, downloadTasks in
dataTasks.forEach { $0.cancel() } for dataTask in dataTasks {
uploadTasks.forEach { $0.cancel() } dataTask.cancel()
downloadTasks.forEach { $0.cancel() } }
for uploadTask in uploadTasks {
uploadTask.cancel()
}
for downloadTask in downloadTasks {
downloadTask.cancel()
}
} }
} }

View File

@ -66,7 +66,7 @@ final class SmartFeed: PseudoFeed {
// Remove any accounts that are no longer active or have been deleted // Remove any accounts that are no longer active or have been deleted
let activeAccountIDs = activeAccounts.map { $0.accountID } let activeAccountIDs = activeAccounts.map { $0.accountID }
unreadCounts.keys.forEach { accountID in for accountID in unreadCounts.keys {
if !activeAccountIDs.contains(accountID) { if !activeAccountIDs.contains(accountID) {
unreadCounts.removeValue(forKey: accountID) unreadCounts.removeValue(forKey: accountID)
} }
@ -75,7 +75,9 @@ final class SmartFeed: PseudoFeed {
if activeAccounts.isEmpty { if activeAccounts.isEmpty {
updateUnreadCount() updateUnreadCount()
} else { } else {
activeAccounts.forEach { self.fetchUnreadCount(for: $0) } for account in activeAccounts {
self.fetchUnreadCount(for: account)
}
} }
} }

View File

@ -24,7 +24,9 @@ final class FetchRequestQueue {
func cancelAllRequests() { func cancelAllRequests() {
precondition(Thread.isMainThread) precondition(Thread.isMainThread)
pendingRequests.forEach { $0.isCanceled = true } for request in pendingRequests {
request.isCanceled = true
}
currentRequest?.isCanceled = true currentRequest?.isCanceled = true
pendingRequests = [FetchRequestOperation]() pendingRequests = [FetchRequestOperation]()
} }

View File

@ -82,12 +82,12 @@ private extension FeedTreeControllerDelegate {
var updatedChildNodes = [Node]() var updatedChildNodes = [Node]()
children.forEach { (representedObject) in for representedObject in children {
if let existingNode = containerNode.childNodeRepresentingObject(representedObject) { if let existingNode = containerNode.childNodeRepresentingObject(representedObject) {
if !updatedChildNodes.contains(existingNode) { if !updatedChildNodes.contains(existingNode) {
updatedChildNodes += [existingNode] updatedChildNodes += [existingNode]
return continue
} }
} }

View File

@ -452,12 +452,14 @@ class TimelineViewController: UITableViewController, UndoableCommandRunner {
guard let feed = note.userInfo?[UserInfoKey.feed] as? Feed else { guard let feed = note.userInfo?[UserInfoKey.feed] as? Feed else {
return return
} }
for indexPath in tableView.indexPathsForVisibleRows? { if let indexPaths = tableView.indexPathsForVisibleRows {
guard let article = dataSource.itemIdentifier(for: indexPath) else { for indexPath in indexPaths {
continue guard let article = dataSource.itemIdentifier(for: indexPath) else {
} continue
if article.feed == feed, let cell = tableView.cellForRow(at: indexPath) as? TimelineTableViewCell, let image = iconImageFor(article) { }
cell.setIconImage(image) if article.feed == feed, let cell = tableView.cellForRow(at: indexPath) as? TimelineTableViewCell, let image = iconImageFor(article) {
cell.setIconImage(image)
}
} }
} }
} }
@ -466,13 +468,15 @@ class TimelineViewController: UITableViewController, UndoableCommandRunner {
guard coordinator.showIcons, let avatarURL = note.userInfo?[UserInfoKey.url] as? String else { guard coordinator.showIcons, let avatarURL = note.userInfo?[UserInfoKey.url] as? String else {
return return
} }
for indexPath in tableView.indexPathsForVisibleRows? { if let indexPaths = tableView.indexPathsForVisibleRows {
guard let article = dataSource.itemIdentifier(for: indexPath), let authors = article.authors, !authors.isEmpty else { for indexPath in indexPaths {
continue guard let article = dataSource.itemIdentifier(for: indexPath), let authors = article.authors, !authors.isEmpty else {
} continue
for author in authors { }
if author.avatarURL == avatarURL, let cell = tableView.cellForRow(at: indexPath) as? TimelineTableViewCell, let image = iconImageFor(article) { for author in authors {
cell.setIconImage(image) if author.avatarURL == avatarURL, let cell = tableView.cellForRow(at: indexPath) as? TimelineTableViewCell, let image = iconImageFor(article) {
cell.setIconImage(image)
}
} }
} }
} }