Repository navigation
SharedPreferences under iOS doesn't always write value to cache and/or concurrency issues #128368
Description
Is there an existing issue for this?
- I have searched the existing issues
- I have read the guide to filing a bug
Steps to reproduce
This issue / bug is being reported because I believe I have a duty of responsibility to report this issue, but don't have all the necessary / typical requirements to ensure this gets resolved.
What I do offer is a set of scenarios that were resolved by not using the SharedPreferences plugin, which leads me to assume there is something fundamentally wrong with the code or approach taken somewhere (or isn't documented in a way that avoids these issues from occurring).
The issue?
I have a complex / large Flutter application (~40K LOC) that is deployed into the wild on iOS, macOS, Android, Windows and Web. The application relies on SharePreferences for storing of key/value pairs relating to settings in the app. Things like username, tokens, theme settings, etc and so on.
On iOS, I've repeatedly had customers complain that the app forgets or is unable to retain details upon returning to the app after initial use - ie they start the app, make changes to the settings, and then subsequent activations of the app (ie from sleep or just restarting it) results in all those settings lost or not known.
Extensive debugging and logging hasn't been able to determine why, but it seems related to race conditions where the value is written and then later read (in other UI events) and the values are not kept/retained. It is behaving as if there are multiple threads that are updating/reading from different cached stores and are not being kept updated. ie the await used on the write doesn't stop another thread in dart UI that is reading the same key value which is not set (yet) in the persistent store.
Regardless, even when using reload() and other methods to keep a single instance of SharePreferences, the problems was not resolved. As a final solution, the internal "settings" class was migrated from using shared_preferences to get_storage and the problem went away - so the approach used by get_storage seems to work in a application design where shared_preferences did not. I'm unsure why, but obviously the plugin and/or code approach taken within shared_preferences has some limitations that are causing bugs/issues.
Expected results
That the use of shared_preferences works in all real world use cases where settings or key/value pairs are likely to be needed - ie within HTTP calls, multiple UI events, injected services etc. The test cases under which shared_preferences is currently evaluated are clearly not extensive enough to uncover issues and problems that might occur on some platforms (in this case iOS).
There are some issues reported of a similar nature, but are often closed or ignored by the Flutter team due to a lack of a simple example being provided. The problem is that it isn't easy to demonstrate concurrency issues with a persistent key/value store when the underlying cache might not yet be updated if another thread of execution is about to read for that same key value.
Actual results
A key value being written is not yet updated when read by another element of the event/thread etc.
Code sample
No code sample is possible.
Screenshots or Video
No response
Logs
No response
Flutter Doctor output
flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.10.3, on macOS 12.6.5 21G531 darwin-x64, locale en-AU)
[✓] Android toolchain - develop for Android devices (Android SDK version 31.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.2)
[✓] VS Code (version 1.78.2)
[✓] Connected device (2 available)
[✓] Network resources
• No issues found!
Activity
gslender commentedon Jun 7, 2023
I suspect the use of SharedPreferences in the iOS context is being impacted by this...
https://docs.flutter.dev/resources/dart-swift-concurrency
As I'm not overly familiar with both Swift on iOS and inside of SharePreferences, I'm not qualified to offer much further advice, but the behaviours seen (in a real world application by iOS users) tends to suggest it is related. Interesting also that a key/value approach that simply relies on basic native Dart File IO does not have this issue. Probably pointing to the fact that the SharedPreferences plugin is impacted by its deep roots into the Swift/iOS ecosystem.
gslender commentedon Jun 7, 2023
The only further input I can add is that it seems to be occurring on the most modern iOS devices - perhaps the modern multi-core processors and latest iOS software combination increases the likely chance of concurrency issues ??
stuartmorgan-g commentedon Jun 7, 2023
What are the scenarios? There's a vague description of a complex app here, but no details at all about how you are using it.
Writing something and then reading it later is not a race condition.
How are you using
reload? What are the "other methods"? What do you mean by "a single instance" given that it's already a singleton?Dart doesn't have threads, so it's not at all clear what you are trying to describe here.
Such as? I'm not sure what issues you are referring to.
I do not know what action you expect the Flutter team to take here; all you've indicated is that you believe that somewhere in 40k lines of complex code that we can't see, you believe some use of shared_preferences (that we also can't see) isn't working the way you expect. That is not actionable.
What do you mean by "concurrency issues" here? You are again referring to threads in Dart, which do not exist, which makes it very difficult to understand what you think the issue here is.
What is an "element of the event/thread"? It's not clear what any of those terms mean as you are using them here.
How is
shared_preferences"impacted" by a conceptual description of Dart's concurrency model written for developers with a Swift background?Closing, as there is no actionable description of a problem here, and what description there is of your usage is actively confusing (such as repeated reference to threads); there is simply no way for us to investigate based on what is here. If you would like to file a new issue with a concrete description of a failing use case, and specific details about usage, even if you haven't been able to fully minimize it, we can follow up.
gslender commentedon Jun 7, 2023
@stuartmorgan that's a shame you took no more than a few minutes to consider this. I tried to report a complex issue/bug in the platform, and as expected, you dismissed it immediately without much thought. I'm certain (by fact of switching out shared_preferences for something else) that there is some kind of flaw or deficiency in the package.
Also a real shame you can't be bothered to care enough to work through what might be a real issue. I'm certainly not the only person in the wild reporting this type of bug. Good luck with the Flutter platform's future if that's the overall way you all intend to work.
@chinmaygarde is this the normal way you expect to work with the community? Can you recommend any escalation path here as I find ignoring and moving on to be a really poor way to improve the platform. I'm going to move on, as I've fixed my problem by not using shared_preferences, but it strikes me as arrogant to just dismiss and assume no issue exists.
stuartmorgan-g commentedon Jun 7, 2023
I'm sorry that closing the issue created the impression that I had not considered it for more than a few minutes. I was simply applying our policy of closing unactionable issues, since after reading the report carefully it was clear that there was not enough information for this to be actionable (as I attempted to explain above), and I had understood your comments above to indicate that you would not be providing any further information that would allow making it actionable. I apologize if I misunderstood those comments, and I'm happy to re-open if that's not the case.
Please let us know what concrete next steps you are looking for us to take here.
gslender commentedon Jun 8, 2023
I think what you've figured out in #42407 is highly likely the problem we were seeing. Based on the symptoms and the fix you have now applied, I'm almost certain that was the issue we were seeing.
At this stage, we've moved on to another package (as mentioned above) that seems to work flawlessly, offers the same broader platform support, and given we feel confident in that working without an onerous process around getting issues considered, I think that's the better option for us right now.
But thanks for taking the time to actually look into this and if we ever decide to use this package again (and we see a repeat of this bug) then I'll raise another issue (and reference this if appropriate).
stuartmorgan-g commentedon Jun 8, 2023
As I mentioned in the other issue, if calling
reloadbefore reading preferences didn't change the behavior—as indicated in your comments above—that bug could not have been the cause. I fixed that issue because it was trivial to do so once I was aware of it, not because I expect it to be related to this issue. (There's no way for us to validate whether that's how your app was usingreload, however, without code).I understand that you've stopped using
shared_preferences, but you also requested an escalation the closing of this issue in the comment where you mentioned that. I re-opened the issue based on that comment; what next step are you looking for the Flutter team to take with this issue?gslender commentedon Jun 8, 2023
Nothing further with this issue.
I've found my solution by using another similar package that doesn't exhibit any issues.
AlexV525 commentedon Jun 8, 2023
Closing as resolved (#128368 (comment)).
github-actions commentedon Jun 22, 2023
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.