• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag
 

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Property in class mirrored to user defaults
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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

  • Follow-Ups:
    • Re: Property in class mirrored to user defaults
      • From: Jonathan Taylor <email@hidden>
References: 
 >Property in class mirrored to user defaults (From: Jonathan Taylor <email@hidden>)

  • Prev by Date: Property in class mirrored to user defaults
  • Next by Date: iOS Drag and Drop
  • Previous by thread: Property in class mirrored to user defaults
  • Next by thread: Re: Property in class mirrored to user defaults
  • Index(es):
    • Date
    • Thread