From eef760fa81d138addc35b38208b09da927f768b4 Mon Sep 17 00:00:00 2001 From: Fernando Trigoso Date: Wed, 7 Aug 2024 13:34:51 -0400 Subject: [PATCH 1/6] [shared_prefs] Fixes get-all when suite name is used In shared_preferences_foundation, fixes getting all preferences when suite name is used. Bug was reading only the standard user defaults. The fix uses suite name when available. --- .../shared_preferences_foundation/CHANGELOG.md | 2 +- .../darwin/Tests/RunnerTests.swift | 14 ++++++++++++++ .../SharedPreferencesPlugin.swift | 2 +- .../shared_preferences_foundation/pubspec.yaml | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md b/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md index 1eec9791673..67bc77eea68 100644 --- a/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md @@ -1,5 +1,5 @@ ## NEXT - +* Fixes getting all preferences when suite name is used. * Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. ## 2.5.0 diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift index 88d38022d61..a611563c33d 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift @@ -115,6 +115,7 @@ class RunnerTests: XCTestCase { // Async system tests. let emptyOptions = SharedPreferencesPigeonOptions() + let optionsWithSuiteName = SharedPreferencesPigeonOptions(suiteName: "group.example.sharedPreferencesFoundationExample") func testAsyncSetAndGet() throws { let plugin = SharedPreferencesPlugin() @@ -153,6 +154,19 @@ class RunnerTests: XCTestCase { XCTAssertEqual(storedValues["aStringList"] as? [String], ["hello", "world"]) } + + func testAsyncGetAllWithAndWithoutSuiteName() throws { + let plugin = SharedPreferencesPlugin() + + try plugin.set(key: "aKey", value: "hello world", options: emptyOptions) + try plugin.set(key: "aKeySuite", value: "hello world with suite", options: optionsWithSuiteName) + + let storedValues = try plugin.getAll(allowList: nil, options: emptyOptions) + XCTAssertEqual(storedValues["aKey"] as? String, "hello world") + + let storedValuesWithGroup = try plugin.getAll(allowList: nil, options: optionsWithSuiteName) + XCTAssertEqual(storedValuesWithGroup["aKeySuite"] as? String, "hello world with suite") + } func testAsyncGetAllWithAllowList() throws { let plugin = SharedPreferencesPlugin() diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift index ae86be7bab2..64694b15be0 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift @@ -152,7 +152,7 @@ public class SharedPreferencesPlugin: NSObject, FlutterPlugin, UserDefaultsApi { var filteredPrefs: [String: Any] = [:] var compatiblePrefs: [String: Any] = [:] let allowSet = allowList.map { Set($0) } - if let appDomain = Bundle.main.bundleIdentifier, + if let appDomain = options.suiteName ?? Bundle.main.bundleIdentifier, let prefs = try getUserDefaults(options: options).persistentDomain(forName: appDomain) { if let allowSet = allowSet { diff --git a/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml b/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml index f465f9120d1..50b20675b80 100644 --- a/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_foundation description: iOS and macOS implementation of the shared_preferences plugin. repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_foundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.5.0 +version: 2.5.1 environment: sdk: ^3.3.0 From f0e9d5fee1f9de16d86aa6c09106e03c3d5d4a5e Mon Sep 17 00:00:00 2001 From: Fernando Trigoso Date: Wed, 7 Aug 2024 16:53:13 -0400 Subject: [PATCH 2/6] Comment to clarify fix --- .../SharedPreferencesPlugin.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift index 64694b15be0..8446e5c2a27 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift @@ -152,6 +152,12 @@ public class SharedPreferencesPlugin: NSObject, FlutterPlugin, UserDefaultsApi { var filteredPrefs: [String: Any] = [:] var compatiblePrefs: [String: Any] = [:] let allowSet = allowList.map { Set($0) } + + // Since `getUserDefaults` is initialized with the suite name, it seems redundant to call + // `persistentDomain` with the suite name again. However, it is necessary because + // `dictionaryRepresentation` returns keys from the global domain. + // Also, Apple's docs on `persistentDomain` are incorrect, + // see: https://github.com/feedback-assistant/reports/issues/165 if let appDomain = options.suiteName ?? Bundle.main.bundleIdentifier, let prefs = try getUserDefaults(options: options).persistentDomain(forName: appDomain) { From d2d0ecf4ad33253277581883eb3036f40f163a99 Mon Sep 17 00:00:00 2001 From: Fernando Trigoso Date: Thu, 8 Aug 2024 14:24:33 -0400 Subject: [PATCH 3/6] Bump version. Formatting. --- .../shared_preferences_foundation/CHANGELOG.md | 2 +- .../darwin/Tests/RunnerTests.swift | 9 +++++---- .../SharedPreferencesPlugin.swift | 10 +++++----- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md b/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md index 67bc77eea68..6bb4695c2d0 100644 --- a/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 2.5.1 * Fixes getting all preferences when suite name is used. * Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift index a611563c33d..62cfd169b4a 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift @@ -115,7 +115,8 @@ class RunnerTests: XCTestCase { // Async system tests. let emptyOptions = SharedPreferencesPigeonOptions() - let optionsWithSuiteName = SharedPreferencesPigeonOptions(suiteName: "group.example.sharedPreferencesFoundationExample") + let optionsWithSuiteName = SharedPreferencesPigeonOptions( + suiteName: "group.example.sharedPreferencesFoundationExample") func testAsyncSetAndGet() throws { let plugin = SharedPreferencesPlugin() @@ -154,16 +155,16 @@ class RunnerTests: XCTestCase { XCTAssertEqual(storedValues["aStringList"] as? [String], ["hello", "world"]) } - + func testAsyncGetAllWithAndWithoutSuiteName() throws { let plugin = SharedPreferencesPlugin() try plugin.set(key: "aKey", value: "hello world", options: emptyOptions) try plugin.set(key: "aKeySuite", value: "hello world with suite", options: optionsWithSuiteName) - + let storedValues = try plugin.getAll(allowList: nil, options: emptyOptions) XCTAssertEqual(storedValues["aKey"] as? String, "hello world") - + let storedValuesWithGroup = try plugin.getAll(allowList: nil, options: optionsWithSuiteName) XCTAssertEqual(storedValuesWithGroup["aKeySuite"] as? String, "hello world with suite") } diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift index 8446e5c2a27..71602ab21e9 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift @@ -152,11 +152,11 @@ public class SharedPreferencesPlugin: NSObject, FlutterPlugin, UserDefaultsApi { var filteredPrefs: [String: Any] = [:] var compatiblePrefs: [String: Any] = [:] let allowSet = allowList.map { Set($0) } - - // Since `getUserDefaults` is initialized with the suite name, it seems redundant to call - // `persistentDomain` with the suite name again. However, it is necessary because - // `dictionaryRepresentation` returns keys from the global domain. - // Also, Apple's docs on `persistentDomain` are incorrect, + + // Since `getUserDefaults` is initialized with the suite name, it seems redundant to call + // `persistentDomain` with the suite name again. However, it is necessary because + // `dictionaryRepresentation` returns keys from the global domain. + // Also, Apple's docs on `persistentDomain` are incorrect, // see: https://github.com/feedback-assistant/reports/issues/165 if let appDomain = options.suiteName ?? Bundle.main.bundleIdentifier, let prefs = try getUserDefaults(options: options).persistentDomain(forName: appDomain) From e7c8efd6341ba9a947de3e792c7deeb665449abc Mon Sep 17 00:00:00 2001 From: Fernando Trigoso Date: Thu, 8 Aug 2024 16:50:11 -0400 Subject: [PATCH 4/6] Add integration tests. --- .../shared_preferences_test.dart | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart index 67777288d98..dc4d07ac2b9 100644 --- a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart @@ -487,6 +487,9 @@ void main() { group('shared_preferences_async', () { final SharedPreferencesAsyncFoundationOptions emptyOptions = SharedPreferencesAsyncFoundationOptions(); + final SharedPreferencesAsyncFoundationOptions optionsWithSuiteName = + SharedPreferencesAsyncFoundationOptions( + suiteName: 'group.example.sharedPreferencesFoundationExample'); const String stringKey = 'testString'; const String boolKey = 'testBool'; @@ -567,6 +570,39 @@ void main() { expect(gotAll[listKey], testList); }); + testWidgets('getPreferences with options', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, optionsWithSuiteName), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, optionsWithSuiteName), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, optionsWithSuiteName) + ]); + + final Map preferencesWithEmptyOptions = + await preferences.getPreferences( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + final Map preferencesWithSuiteName = + await preferences.getPreferences( + const GetPreferencesParameters(filter: PreferencesFilters()), + optionsWithSuiteName, + ); + + expect(preferencesWithEmptyOptions.length, 2); + expect(preferencesWithSuiteName.length, 3); + + expect(preferencesWithEmptyOptions[boolKey], testBool); + expect(preferencesWithEmptyOptions[doubleKey], testDouble); + + expect(preferencesWithSuiteName[stringKey], testString); + expect(preferencesWithSuiteName[intKey], testInt); + expect(preferencesWithSuiteName[listKey], testList); + }); + testWidgets('getPreferences with filter', (WidgetTester _) async { final SharedPreferencesAsyncPlatform preferences = await getPreferences(); await Future.wait(>[ @@ -612,6 +648,37 @@ void main() { expect(keys, contains(listKey)); }); + testWidgets('getKeys with options', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, optionsWithSuiteName), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, optionsWithSuiteName), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, optionsWithSuiteName) + ]); + + final Set keysWithEmptyOptions = await preferences.getKeys( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + final Set keysWithSuiteName = await preferences.getKeys( + const GetPreferencesParameters(filter: PreferencesFilters()), + optionsWithSuiteName, + ); + + expect(keysWithEmptyOptions.length, 2); + expect(keysWithSuiteName.length, 3); + + expect(keysWithEmptyOptions, contains(boolKey)); + expect(keysWithEmptyOptions, contains(doubleKey)); + + expect(keysWithSuiteName, contains(stringKey)); + expect(keysWithSuiteName, contains(intKey)); + expect(keysWithSuiteName, contains(listKey)); + }); + testWidgets('getKeys with filter', (WidgetTester _) async { final SharedPreferencesAsyncPlatform preferences = await getPreferences(); await Future.wait(>[ From 00f0cb1a1187d6667bc2ee24748b06007d584a01 Mon Sep 17 00:00:00 2001 From: Fernando Trigoso Date: Thu, 8 Aug 2024 17:57:20 -0400 Subject: [PATCH 5/6] Tests pass on local machine. Clearing preferences for CI. --- .../example/integration_test/shared_preferences_test.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart index dc4d07ac2b9..4b102b4b28c 100644 --- a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart @@ -489,7 +489,7 @@ void main() { SharedPreferencesAsyncFoundationOptions(); final SharedPreferencesAsyncFoundationOptions optionsWithSuiteName = SharedPreferencesAsyncFoundationOptions( - suiteName: 'group.example.sharedPreferencesFoundationExample'); + suiteName: 'group.example.sharedPreferencesFoundation'); const String stringKey = 'testString'; const String boolKey = 'testBool'; @@ -509,6 +509,9 @@ void main() { await preferences.clear( const ClearPreferencesParameters(filter: PreferencesFilters()), emptyOptions); + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + optionsWithSuiteName); return preferences; } From 0165741dae77526bd429317891d1630bdeb55c75 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 12 Aug 2024 07:42:22 -0400 Subject: [PATCH 6/6] Re-bump version --- .../shared_preferences_foundation/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml b/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml index 50b20675b80..f873bb6d8e7 100644 --- a/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_foundation description: iOS and macOS implementation of the shared_preferences plugin. repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_foundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.5.1 +version: 2.5.2 environment: sdk: ^3.3.0