Repository navigation
Conversation
Settings gains a Devices section between Backup and Data, shown only when the device coordinator is configured (a build with CloudKit). Pair a Device… opens a sheet that shows the QR code drawn with Core Image and a countdown, then the device that scanned it (its platform marked as not verified) while the Mac waits for the iPhone's reveal, then a six-digit field for the code the iPhone shows. Confirm checks the entry; a wrong or malformed entry asks for nothing, and an accepted code asks for system authentication at once. A cancelled or failed prompt keeps the accepted entry, and Confirm asks again without a new entry. The field is cleared once the code is accepted, is not a one-time-code field and does not cut an entry short. The sheet never shows the Mac's code: no state it reads carries one. Expiry, three wrong codes, a second responder, cancellation and start failures each have their own message, with Pair Again where it helps. The list shows each device's name, platform, paired date, last reached and delivery check state, with Rename, Test Delivery and Remove; Remove and Reset Device Identity… confirm inline first. The section explains each unavailable state: no Secure Enclave, iCloud signed out, restricted or unavailable, an account change, an unusable identity or refused list (with the reset), and the limit of 8. Pairing confirmation now asks for system authentication with its own reason, "Pair a device with this Mac", which the authentication subprocess accepts. Every new string is in the catalog in English and Simplified Chinese, with the four device keychain conflict messages and neutral text for push.message.title and push.message.body. The glossary adds Device, Pair, Pairing code and Delivery check. The E2E App gets fake device services and a simulated iPhone only in the screenshots-devices scenario, and the screenshot suite captures the list, the QR code, waiting, code entry and iCloud signed out in English (30-34) and Simplified Chinese (35-39). Closes #288
- The accepted-code step has its own title and message, Confirm Pairing, instead of repeating the entry instruction without a field. - Unavailable Devices actions are dimmed as well as disabled, because the shared secondary button styles draw the same when disabled. - A Mac without a Secure Enclave says so before the first attempt. - Removing the last device after an account change checks the account again, so pairing in the new account can start. - The device row shows the delivery check on its own line, and none while the device can't be reached. - Each Devices subview subscribes to the language itself, so switching language updates the section and the sheet. - Copy: the signed-out line no longer says pairing can't start while Pair a Device stays available; the removal confirmation states the local guarantee and asks to remove this Mac on the device if it still shows it; Chinese wording fixes for the code warning, the restricted and undone messages and the removed-device issue. - The test-only shownText helper moves to the test target.
The first CI screenshot run clicked Pair a Device… and no sheet appeared; afterwards the button was enabled again, so the session had been cancelled. The only path that does that without a click is the sheet binding's setter. It no longer cancels: every way out of the sheet goes through its buttons (Esc is Cancel) or the end of the management session. The QR code is drawn with Core Image's software renderer, which needs no GPU. The E2E device fixture records each pairing state the sheet reads, by case name only, in device-transitions.json, so a failed capture shows where the session went.
- The sheet binding clears a session that has finished when SwiftUI reports the sheet dismissed; a live session still ends only through the sheet's buttons or the end of the management session. - On the QR step, Cancel and Pair Again share one row, so Cancel lines up with the other steps' buttons. - The removal confirmation says Ask Key tries to tell the device, which then removes this Mac when it next connects. - E2E: the second CI run again opened no sheet after the click, and no pairing state changed. The fixture now records the coordinator's state at launch and after every change the section reads; the test checks for the sheet first, clicks again or starts pairing through a fixture command when the click opened none, takes the captures, and fails at the end if the click was not enough.
The third CI run settled where the captures fail: two synthesized clicks on Pair a Device… started no pairing, while the fixture calling the coordinator's startPairing() opened the sheet at once, so presentation works and the button's action does not run under the CI click. The section now follows the Backup section's layout, whose buttons the same run clicks successfully: the sheet hangs off the pairing row instead of the whole group, the button calls a method, and the section refreshes on appear instead of through a task. After the pairing, the runner's accessibility queries timed out after four 600-point scrolls. The test now scrolls only until the device row is hittable, and attaches the fixture's state record as soon as the sheet shows and after the delivery check, so it survives a later timeout.
Diagnostics only, in Tests: before each click the fixture records what Pair a Device… checks (Secure Enclave, pairing, availability, account, identity, devices) and the runner records the button's enabled, hittable and frame values. A click that opens no sheet is followed by a click with the button scrolled higher, then the fixture start; the test fails at the end unless the first click opened the sheet. After the pairing the runner waits eight seconds so the state record shows what changes once the sheet has closed, and a teardown block terminates the App so a hung accessibility query cannot also hang the teardown.
…ries in the Devices E2E
…ibility in the E2E
…ction again after pairing
…sect the Devices row Identify the Devices status lines and the Settings page for the E2E, which now finds them by identifier.
…, and Erase as a lower control
The Devices section kept an optional observable model in @State, created in its initializer. On macOS 26, Pair a Device... then ignored clicks until the section redrew (#298). The model now lives as long as the App's device coordinator and is passed in, as the Backup section receives the vault; a section that appears again closes a rename or confirmation left open. The Devices E2E clicks Pair a Device... itself and has no fixture path that starts a session, so the macOS 26 runner catches the regression. It finds the Devices texts by identifier: after the code field had focus, whole-window snapshots hung on the text cursor's remote element on the runner. This removes the temporary #298 diagnostics from 0f547e7, f9fee03 and b81ac28 and the diagnostic fixture commands.
ADR 0013 lets the unpair notice be lost or expire, so the removal confirmation now says only that Ask Key tries to tell the device.
On macOS 26, Pair a Device... ignored clicks until the Devices section redrew; passing the model in from a stable holder did not change that. The cause is unknown (#298). Clicks worked in every run after a redraw, so the section rebuilds its controls once, after it has loaded. Nothing redraws repeatedly, and the controls look the same before and after.
This reverts commit 7562301.
….. does on a click The button action, the model's canPair and its inputs, and each return of the coordinator's startPairing are recorded in the E2E App; the screenshot test prints the trace after its click.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #288
Summary
screenshots-devicesshow nothing.Pair a device with this Mac/ 确认将设备与这台 Mac 配对, accepted by the authentication subprocess; the four device keychain conflict messages are translated. Step 8 terms are indocs/glossary.mdand checked by a test.Diff stat
git diff --stat origin/main: 21 files changed, 2376 insertions(+), 6 deletions(-). New:Views/Settings/DevicesSettingsSection.swift,DevicePairingSheet.swift,DevicesSettingsModel.swift,DevicesPresentation.swift,PairingSheetPresentation.swift,VaultViewModel+Devices.swift,Tests/AskKeyTestSupport/E2EDeviceScenario.swift, two App test files.Localizable.xcstrings+672 lines (84 new entries with en, zh-Hans and qps-ploc).Tests
Local, head 22120f6:
swift build: complete.swift test --filter "DevicesSettingsModelTests|DevicesSettingsRenderTests|DeviceCoordinatorPairingTests|DeviceCoordinatorDevicesTests|CloudKitGateTests|AppLanguageCatalogTests|ManagementAuthenticationProcessTests|LocalizationUnificationTests|LocalizationRemediationTests|ReviewLocalizationTests|AgentAccessPolicyMessageTests|LanguageSwitchPickerTitleTests|OnboardingFlowTests|BackupSheetRenderTests|Batch4SettingsLanguageTests|AppLanguageExperienceTests|WorkspaceVisualContractTests": 115 tests, 0 failed, 0 skipped.contentTypeis nilThe Acceptance view-model checks are covered in
DevicesSettingsModelTests: section absent when not configured; pairing needs the management session; a wrong code asks for no authentication and counts one attempt; a matching code needs authentication with the pairing reason, a cancel stores nothing, a retry needs no new entry; three wrong codes, expiry and a second responder store nothing; the field appears only after the reveal; no sheet text in any state contains the code; Remove and Reset confirm first; each unavailable state shows its message.Full
swift test,basic-ui-flowsand the screenshot suite run in CI (not run locally, per AGENTS.md).Checks
python3 scripts/check_hygiene.py: passed (size=0, test-support=0, debug=0, local-path=0, non-ascii-name=0, multica=0, cjk=0).python3 scripts/check_module_deps.py: passed.Deviations and questions
Vault.devicePairingReasoninSources/AskKeyVault/Vault+Devices.swiftstill equals the management reason and its comment still says Add the Devices section to Mac Settings #288 will add a dedicated one. The App now passesManagementAuthenticationAction.pairDevice.reasonKeyto the approval signature, so the prompt shows the pairing reason; the Vault only checks the authenticator against its constant, and the App's authenticator accepts any reason. Updating the constant and its comment is outside this issue's Scope (Sources/AskKeyVault/), left for a follow-up.WorkspaceVisualContract.swifthas no Settings section and was not changed; the Devices views reuseTheme,GroupedList, the Settings row layout and the backup sheet frame.