Re: Property in class mirrored to user defaults
Re: Property in class mirrored to user defaults
- Subject: Re: Property in class mirrored to user defaults
- From: Charles Srstka <email@hidden>
- Date: Mon, 12 Jun 2017 10:01:44 -0500
> On Jun 12, 2017, at 4:04 AM, Jonathan Taylor <email@hidden>
> wrote:
>
> Hi all,
>
> This feels like a very basic question, but one that I have not had any luck
> searching for (maybe I am using the wrong terms?).
>
> At the moment I have properties in a (singleton) class that are bound to UI
> elements. At the moment they have the same automatic values every time the
> app is launched, although the user can change them while it is running. What
> I would like is for the user's previous choice to be remembered when the app
> is next launched. Obviously this would seem to be a job for NSUserDefaults. I
> am not sure quite what the right way to structure things is, though.
>
> All the simple examples I have seen bind to NSUserDefaults and then if the
> program actually wants to know what the value is, it simply accesses the
> values directly on NSUserDefaults. I would prefer not to do that, though (see
> below).
>
> What I ~think~ I would like is to still be able to access the values as
> properties of my class. That seems to me like the natural way, and it would
> seem odd to have two categories of value, some accessed through properties on
> the class, and some accessed some other way via NSUserDefaults. However, if I
> bind the UI elements to the shared user defaults object, is that not what
> will happen? Or is there some way that I can "link" my class and the user
> defaults object, so that the properties are saved in user defaults but I can
> still access them in a fairly seamless way from my class? I do like the idea
> of having the properties (and their types) explicitly declared as part of my
> class, rather than being mysterious objects that only exist in the user
> defaults.
>
> Does anyone have any advice on how I can achieve this, or on how I should be
> thinking about all this differently?
> Thanks
> Jonny
If performance isn’t an issue, as it usually isn’t for properties linked to UI
elements, and you don’t want to bind the UI elements directly to an
NSUserDefaultsController, you can just use UserDefaults as the backing for a
property, like this:
class MyClass: NSObject {
// this is a property only so we can make key paths that will go
through it
@objc private let userDefaults: UserDefaults
private static let fooDefaultsKey = "Foo"
@objc private static let keyPathsForValuesAffectingFoo: Set<String> =
["\(#keyPath(userDefaults)).\(MyClass.fooDefaultsKey)"]
@objc dynamic var foo: String {
get { return UserDefaults.standard.string(forKey:
MyClass.fooDefaultsKey) ?? "" }
set { UserDefaults.standard.set(newValue, forKey:
MyClass.fooDefaultsKey) }
}
override init() {
self.userDefaults = UserDefaults.standard
super.init()
}
}
This is pretty cool; on recent releases of macOS, it’ll respond to changes in
the defaults even if they come from outside the process. So, if you observe the
“foo” property, and then manually use /usr/bin/defaults to change the defaults,
your notifications in the app will fire.
Alternately, you can just set up the property at init time and then update the
defaults whenever it changes, like this. You won’t get the cool observation
behavior, though, unless you use KVO’s infamously ugly observation APIs (the
slick new closure-based one won’t work here, since we’re stuck with using
string keys for this).
class MyClass: NSObject {
private static let fooDefaultsKey = "Foo"
@objc dynamic var foo: String {
didSet { UserDefaults.standard.set(self.foo, forKey:
MyClass.fooDefaultsKey) }
}
override init() {
self.foo = UserDefaults.standard.string(forKey:
MyClass.fooDefaultsKey) ?? ""
super.init()
}
}
Charles
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden