Skip to content

Commit e6c630c

Browse files
committed
fix: address CodeRabbit review feedback for password sync
1 parent 04e496a commit e6c630c

3 files changed

Lines changed: 32 additions & 15 deletions

File tree

TablePro/AppDelegate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
5252

5353
func applicationDidFinishLaunching(_ notification: Notification) {
5454
NSWindow.allowsAutomaticWindowTabbing = true
55-
KeychainHelper.shared.migrateFromLegacyKeychainIfNeeded()
5655
let syncSettings = AppSettingsStorage.shared.loadSync()
5756
let passwordSyncExpected = syncSettings.enabled && syncSettings.syncConnections && syncSettings.syncPasswords
5857
let previousSyncState = UserDefaults.standard.bool(forKey: KeychainHelper.passwordSyncEnabledKey)
5958
UserDefaults.standard.set(passwordSyncExpected, forKey: KeychainHelper.passwordSyncEnabledKey)
59+
KeychainHelper.shared.migrateFromLegacyKeychainIfNeeded()
6060
if passwordSyncExpected != previousSyncState {
6161
Task.detached(priority: .background) {
6262
KeychainHelper.shared.migratePasswordSyncState(synchronizable: passwordSyncExpected)

TablePro/Core/Storage/KeychainHelper.swift

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -263,18 +263,23 @@ final class KeychainHelper {
263263
continue
264264
}
265265

266-
let deleteQuery: [String: Any] = [
267-
kSecClass as String: kSecClassGenericPassword,
268-
kSecAttrService as String: service,
269-
kSecAttrAccount as String: account,
270-
kSecUseDataProtectionKeychain as String: true,
271-
kSecAttrSynchronizable as String: !synchronizable
272-
]
273-
let deleteStatus = SecItemDelete(deleteQuery as CFDictionary)
274-
if deleteStatus != errSecSuccess, deleteStatus != errSecItemNotFound {
275-
Self.logger.warning(
276-
"Migrated item '\(account, privacy: .public)' but failed to delete old entry: \(deleteStatus)"
277-
)
266+
// When opting IN (synchronizable=true), delete the old local-only item safely.
267+
// When opting OUT (synchronizable=false), keep the synchronizable item — deleting it
268+
// would propagate via iCloud Keychain and remove it from other Macs still opted in.
269+
if synchronizable {
270+
let deleteQuery: [String: Any] = [
271+
kSecClass as String: kSecClassGenericPassword,
272+
kSecAttrService as String: service,
273+
kSecAttrAccount as String: account,
274+
kSecUseDataProtectionKeychain as String: true,
275+
kSecAttrSynchronizable as String: false
276+
]
277+
let deleteStatus = SecItemDelete(deleteQuery as CFDictionary)
278+
if deleteStatus != errSecSuccess, deleteStatus != errSecItemNotFound {
279+
Self.logger.warning(
280+
"Migrated item '\(account, privacy: .public)' but failed to delete old entry: \(deleteStatus)"
281+
)
282+
}
278283
}
279284

280285
migratedCount += 1

TablePro/Views/Settings/SyncSettingsView.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct SyncSettingsView: View {
1919
Toggle("iCloud Sync:", isOn: $syncSettings.enabled)
2020
.onChange(of: syncSettings.enabled) { _, newValue in
2121
persistSettings()
22+
updatePasswordSyncFlag()
2223
if newValue {
2324
syncCoordinator.enableSync()
2425
} else {
@@ -178,9 +179,20 @@ struct SyncSettingsView: View {
178179
}
179180

180181
private func onPasswordSyncChanged(_ enabled: Bool) {
182+
let effective = syncSettings.enabled && syncSettings.syncConnections && enabled
181183
Task.detached {
182-
KeychainHelper.shared.migratePasswordSyncState(synchronizable: enabled)
183-
UserDefaults.standard.set(enabled, forKey: KeychainHelper.passwordSyncEnabledKey)
184+
KeychainHelper.shared.migratePasswordSyncState(synchronizable: effective)
185+
UserDefaults.standard.set(effective, forKey: KeychainHelper.passwordSyncEnabledKey)
186+
}
187+
}
188+
189+
private func updatePasswordSyncFlag() {
190+
let effective = syncSettings.enabled && syncSettings.syncConnections && syncSettings.syncPasswords
191+
let current = UserDefaults.standard.bool(forKey: KeychainHelper.passwordSyncEnabledKey)
192+
guard effective != current else { return }
193+
Task.detached {
194+
KeychainHelper.shared.migratePasswordSyncState(synchronizable: effective)
195+
UserDefaults.standard.set(effective, forKey: KeychainHelper.passwordSyncEnabledKey)
184196
}
185197
}
186198

0 commit comments

Comments
 (0)