Re: Property in class mirrored to user defaults

2017-06-19 Thread Alex Zavatone
As mentioned, there is NSUserDefaults.

I think that there is a better way that is pretty much built in, but one that 
we often ignore.  NSCoding.

This is what it does.
 
Matt Thompson has a nice write up on this here (in Objective-C and Swift), with 
a critical observation at the end of the comparison between Core Data and 
NSCoding/NSKeyedArchiver.

http://nshipster.com/nscoding/


Core Data   NSKeyedArchiver
Persists State  Yes Yes
Pain in the Ass Yes No

Once you set it up, you do this to save the data:

[NSKeyedArchiver archiveRootObject:myRootObject oFile:@"/path/to/archive”];

And you can do this on your singleton’s initialization to populate the object:

[NSKeyedUnarchiver unarchiveObjectWithFile:@"/path/to/archive"];


ALSO, the official patron saint of “Why didn’t Apple give us this?”, Nick 
Lockwood has created really nice AutoCoding category on NSObject that makes 
this automatic, so you don’t have to set it up manually for every property 
within your singleton.


https://github.com/nicklockwood/AutoCoding
 
You’ll also want to pay attention to when you have to change your data format 
too:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Archiving/Articles/compatibility.html#//apple_ref/doc/uid/20001055-BCICFFGE






> On Jun 12, 2017, at 4:04 AM, Jonathan Taylor  
> 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
> ___
> 
> Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)
> 
> 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:
> https://lists.apple.com/mailman/options/cocoa-dev/zav%40mac.com
> 
> This email sent to z...@mac.com

___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com


Re: Property in class mirrored to user defaults

2017-06-16 Thread Jonathan Taylor
Thankyou Charles and Keary for your replies. I somehow hadn't thought of using 
NSDefaults as backing for the properties. There are a lot of properties 
involved, but with a macro or something I should be able to set it up without a 
massive code bloat. Will give that a go.
Cheers
Jonny

On 12 Jun 2017, at 16:01, Charles Srstka  wrote:

>> On Jun 12, 2017, at 4:04 AM, Jonathan Taylor  
>> 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 = 
> ["\(#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 (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com


Re: Property in class mirrored to user defaults

2017-06-12 Thread Charles Srstka
> On Jun 12, 2017, at 4:04 AM, Jonathan Taylor  
> 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 = 
["\(#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 (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com


Property in class mirrored to user defaults

2017-06-12 Thread Jonathan Taylor
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
___

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com