Skip to content

Add shortcut to clear cache#454

Open
phil1995 wants to merge 4 commits intodevelopfrom
feature/clear-cache-shortcut
Open

Add shortcut to clear cache#454
phil1995 wants to merge 4 commits intodevelopfrom
feature/clear-cache-shortcut

Conversation

@phil1995
Copy link
Copy Markdown
Collaborator

Adds an shortcut to clear the cache by adding a new AppIntent.
As part of this PR I also migrated away from the dependency I introduced 1-2 years ago to the official one swift-dependencies as I hit a wall with the simplified version. Also it no longer hits the build time, is better supported for future Swift versions and has nice feature like automatically created mock with the @DependencyClient macro.
I'm already sorry for the huge diff which mainly comes from the migration to the official swift-dependencies package 🙈

Simulator Screenshot - iPhone 17 Pro - 2026-04-11 at 11 18 15

To test the feature:

  1. Open the Shortcuts app
  2. Check that you can see a new suggestion Clear Cache
  3. Create a new shortcut with it
  4. Open the Cryptomator app and check in settings that your cache is currently > 0MB
  5. Trigger the new shortcut
  6. Open the Cryptomator app and check in settings that your cache is now empty

Warning

Localization for the newly added strings is still missing

@phil1995 phil1995 requested a review from tobihagemann April 11, 2026 09:26
@phil1995
Copy link
Copy Markdown
Collaborator Author

I have no idea why I can't link #393 in the sidebar but this would resolve this issue 😬

@phil1995 phil1995 changed the title Feature/clear cache shortcut Add shortcut to clear cache Apr 11, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 11, 2026

Walkthrough

Added a cache abstraction and XPC-backed implementation (protocol CacheControlling and XPCCacheController), App Intent ClearCacheAppIntent and CryptomatorAppShortcutsProvider, wired SettingsViewModel to use the new cacheController dependency, updated SwiftPM pins to Point‑Free packages and CryptomatorCommon Package.swift, added three new Swift files and unit tests for the XPC cache controller, added localized strings, and refactored many tests to replace global DependencyValues.mockDependency(...) with scoped withDependencies { ... } operation: { ... }.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add shortcut to clear cache' directly and concisely summarizes the primary change: introducing a new app shortcut for clearing the cache, which is the main feature added in this PR.
Description check ✅ Passed The description clearly explains the feature being added (AppIntent for clearing cache), the motivations for the large diff (migration to official swift-dependencies package), and provides comprehensive testing instructions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/clear-cache-shortcut

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
CryptomatorFileProviderTests/Middleware/TaskExecutor/ItemEnumerationTaskTests.swift (1)

235-237: Consider asserting itemIdentifier alongside filename/parent to keep test strength.

The new comparison avoids reference-equality issues, but checking itemIdentifier too would better catch regressions.

Suggested assertion additions
 XCTAssertEqual(expectedRootFolderFileProviderItems.map(\.filename), fileProviderItemList.items.map(\.filename))
 XCTAssertEqual(expectedRootFolderFileProviderItems.map(\.parentItemIdentifier), fileProviderItemList.items.map(\.parentItemIdentifier))
+XCTAssertEqual(expectedRootFolderFileProviderItems.map(\.itemIdentifier), fileProviderItemList.items.map(\.itemIdentifier))
 ...
 XCTAssertEqual(expectedSubFolderFileProviderItems.map(\.filename), fileProviderItemList.items.map(\.filename))
 XCTAssertEqual(expectedSubFolderFileProviderItems.map(\.parentItemIdentifier), fileProviderItemList.items.map(\.parentItemIdentifier))
+XCTAssertEqual(expectedSubFolderFileProviderItems.map(\.itemIdentifier), fileProviderItemList.items.map(\.itemIdentifier))

Also applies to: 256-258, 304-306, 316-317

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@CryptomatorFileProviderTests/Middleware/TaskExecutor/ItemEnumerationTaskTests.swift`
around lines 235 - 237, Add assertions comparing itemIdentifier alongside
filename and parentItemIdentifier in the ItemEnumerationTaskTests to strengthen
equality checks; specifically, for the existing comparisons that use
expectedRootFolderFileProviderItems.map(\.filename) and
.map(\.parentItemIdentifier) vs fileProviderItemList.items.map(...), also assert
expectedRootFolderFileProviderItems.map(\.itemIdentifier) ==
fileProviderItemList.items.map(\.itemIdentifier). Apply the same additional
assertion pattern to the other failing spots referenced (the comparisons around
the variables expectedRootFolderFileProviderItems, fileProviderItemList.items,
and metadataManagerMock.cachedMetadata.count at the other test locations).
Cryptomator/Settings/XPCCacheController.swift (1)

46-49: Use an unimplemented stub for testValue instead of the live XPC controller.

Line 47 currently allows tests to fall back to real XPC behavior, weakening isolation and masking missing overrides. Per swift-dependencies conventions, side-effectful dependencies should have testValue use failing closures (e.g., via @DependencyClient macro or manual implementations that throw/fail if called). This ensures tests fail explicitly if unmocked side effects run rather than silently using live behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Cryptomator/Settings/XPCCacheController.swift` around lines 46 - 49, The test
dependency `CacheControllerKey.testValue` currently returns the live
`XPCCacheController`, allowing tests to hit real XPC; change
`CacheControllerKey.testValue` to an unimplemented/failing stub that conforms to
`CacheControlling` (e.g., a struct or closures that call fatalError/throw when
any method is invoked) so tests must explicitly provide a mock, while keeping
`CacheControllerKey.liveValue = XPCCacheController()` unchanged; update or add
the stub type referenced by `testValue` and ensure it implements the same
`CacheControlling` API as `XPCCacheController`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Cryptomator/Settings/ClearCacheAppIntent.swift`:
- Around line 5-7: The intent strings in ClearCacheAppIntent (static properties
title, description, dialog, shortTitle and any other user-facing phrases) are
hard-coded in English; replace them with localized
LocalizedStringResource/LocalizedStringKey references and add corresponding
translations in the app’s Localizable.strings (and any Intent-specific strings
files) for all supported languages; update ClearCacheAppIntent to use
NSLocalizedString or LocalizedStringResource initializers (e.g.,
LocalizedStringResource("clear_cache_title")) and ensure matching keys are added
for title, description, dialog, shortTitle and other phrases so the intent is
fully localized across supported languages.

In `@CryptomatorFileProviderTests/FileImportingServiceSourceTests.swift`:
- Around line 34-43: The test is scoping the mock fullVersionChecker only for
construction but FileImportingServiceSource uses the `@Dependency-wrapped`
fullVersionChecker later (e.g., in importFile()), causing resolution to occur
outside the withDependencies scope; fix by making the dependency stable at
instance level—either capture and store the resolved fullVersionChecker during
init (add a stored property on FileImportingServiceSource and assign
Dependency(\.fullVersionChecker) or pass the checker into the initializer) or
ensure tests call importFile() inside the same withDependencies block; update
FileImportingServiceSource initializer and usages (constructor, importFile(),
any other methods accessing fullVersionChecker) to use the stored checker
instead of resolving the `@Dependency` at access time.

In `@CryptomatorFileProviderTests/PermissionProviderImplTests.swift`:
- Around line 24-29: The test scopes dependency overrides only during
PermissionProviderImpl() creation, but PermissionProviderImpl reads `@Dependency`
properties inside getPermissions() and getPermissionsForRootItem(), so the mocks
(hubRepositoryMock, fullVersionCheckerMock) must be active when those methods
run; move the calls to getPermissions() and getPermissionsForRootItem() inside
the same withDependencies block that creates PermissionProviderImpl(), or
alternatively wrap the entire test (both initialization and subsequent method
invocations) in withDependencies so PermissionProviderImpl uses the mocked
dependencies at call time.

In
`@CryptomatorFileProviderTests/ServiceSource/CacheManagingServiceSourceTests.swift`:
- Line 87: The test currently uses [testItem].compactMap { $0 } which can mask a
nil setup; first add an explicit non-nil assertion for testItem (e.g.
XCTAssertNotNil(testItem)) and then compare the unwrapped value directly against
notificatorMock.signalUpdateForReceivedInvocations (for example assert equality
between [testItem!] and notificatorMock.signalUpdateForReceivedInvocations as?
[FileProviderItem]); update the assertion in CacheManagingServiceSourceTests
(referencing testItem and notificatorMock.signalUpdateForReceivedInvocations) to
use the unwrapped value so a nil testItem fails explicitly.

---

Nitpick comments:
In `@Cryptomator/Settings/XPCCacheController.swift`:
- Around line 46-49: The test dependency `CacheControllerKey.testValue`
currently returns the live `XPCCacheController`, allowing tests to hit real XPC;
change `CacheControllerKey.testValue` to an unimplemented/failing stub that
conforms to `CacheControlling` (e.g., a struct or closures that call
fatalError/throw when any method is invoked) so tests must explicitly provide a
mock, while keeping `CacheControllerKey.liveValue = XPCCacheController()`
unchanged; update or add the stub type referenced by `testValue` and ensure it
implements the same `CacheControlling` API as `XPCCacheController`.

In
`@CryptomatorFileProviderTests/Middleware/TaskExecutor/ItemEnumerationTaskTests.swift`:
- Around line 235-237: Add assertions comparing itemIdentifier alongside
filename and parentItemIdentifier in the ItemEnumerationTaskTests to strengthen
equality checks; specifically, for the existing comparisons that use
expectedRootFolderFileProviderItems.map(\.filename) and
.map(\.parentItemIdentifier) vs fileProviderItemList.items.map(...), also assert
expectedRootFolderFileProviderItems.map(\.itemIdentifier) ==
fileProviderItemList.items.map(\.itemIdentifier). Apply the same additional
assertion pattern to the other failing spots referenced (the comparisons around
the variables expectedRootFolderFileProviderItems, fileProviderItemList.items,
and metadataManagerMock.cachedMetadata.count at the other test locations).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ed971909-428f-4655-8570-8bc324eac228

📥 Commits

Reviewing files that changed from the base of the PR and between 6a741fa and 25689e0.

📒 Files selected for processing (27)
  • Cryptomator.xcodeproj/project.pbxproj
  • Cryptomator.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • Cryptomator/Settings/ClearCacheAppIntent.swift
  • Cryptomator/Settings/SettingsViewModel.swift
  • Cryptomator/Settings/XPCCacheController.swift
  • CryptomatorCommon/Package.swift
  • CryptomatorCommon/Tests/CryptomatorCommonCoreTests/FullVersionCheckerTests.swift
  • CryptomatorCommon/Tests/CryptomatorCommonCoreTests/Hub/HubAuthenticationViewModelTests.swift
  • CryptomatorFileProviderTests/FileImportingServiceSourceTests.swift
  • CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterEnumerateItemTests.swift
  • CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterImportDocumentTests.swift
  • CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterTestCase.swift
  • CryptomatorFileProviderTests/FileProviderEnumeratorTests.swift
  • CryptomatorFileProviderTests/FileProviderItemTests.swift
  • CryptomatorFileProviderTests/FileProviderNotificatorTests.swift
  • CryptomatorFileProviderTests/Middleware/TaskExecutor/ItemEnumerationTaskTests.swift
  • CryptomatorFileProviderTests/PermissionProviderImplTests.swift
  • CryptomatorFileProviderTests/ServiceSource/CacheManagingServiceSourceTests.swift
  • CryptomatorFileProviderTests/WorkingSetObserverTests.swift
  • CryptomatorTests/ChangePasswordViewModelTests.swift
  • CryptomatorTests/DatabaseManagerTests.swift
  • CryptomatorTests/MoveVaultViewModelTests.swift
  • CryptomatorTests/RenameVaultViewModelTests.swift
  • CryptomatorTests/SettingsViewModelTests.swift
  • CryptomatorTests/VaultKeepUnlockedViewModelTests.swift
  • CryptomatorTests/VaultListViewModelTests.swift
  • CryptomatorTests/XPCCacheControllerTests.swift

Comment thread Cryptomator/Settings/ClearCacheAppIntent.swift Outdated
Comment thread CryptomatorFileProviderTests/FileImportingServiceSourceTests.swift
Comment thread CryptomatorFileProviderTests/PermissionProviderImplTests.swift
Comment thread CryptomatorFileProviderTests/ServiceSource/CacheManagingServiceSourceTests.swift Outdated
@tobihagemann tobihagemann linked an issue Apr 17, 2026 that may be closed by this pull request
2 tasks
@tobihagemann
Copy link
Copy Markdown
Member

@phil1995 two things I want your take on before I touch anything:

1. withDependencies placement convention. The migration landed two different shapes across test files:

  • Wrap only construction: viewModel = withDependencies { ... } operation: { ViewModel() }, then call methods outside the block. Works because @Dependency captures initialValues at init and those survive the scope.
  • Wrap the whole call: withDependencies { ... } operation: { viewModel.method(...) }.

Both are valid per the library's lifetime docs, but I'd like to pick one for new tests so we stop having this discussion per file. Do you have a preference?

2. ItemEnumerationTaskTests dep scope. Looking at testFolderEnumeration / testFolderEnumerationSameFolderTwice, the .then closures run after the operation: block returns, so any FileProviderItem constructed inside the async chain captures the default permissionProvider instead of the mock. Looks like you worked around it by comparing .filename / .parentItemIdentifier instead of full-item equality.

A few ways to put the full equality back:

  • Wait for the promise synchronously (e.g. wait(for: promise)) inside the block, then assert outside, so items carry the mocked dependency.
  • Materialize expected items inside withDependencies and compare against the actuals (actuals still built without the mock, so capabilities still wouldn't match — probably same problem).
  • Push withDependencies into ItemEnumerationTaskExecutor.execute(...) itself so items built async still see the mock.

What's your call? Happy to go with whichever.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
Cryptomator/Settings/XPCCacheController.swift (1)

46-54: Minor: consider a computed testValue for consistency with liveValue.

liveValue is a computed property returning a fresh XPCCacheController(), but testValue is a stored var sharing a single UnimplementedCacheController instance across tests. Since UnimplementedCacheController is stateless here, this is harmless in practice, but a computed property keeps the pattern uniform and avoids accidental state leakage if the stub ever gains properties.

Suggested tweak
-	`#if` DEBUG
-	static var testValue: any CacheControlling = UnimplementedCacheController()
-	`#endif`
+	`#if` DEBUG
+	static var testValue: any CacheControlling {
+		UnimplementedCacheController()
+	}
+	`#endif`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Cryptomator/Settings/XPCCacheController.swift` around lines 46 - 54, The
testValue in CacheControllerKey is a stored var returning a single
UnimplementedCacheController instance which differs from the computed liveValue
pattern; change testValue to be a computed property (like liveValue) so it
returns a new UnimplementedCacheController() each access to maintain consistency
and avoid accidental shared state—update CacheControllerKey.testValue to be a
computed var returning UnimplementedCacheController() (while keeping the `#if`
DEBUG guard and the DependencyKey conformance).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Cryptomator/Settings/XPCCacheController.swift`:
- Around line 46-54: The testValue in CacheControllerKey is a stored var
returning a single UnimplementedCacheController instance which differs from the
computed liveValue pattern; change testValue to be a computed property (like
liveValue) so it returns a new UnimplementedCacheController() each access to
maintain consistency and avoid accidental shared state—update
CacheControllerKey.testValue to be a computed var returning
UnimplementedCacheController() (while keeping the `#if` DEBUG guard and the
DependencyKey conformance).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 933c45b5-8ca8-43ee-b8a9-bb573d5b4d7c

📥 Commits

Reviewing files that changed from the base of the PR and between 9d36942 and b3e2e23.

📒 Files selected for processing (26)
  • Cryptomator/Settings/ClearCacheAppIntent.swift
  • Cryptomator/Settings/XPCCacheController.swift
  • CryptomatorCommon/Tests/CryptomatorCommonCoreTests/FullVersionCheckerTests.swift
  • CryptomatorCommon/Tests/CryptomatorCommonCoreTests/Hub/HubAuthenticationViewModelTests.swift
  • CryptomatorCommon/Tests/CryptomatorCommonCoreTests/Manager/CloudProviderAccountManagerTests.swift
  • CryptomatorFileProviderTests/FileImportingServiceSourceTests.swift
  • CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterEnumerateItemTests.swift
  • CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterImportDocumentTests.swift
  • CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterRecoverUploadsTests.swift
  • CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterTestCase.swift
  • CryptomatorFileProviderTests/FileProviderEnumeratorTests.swift
  • CryptomatorFileProviderTests/FileProviderItemTests.swift
  • CryptomatorFileProviderTests/FileProviderNotificatorTests.swift
  • CryptomatorFileProviderTests/Middleware/TaskExecutor/ItemEnumerationTaskTests.swift
  • CryptomatorFileProviderTests/PermissionProviderImplTests.swift
  • CryptomatorFileProviderTests/ServiceSource/CacheManagingServiceSourceTests.swift
  • CryptomatorFileProviderTests/WorkingSetObserverTests.swift
  • CryptomatorTests/ChangePasswordViewModelTests.swift
  • CryptomatorTests/DatabaseManagerTests.swift
  • CryptomatorTests/MoveVaultViewModelTests.swift
  • CryptomatorTests/RenameVaultViewModelTests.swift
  • CryptomatorTests/SettingsViewModelTests.swift
  • CryptomatorTests/VaultKeepUnlockedViewModelTests.swift
  • CryptomatorTests/VaultListViewModelTests.swift
  • CryptomatorTests/XPCCacheControllerTests.swift
  • SharedResources/en.lproj/Localizable.strings
✅ Files skipped from review due to trivial changes (5)
  • CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterRecoverUploadsTests.swift
  • CryptomatorFileProviderTests/PermissionProviderImplTests.swift
  • SharedResources/en.lproj/Localizable.strings
  • CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterTestCase.swift
  • CryptomatorFileProviderTests/Middleware/TaskExecutor/ItemEnumerationTaskTests.swift
🚧 Files skipped from review as they are similar to previous changes (15)
  • CryptomatorFileProviderTests/FileProviderEnumeratorTests.swift
  • CryptomatorCommon/Tests/CryptomatorCommonCoreTests/Hub/HubAuthenticationViewModelTests.swift
  • CryptomatorFileProviderTests/FileProviderItemTests.swift
  • CryptomatorTests/RenameVaultViewModelTests.swift
  • CryptomatorFileProviderTests/FileImportingServiceSourceTests.swift
  • CryptomatorTests/VaultKeepUnlockedViewModelTests.swift
  • CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterEnumerateItemTests.swift
  • CryptomatorFileProviderTests/WorkingSetObserverTests.swift
  • CryptomatorTests/VaultListViewModelTests.swift
  • CryptomatorCommon/Tests/CryptomatorCommonCoreTests/FullVersionCheckerTests.swift
  • CryptomatorFileProviderTests/FileProviderNotificatorTests.swift
  • CryptomatorTests/XPCCacheControllerTests.swift
  • CryptomatorTests/DatabaseManagerTests.swift
  • CryptomatorTests/ChangePasswordViewModelTests.swift
  • Cryptomator/Settings/ClearCacheAppIntent.swift

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

iOS shortcut to clear cache

2 participants