Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@AppStorage behind the scenes? #1

Open
pd95 opened this issue Aug 27, 2021 · 9 comments
Open

@AppStorage behind the scenes? #1

pd95 opened this issue Aug 27, 2021 · 9 comments
Assignees
Labels
question Further information is requested

Comments

@pd95
Copy link
Owner

pd95 commented Aug 27, 2021

The current implementation of @AppStorage for iOS 13 is fully relying on the UserDefaults data store:
We do not keep track of the current value but fully rely on some caching by the store. Through KVO we observe whether the value has changed and trigger objectWillChange on our storage wrapper.

How is Apple doing this on iOS 14? Do they cache the value for quick access?

Latest version of the source: AppStorage.swift

@pd95 pd95 added the question Further information is requested label Aug 27, 2021
@pd95 pd95 assigned pd95 and unassigned pd95 Aug 27, 2021
@pd95
Copy link
Owner Author

pd95 commented Aug 27, 2021

@malhal: Do you have insights? Perhaps using Hopper?

@malhal
Copy link

malhal commented Aug 27, 2021

I don't think they cache the value but they do cache the store.

The struct AppStorage has a var wrappedValue that I believe does KVO on a var location which is an instance of the UserDefaultLocation class using its func getValue:forReading: that retrieves the NSUserDefaults and sets it on an ivar store.

@malhal
Copy link

malhal commented Aug 27, 2021

They might only be caching the store if they have to retrieve it from the environment, that uses a lot of private methods.

@malhal
Copy link

malhal commented Aug 27, 2021

I just found a var cachedValue in UserDefaultLocation (I think its in an extension) so I believe they do cache the value.

By the way they store their instances in a Box, e.g. UserDefaultLocationBox I'm not sure if that is something Swift generates though.

@pd95
Copy link
Owner Author

pd95 commented Aug 28, 2021

Interesting... Probably they call their wrapper class UserDefaultLocation... Perhaps the "Box" thing is due to the generic types used in this context "boxing" the different types into a helper struct. I've bought now my copy of Hopper and will see if I can reproduce your findings. The demo version was very promising but at my speed 30 minutes is not enough 😉

@pd95
Copy link
Owner Author

pd95 commented Aug 28, 2021

Based on your input and some fiddling with Hopper, I've rewritten my AppStorage implementation using some Generic approach as done by Apple. See PR #2.
I could get rid of 100 lines of code 👍

@pd95
Copy link
Owner Author

pd95 commented Aug 29, 2021

Based on what I learned on AppStorage above I have now implemented SceneStorage in commit 76d6197

Initially I looked at how Apple implemented it in SwiftUI, but found that it is really complicated... So I created a custom solution using a SceneManager class which is instantiated in the (iOS 13 specific) SceneDelegate in scene(_:, willConnectTo:, options:) and attached to the View hierarchy. It holds a [AnyHashable: Any] dictionary which is used to store the scene specific values. The SceneStorage instances are asking the SceneManager for the current value and update it directly there. So far my tests have been successful and the values were persisted across multiple app launches.

I haven't used any KVO in SceneStorage to track any value. I'm not sure I would need this at all. Im also not sure whether it would be possible to track a single value of a dictionary...

The SceneManager is also responsible for tracking and updating the scenePhase and injecting the information into the environment.

@pd95
Copy link
Owner Author

pd95 commented Sep 2, 2021

After having written Unit tests for AppStorage (and having fixed the issue with URL type), I'm pretty confident that I have a good solution matching Apples implementation. I will probably write some Unit tests for SceneStorage and then check whether I can package it using Swift Package Manager. This would make it easier to integrate into other projects even if it's only for testing.

@pd95
Copy link
Owner Author

pd95 commented Oct 11, 2021

Sadly I was not able to build a SPM package, as I basically need multiple versions based on the same code: one for iOS13 and one for iOS14. And if I would want to compare the demo app to iOS15 behaviour (without any shims) without modifying the code, I have to provide a library/package for iOS15.
In SPM I would have to create 3 copies of the same package including the same code with different deployment target, so I have created the library SwiftUIShim in three variations in the current Xcode project and adapted the demo app code to use import SwiftUIShim instead of SwiftUI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants