Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for unexpected removeItems chaining behavior #60

Merged
merged 3 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/Boutique/Store.ItemRemovalStrategy.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

public extension Store {
extension Store {
/// An invalidation strategy for a `Store` instance.
///
/// An `ItemRemovalStrategy` provides control over how items are removed from the `Store`
Expand Down
41 changes: 18 additions & 23 deletions Sources/Boutique/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -385,14 +385,14 @@ public extension Store {
// Internal versions of the `insert`, `remove`, and `removeAll` function code paths so we can avoid duplicating code.
internal extension Store {
func performInsert(_ item: Item, firstRemovingExistingItems existingItemsStrategy: ItemRemovalStrategy<Item>? = nil) async throws {
var currentItems = await self.items

if let strategy = existingItemsStrategy {
// Remove items from disk and memory based on the cache invalidation strategy
try await self.removeItems(withStrategy: strategy, items: &currentItems)
var removedItems: [Item] = [item]
try await self.removeItems(&removedItems, withStrategy: strategy)
}

// Take the current items array and turn it into an OrderedDictionary.
let currentItems = await self.items
let identifier = item[keyPath: self.cacheIdentifier]
let currentItemsKeys = currentItems.map({ $0[keyPath: self.cacheIdentifier] })
var currentValuesDictionary = OrderedDictionary<String, Item>(uniqueKeys: currentItemsKeys, values: currentItems)
Expand All @@ -407,11 +407,11 @@ internal extension Store {
}

func performInsert(_ items: [Item], firstRemovingExistingItems existingItemsStrategy: ItemRemovalStrategy<Item>? = nil) async throws {
var currentItems = await self.items

if let strategy = existingItemsStrategy {
// Remove items from disk and memory based on the cache invalidation strategy
try await self.removeItems(withStrategy: strategy, items: &currentItems)
var removedItems = items
try await self.removeItems(&removedItems, withStrategy: strategy)
}

var insertedItemsDictionary = OrderedDictionary<String, Item>()
Expand All @@ -424,6 +424,7 @@ internal extension Store {
}

// Take the current items array and turn it into an OrderedDictionary.
let currentItems = await self.items
let currentItemsKeys = currentItems.map({ $0[keyPath: self.cacheIdentifier] })
var currentValuesDictionary = OrderedDictionary<String, Item>(uniqueKeys: currentItemsKeys, values: currentItems)

Expand Down Expand Up @@ -502,30 +503,24 @@ private extension Store {
try await self.storageEngine.remove(keys: itemKeys)
}

func removeItems(withStrategy strategy: ItemRemovalStrategy<Item>, items: inout [Item]) async throws {
func removeItems(_ items: inout [Item], withStrategy strategy: ItemRemovalStrategy<Item>) async throws {
let itemsToRemove = strategy.removedItems(items)

// If we're using the `.removeNone` strategy then there are no items to invalidate and we can return early
guard itemsToRemove.count != 0 else { return }

// If we're using the `.removeAll` strategy then we want to remove all the data without iterating
// Else, we're using a strategy and need to iterate over all of the `itemsToInvalidate` and invalidate them
if items.count == itemsToRemove.count {
items = []
try await self.storageEngine.removeAllData()
} else {
items = items.filter { item in
!itemsToRemove.contains(where: {
$0[keyPath: cacheIdentifier] == item[keyPath: cacheIdentifier]
}
)}
let itemKeys = items.map({ CacheKey(verbatim: $0[keyPath: self.cacheIdentifier]) })

if itemKeys.count == 1 {
try await self.storageEngine.remove(key: itemKeys[0])
} else {
try await self.storageEngine.remove(keys: itemKeys)
items = items.filter { item in
!itemsToRemove.contains(where: {
$0[keyPath: cacheIdentifier] == item[keyPath: cacheIdentifier]
}
)}

let itemKeys = itemsToRemove.map({ CacheKey(verbatim: $0[keyPath: self.cacheIdentifier]) })

if itemKeys.count == 1 {
try await self.storageEngine.remove(key: itemKeys[0])
} else {
try await self.storageEngine.remove(keys: itemKeys)
}
}
}
Loading