Skip to content

SharedPreferences under iOS doesn't always write value to cache and/or concurrency issues #128368

Closed

Description

@gslender

Is there an existing issue for this?

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

  1. gslender commented on Jun 7, 2023

    @gslender
    ContributorAuthor

    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.

  2. gslender commented on Jun 7, 2023

    @gslender
    ContributorAuthor

    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 ??

  3. stuartmorgan-g commented on Jun 7, 2023

    @stuartmorgan-g
    Contributor

    What I do offer is a set of scenarios that were resolved by not using the SharedPreferences plugin

    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.

    it seems related to race conditions where the value is written and then later read (in other UI events)

    Writing something and then reading it later is not a race condition.

    even when using reload() and other methods to keep a single instance of SharePreferences

    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?

    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.

    Dart doesn't have threads, so it's not at all clear what you are trying to describe here.

    There are some issues reported of a similar nature

    Such as? I'm not sure what issues you are referring to.

    but are often closed or ignored by the Flutter team due to a lack of a simple example being provided.

    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.

    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.

    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.

    A key value being written is not yet updated when read by another element of the event/thread etc.

    What is an "element of the event/thread"? It's not clear what any of those terms mean as you are using them here.

    I suspect the use of SharedPreferences in the iOS context is being impacted by this...
    https://docs.flutter.dev/resources/dart-swift-concurrency

    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.

  4. gslender commented on Jun 7, 2023

    @gslender
    ContributorAuthor

    @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.

  5. stuartmorgan-g commented on Jun 7, 2023

    @stuartmorgan-g
    Contributor

    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.

  6. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    p: shared_preferencesPlugin to read and write Shared Preferences
    packageflutter/packages repository. See also p: labels.
    on Jun 7, 2023
  7. gslender commented on Jun 8, 2023

    @gslender
    ContributorAuthor

    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).

  8. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Jun 8, 2023
  9. stuartmorgan-g commented on Jun 8, 2023

    @stuartmorgan-g
    Contributor

    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.

    As I mentioned in the other issue, if calling reload before 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 using reload, 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?

  10. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Jun 8, 2023
  11. gslender commented on Jun 8, 2023

    @gslender
    ContributorAuthor

    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?

    Nothing further with this issue.

    I've found my solution by using another similar package that doesn't exhibit any issues.

  12. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Jun 8, 2023
  13. AlexV525 commented on Jun 8, 2023

    @AlexV525
    Member

    Closing as resolved (#128368 (comment)).

  14. github-actions commented on Jun 22, 2023

    @github-actions

    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 -v and a minimal reproduction of the issue.

  15. locked as resolved and limited conversation to collaborators on Jun 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    p: shared_preferencesPlugin to read and write Shared Preferencespackageflutter/packages repository. See also p: labels.r: solvedIssue is closed as solved

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions