bug: ユーザ辞書編集後に設定画面の「N件のアイテム」表示が更新されない#328
Open
itouuuuuuuuu wants to merge 1 commit into
Open
Conversation
設定画面に表示される "N件のアイテム" のカウントが、ユーザ辞書編集ウィンドウで 項目を追加・削除しても古いまま残るバグを修正する。 原因: @ConfigState は内部で @State (underlyingState) を保持していたが、ConfigWindow と UserDictionaryEditorWindow はそれぞれ独立した @ConfigState インスタンスを持つ。 編集ウィンドウは self.userDictionary.value.items.append(...) のように Binding を 経由しない直接 mutation で UserDefaults だけを更新しており、別ウィンドウである ConfigWindow の @State は同期されない。ConfigWindow には body 再評価のトリガーが ないため、件数表示が更新されなかった。 修正: 1. ConfigState を @StateObject + ConfigStateStore (ObservableObject) に置き換え、 UserDefaults.didChangeNotification を購読してプロセス内のあらゆる UserDefaults 更新で store.value を reload する。これにより別ウィンドウからの書き込みでも View の再描画がトリガーされる。 2. ConfigStateStore は SwiftUI-facing な UI state を持つため @mainactor で隔離。 通知 callback は queue: .main で main thread に乗るが、型レベル isolation の ため Task { @mainactor in ... } で hop する (MainActor.assumeIsolated は macOS 14+ のため未採用、deployment target は macOS 12/13)。 3. UserDictionaryEditorWindow の追加・削除・編集処理を updateUserDictionary ヘルパに集約し、必ず $userDictionary (Binding) 経由で書き込むようにした。 store と UserDefaults が同期更新され、同一 view 内の即時反映も担保される。 4. 同 view の read 経路も userDictionaryValue computed property (= $userDictionary.wrappedValue) に統一し、表示は store / 保存は Binding setter という構図を明確化。Item.value (毎回 UserDefaults を JSON decode する computed property) 経由の参照を排除した。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Author
|
すみません、こちらまだベータ版の機能だったんですね。 |
Member
|
ありがとうございます、簡単に治るならmergeしてたんですが、解決される課題に対して変更が結構デカいのでちょっと悩み中です |
Contributor
Author
|
ご確認ありがとうございます…!
|
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 #327
azooKey 設定画面の「azooKeyユーザ辞書」セクションに表示される 「N件のアイテム」 のカウントが、ユーザ辞書編集ウィンドウで項目を追加・削除しても更新されない不具合の修正です。
設定画面で別の操作をして view が再描画されると最新の件数になるため、データの保存自体は行われていることが確認できます。
原因
@ConfigStateプロパティラッパーの内部実装と、ユーザ辞書編集ウィンドウ側の書き込みパターンの組み合わせが原因でした。修正前の
@ConfigStateは、内部に@State private var underlyingState: Item.Valueを保持し、projectedValue(Binding)経由の書き込みでのみunderlyingStateを更新する設計になっていました。設定画面 (
ConfigWindow) と編集ウィンドウ (UserDictionaryEditorWindow) はAppDelegateでそれぞれ独立したNSHostingControllerとして生成されるため、同じConfig.UserDictionaryを参照していてもそれぞれ別々の@ConfigStateインスタンス(= 別々の@State)を持ちます。一方、編集ウィンドウ側は Binding を経由せず 直接 mutation していました:
これは
Item.valueの computed property setter 経由で UserDefaults だけを更新する経路です。設定画面側の@Stateは更新されないうえ、設定画面には body 再評価のトリガーが何も発生しないため、Text("\(self.userDictionary.value.items.count)件のアイテム")は古い@Stateの世代のまま再描画されず、件数表示が更新されない症状が発生していました。なお、編集ウィンドウ自身は同じハンドラ内で
editTargetID/undoItem(@State) を更新していたため、たまたま body が再評価されて表示が更新される副次的挙動になっており、「自分の画面では更新されるのに、設定画面では更新されない」という挙動になっていました。修正内容
1.
@ConfigStateを@StateObject+ConfigStateStoreベースに変更azooKeyMac/Windows/ConfigState.swiftの内部実装を、@Stateベースから@StateObjectで所有するObservableObjectストアに置き換えました。UserDefaults.didChangeNotificationを購読することで、別ウィンドウからの書き込み・直valuemutation を含むプロセス内のあらゆる UserDefaults 更新でstore.valueを reload します。@Published経由でobjectWillChangeが発火し、subscribe 側の view が再評価されるため、別ウィンドウで行われた変更も即時反映されます。ConfigStateStoreは SwiftUI-facing な UI state を持つため@MainActorで隔離。queue: .mainで main thread に乗りますが、型レベルの isolation のためTask { @MainActor in }で hop しています。MainActor.assumeIsolatedを使えば 1 hop 減らせますが macOS 14+ なので採用していません。[weak self]で循環参照を回避し、@StateObjectの lifetime に追従してdeinitで removeObserver します。2. ユーザ辞書編集ウィンドウの mutation を
updateUserDictionaryヘルパに集約azooKeyMac/Windows/UserDictionaryEditorWindow.swiftの追加・削除・編集・undo の各処理を、$userDictionary(Binding) 経由の read-modify-write に統一しました。Binding setter 経由で書き込むことで
store.set→@Published value更新 → UserDefaults 書き込み が同期的に行われ、通知の reload を待たずに自 view の即時反映も担保されます。3. read 経路を
userDictionaryValue(=$userDictionary.wrappedValue) に統一editor 内の
isAdditionDisabled/itemBinding.get/Table(...)の参照も in-memory な store 経由に揃えました。これまで
self.userDictionary.value.items.countのようにItem.valueの computed property(毎回 UserDefaults を JSON decode)経由で読んでいた箇所を、store.valueを直接読む形に変更。「表示は store、保存は Binding setter」という構図が明確になり、body 評価ごとのデコードコストも軽減されます。動作確認
xcodebuild -scheme azooKeyMac -configuration Debug build CODE_SIGNING_ALLOWED=NO→ BUILD SUCCEEDED