pointfreeco / swift-composable-architecture

A library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind.
https://www.pointfree.co/collections/composable-architecture
MIT License
11.91k stars 1.37k forks source link

@Shared(.appStorage) updates are triggered whenever any UserDefaults key changes, not just a specific key #3179

Closed larryonoff closed 3 weeks ago

larryonoff commented 3 weeks ago

Description

It looks that AppStorage should migrate from NSUserDefaultsDidChangeNotification to key-value observing for specific keys. According to Apple's documentation.

You can use key-value observing to register observers for specific keys of interest in order to be notified of all updates, regardless of whether changes are made within or outside the current process.

Checklist

Expected behavior

AppStorage triggers updates only when specific key changes.

Actual behavior

AppStorage triggers updates whenever any change occurs in NSUserDefaults.

Steps to reproduce

Check the sample project

The Composable Architecture version information

1.11.1

Destination operating system

iOS 17.5.1

Xcode version information

Version 15.4 (15F31d)

Swift Compiler version information

swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: arm64-apple-macosx14.0
mbrandonw commented 3 weeks ago

Hi @larryonoff, this will be fixed in #3186.

We can't subscribe to the changes for just a single key in UserDefaults because KVO treats "compound" keys (i.e. "foo.bar") in a special way, and it that breaks observation if you were ever to do .appStorage("feature.count"). We have no choice but to subscribe to the firehose of all user default changes.

However, because user defaults can only store very basic data, we can implement some simple remove duplicates logic.

larryonoff commented 3 weeks ago

Hi @larryonoff, this will be fixed in #3186.

We can't subscribe to the changes for just a single key in UserDefaults because KVO treats "compound" keys (i.e. "foo.bar") in a special way, and it that breaks observation if you were ever to do .appStorage("feature.count"). We have no choice but to subscribe to the firehose of all user default changes.

However, because user defaults can only store very basic data, we can implement some simple remove duplicates logic.

Thanks for the fix. Looking forward to the new release with the latest fixes!

PS. Sad that it's not possible to fix the issue in an optimal way.