Skip to content
Open
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
20 changes: 20 additions & 0 deletions Cryptomator.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,9 @@
B34C532A2D142BA700F30FE9 /* SharePointAuthenticating.swift in Sources */ = {isa = PBXBuildFile; fileRef = B34C53292D142B9200F30FE9 /* SharePointAuthenticating.swift */; };
B379DBBF2D27F595003B5849 /* SharePointDriveListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B379DBBE2D27F58C003B5849 /* SharePointDriveListViewController.swift */; };
B379DBC12D27F5B5003B5849 /* SharePointDriveListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B379DBC02D27F5A4003B5849 /* SharePointDriveListViewModel.swift */; };
B3C397FE2EB10FC0001280AC /* ShareVaultViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3C397FC2EB10FC0001280AC /* ShareVaultViewController.swift */; };
B3C398002EB110F9001280AC /* ShareVaultViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3C397FF2EB110F9001280AC /* ShareVaultViewModel.swift */; };
988E23D6223540118B002237 /* ShareVaultCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 988E23D6223540118B002238 /* ShareVaultCoordinator.swift */; };
B3D19A442CB937C700CD18A5 /* FileProviderCoordinatorError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3D19A432CB937BF00CD18A5 /* FileProviderCoordinatorError.swift */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -1064,6 +1067,9 @@
B34C53292D142B9200F30FE9 /* SharePointAuthenticating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharePointAuthenticating.swift; sourceTree = "<group>"; };
B379DBBE2D27F58C003B5849 /* SharePointDriveListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharePointDriveListViewController.swift; sourceTree = "<group>"; };
B379DBC02D27F5A4003B5849 /* SharePointDriveListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharePointDriveListViewModel.swift; sourceTree = "<group>"; };
B3C397FC2EB10FC0001280AC /* ShareVaultViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareVaultViewController.swift; sourceTree = "<group>"; };
B3C397FF2EB110F9001280AC /* ShareVaultViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareVaultViewModel.swift; sourceTree = "<group>"; };
988E23D6223540118B002238 /* ShareVaultCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareVaultCoordinator.swift; sourceTree = "<group>"; };
B3D19A432CB937BF00CD18A5 /* FileProviderCoordinatorError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderCoordinatorError.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -1282,6 +1288,7 @@
4A4B7E3E26B2ABC0009BFDB1 /* VaultDetailViewController.swift */,
4A4B7E4126B2AD6F009BFDB1 /* VaultDetailViewModel.swift */,
4AB8539B26BA8A8200555F00 /* VaultPasswordVerifying.swift */,
B3C397FD2EB10FC0001280AC /* ShareVault */,
4AF45357271F2A8B00CF1919 /* RenameVault */,
4A0337C82726FBEC001753B7 /* MoveVault */,
4A91D8CF272ADC7E003F8BD8 /* ChangePassword */,
Expand Down Expand Up @@ -2114,6 +2121,16 @@
path = Purchase;
sourceTree = "<group>";
};
B3C397FD2EB10FC0001280AC /* ShareVault */ = {
isa = PBXGroup;
children = (
988E23D6223540118B002238 /* ShareVaultCoordinator.swift */,
B3C397FF2EB110F9001280AC /* ShareVaultViewModel.swift */,
B3C397FC2EB10FC0001280AC /* ShareVaultViewController.swift */,
);
path = ShareVault;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -2783,6 +2800,8 @@
4A644B57267C958F008CBB9A /* ChildCoordinator.swift in Sources */,
4A53CC15267CC33100853BB3 /* CreateNewVaultPasswordViewModel.swift in Sources */,
4AA22C1E261CA94700A17486 /* UsernameFieldCell.swift in Sources */,
B3C397FE2EB10FC0001280AC /* ShareVaultViewController.swift in Sources */,
988E23D6223540118B002237 /* ShareVaultCoordinator.swift in Sources */,
4A136132276770BB0077EB7F /* SnapshotVaultListViewModel.swift in Sources */,
4AED9A79286B4DF500352951 /* S3Authenticating.swift in Sources */,
747C35172762A3F500E4CA28 /* AttributedTextHeaderFooterViewModel.swift in Sources */,
Expand Down Expand Up @@ -2810,6 +2829,7 @@
4A644B55267C926A008CBB9A /* FolderCreating.swift in Sources */,
74A295EF2D80902800C54136 /* SharePointAuthenticator.swift in Sources */,
4A3D658626847B11000DA764 /* CreateNewLocalVaultViewModel.swift in Sources */,
B3C398002EB110F9001280AC /* ShareVaultViewModel.swift in Sources */,
4AE97DAB24572E4900452814 /* AppDelegate.swift in Sources */,
4AA22C16261CA8D800A17486 /* URLFieldCell.swift in Sources */,
4A2FD07925B5D98B008565C8 /* CloudCell.swift in Sources */,
Expand Down
46 changes: 46 additions & 0 deletions Cryptomator/VaultDetail/ShareVault/ShareVaultCoordinator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// ShareVaultCoordinator.swift
// Cryptomator
//
// Created by Majid Achhoud on 30.10.25.
// Copyright © 2025 Skymatic GmbH. All rights reserved.
//

import CryptomatorCloudAccessCore
import CryptomatorCommonCore
import UIKit

class ShareVaultCoordinator: Coordinator {
weak var parentCoordinator: Coordinator?
lazy var childCoordinators = [Coordinator]()
var navigationController: UINavigationController
private let vaultInfo: VaultInfo

init(vaultInfo: VaultInfo, navigationController: UINavigationController) {
self.vaultInfo = vaultInfo
self.navigationController = navigationController
}

func start() {
let viewModel: ShareVaultViewModel
if vaultInfo.vaultConfigType == .hub, let hubURL = extractHubVaultURL() {
viewModel = ShareVaultViewModel(type: .hub(hubURL))
} else {
viewModel = ShareVaultViewModel(type: .normal)
}
let shareVaultViewController = ShareVaultViewController(viewModel: viewModel)
shareVaultViewController.coordinator = self
navigationController.pushViewController(shareVaultViewController, animated: true)
}
Comment on lines +24 to +34
Copy link

@coderabbitai coderabbitai bot Nov 6, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Consider handling Hub URL extraction failures more explicitly.

When vaultInfo.vaultConfigType == .hub but extractHubVaultURL() returns nil, the code silently falls back to .normal type. This could confuse users who expect Hub-specific features. Consider:

  • Logging the failure for debugging purposes
  • Showing an error message to the user if Hub configuration is expected but unavailable
  • Distinguishing between "normal vault" and "Hub vault with extraction error" states
🤖 Prompt for AI Agents
In Cryptomator/VaultDetail/ShareVault/ShareVaultCoordinator.swift around lines
24 to 34, when vaultInfo.vaultConfigType == .hub and extractHubVaultURL()
returns nil the code currently falls back silently to .normal; change this to
explicitly handle the failure by (1) logging the extraction failure with context
(vault id/name) via the app logger, (2) presenting a user-facing error
(UIAlertController) on the main thread that explains the Hub configuration could
not be loaded and offers to retry or cancel, and (3) updating the created
ViewModel/state so the UI can reflect the distinct case (either add a new
ShareVaultViewModel case like .hubExtractionError or pass a flag into .normal to
show an error state) — implement logging before constructing the view
controller, present the alert if extraction fails and only push the view
controller after user confirmation or push a view controller initialized with
the error state so the UI clearly distinguishes "normal vault" from "Hub vault
with extraction error".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@tobihagemann I see three ways to fix this:

  • Option 1: Show an error alert and don't navigate at all if URL extraction fails
  • Option 2: Add a .hubError state that shows "Failed to load Hub information"
  • Option 3: Hide the Share button upstream if we detect URL extraction would fail I'd lean towards

Option 1 - it's the clearest for users. Alternatively, is this just an edge case that's unlikely to happen in practice? In the worst case, users would currently just see our Hub marketing page instead of their actual Hub instance, which would be confusing but not breaking anything. What do you think?

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, let's go with option 1. It's totally an edge case and shouldn't happen, but you never know what we're changing in the future. 😄


private func extractHubVaultURL() -> URL? {
guard let cachedVault = try? VaultDBCache().getCachedVault(withVaultUID: vaultInfo.vaultUID),
let vaultConfigToken = cachedVault.vaultConfigToken,
let vaultConfig = try? UnverifiedVaultConfig(token: vaultConfigToken),
let hubConfig = vaultConfig.allegedHubConfig else {
return nil
}

return hubConfig.getWebAppURL()
}
}
Loading
Loading