{"id":53,"date":"2011-08-27T10:24:39","date_gmt":"2011-08-27T15:24:39","guid":{"rendered":"http:\/\/blog.ddg.com\/?p=53"},"modified":"2011-08-27T10:58:30","modified_gmt":"2011-08-27T15:58:30","slug":"ddgpreferences-a-class-for-all-settings","status":"publish","type":"post","link":"http:\/\/blog.ddg.com\/?p=53","title":{"rendered":"DDGPreferences: A Class for all Settings"},"content":{"rendered":"<p>Almost every iOS application has individual user preferences. Some apps also use Apple&#8217;s Settings app, some don&#8217;t. If you develop many different applications, as I do in my development consulting practice, it is tedious to code up a custom preferences class for each app. My class, <code>DDGPreferences<\/code>, is an attempt to minimize the tedium by providing a very simple API to the <code>NSUserDefaults<\/code> class for both settings and custom preferences. In addition to <code>DDGPreferences<\/code>, I have included a set of standard logging macros, <code>DDGMacros<\/code>, and an example single view iOS app tying all of the pieces together.<\/p>\n<h2 id=\"the-api\">The API<\/h2>\n<p>I like simple APIs. They are easy to use and easy to share. I wanted this API to be no more than a list of properties. As in:<\/p>\n<pre><code>@interface Preferences : DDGPreferences\n\n@property (nonatomic, copy)   NSString *nameSetting;\n@property (nonatomic, assign, getter=isEnabledSetting) BOOL enabledSetting;\n@property (nonatomic, assign) CGFloat sliderSetting;\n\n@property (nonatomic, copy)   NSString *namePref;\n@property (nonatomic, assign, getter=isEnabledPref) BOOL enabledPref;\n@property (nonatomic, assign) CGFloat sliderPref;\n@property (nonatomic, retain) NSData *rectPrefData;\n\n@end\n<\/code><\/pre>\n<p>Furthermore, the only difference between whether a property was visible in Apple&#8217;s settings app should be if a key matching its exact name was present in the <code>Root.plist<\/code> in the <code>Settings.bundle<\/code>. In other words, each setting has an identifier\/key which is identical to a property name. This post is not a tutorial on how to build an app that uses Apple&#8217;s Settings application. That said the example app has only made minor changes to the fields created when you add a <code>Settings.bundle<\/code> to your app. (In particular, I changed the Key\/Identifier from using under bars, _, as word separators to using standard Cocoa camel case.) In other words, I believe a beginner should be able to follow the logic of using this class without too much difficulty.<\/p>\n<h3 id=\"how-do-you-use-ddgpreferences\">How do you use DDGPreferences?<\/h3>\n<p>Using <code>DDGPreferences<\/code> is simple. Make your Preferences class a subclass of <code>DDGPreferences<\/code> and then instantiate it. Really, that is all you have to do. Your preferences are limited to those supported by Apple&#8217;s .plist files. This is not as restrictive as it might seem. Later, I&#8217;ll show you how to convert an arbitrary NSCoding compliant class to a preference.<\/p>\n<p>If you have default preference values which are different from the state of a freshly initialized object, then you must implement the <code>DDGPreferences<\/code> protocol&#8217;s single method, <code>-setDefaultPreferences<\/code>. The example application has this method.<\/p>\n<p>What about synchronizing changes between Apple&#8217;s Settings app and yours while the app is in the background? When your app returns to the foreground, I recommend you read\/write the Settings managed values in response to the <code>UIApplicationDidBecomeActiveNotification<\/code>, <code>UIApplicationWillResignActiveNotification<\/code> notification pair. The example app shows one way to do this. All other coordination with the Settings app is handled by <code>DDGPreferences<\/code>.<\/p>\n<h3 id=\"the-ddgpreferences-app\">The DDGPreferences app:<\/h3>\n<p>I&#8217;ve included an app showing how to use <code>DDGPreferences<\/code>. It is a single view iPhone app with an array of identical controls for both Apple&#8217;s Settings app and the <code>DDGPreferences<\/code> app. You can change the preferences for the settings in both apps and they transfer bi-directionally. A simple <code>CGRect<\/code> is also initialized and stored. It is then displayed in a UILabel. How to store a complex structure, such as a <code>CGRect<\/code>, is described below. Traditionally, your preferences are stored with your application singleton. In this example, for pedagogical simplicity, I store them in the root view controller.<\/p>\n<h3 id=\"saving-complex-classes\">Saving complex classes:<\/h3>\n<p>This is an advanced technique and, if you can, I recommend that you avoid using it. Any <code>NSCoding<\/code> compliant class can be stored, with care, in <code>DDGPreferences<\/code>. As <code>DDGPreferences<\/code> uses the properties to determine what needs to be persisted, you cannot just define a <code>@property<\/code> for your class that is not one of those supported by Apple&#8217;s .plist format; you need to define an <code>NSData<\/code> typed instance variable to hold an archived instance of your class\/structure. In the example, <code>rectPrefData<\/code> is that property. To access this data as your preferred type, you need to define &#8220;old school&#8221; Objective-C v1 style accessors. In the example, these are <code>-rectPref<\/code>\/<code>-setRectPref:<\/code>. Somewhat obviously, these accessors will use rectPrefData to store the value. A example implementation of these methods is:<\/p>\n<pre><code>- (CGRect) rectPref {\n\n    return [[NSKeyedUnarchiver unarchiveObjectWithData:\n             self.rectPrefData] CGRectValue];\n\n} \/\/ -rectPref\n\n- (void) setRectPref: (CGRect) rect {\n\n    self.rectPrefData = [NSKeyedArchiver archivedDataWithRootObject:\n                         [NSValue valueWithCGRect: rect]];\n} \/\/ -setRectPref:\n<\/code><\/pre>\n<p>They work by taking your <code>NSCoding<\/code> compliant class and archiving it using the <code>NSKeyedArchiver<\/code>\/<code>NSKeyedUnarchiver<\/code> classes. The above methods, for pedagogical purposes, are not key-value coding compliant.<\/p>\n<h3 id=\"licensing\">Licensing:<\/h3>\n<p><code>DDGPreferences<\/code> is covered under a public attribution required version of the new BSD license. Why do I require that you acknowledge me publicly in your app? Similarly to many other developers, I make my code available under an open source license as an advertisement for my development consulting services. Hence, I need to be able to easily point to applications that use my code. While it is not necessary, I would appreciate it if you also sent me an email saying in which apps you use <code>DDGPreferences<\/code>.<\/p>\n<p>From my experience making other code available under an open source license, some folks will write asking to be relieved of my public recognition requirement. Unless the requestor is willing to compensate me to change the licensing terms, I will always decline to change my agreement. I have put some time and care into crafting this class, app and this blog post. That time deserves compensation. I have chosen to be compensated by using this class as a marketing mechanism. I apologize if this does not align with your open source values. I have a family to feed and a mortgage to service. I sell coding services and code to provide for all of us. I hope you understand.<\/p>\n<h3 id=\"where-to-get-the-code\">Where to get the code:<\/h3>\n<p>This code is available from GitHub at this URL: &lt;<a href=\"https:\/\/github.com\/adonoho\/DDGPreferences\">https:\/\/github.com\/adonoho\/DDGPreferences<\/a>&gt;. I will be tracking comments at both GitHub and this post on my personal blog, &lt;<a href=\"http:\/\/blog.ddg.com\/?p=53\">http:\/\/blog.DDG.com\/?p=53<\/a>&gt;. I, of course, encourage you to send in bug fixes and make suggestions to improve <code>DDGPreferences<\/code> for all of us.<\/p>\n<p>I hope you find <code>DDGPreferences<\/code> useful.<\/p>\n<p>In a future post for advanced programmers, I will describe how <code>DDGPreferences<\/code> functions.<\/p>\n<h3 id=\"acknowledgements\">Acknowledgements:<\/h3>\n<p>I would like to thank Scott Gustafson and Mason Weems for their suggestions and support. Also, Austin&#8217;s local Mac OS X\/iOS developer group, Cocoa Coders, organized by Jim Hillhouse and Rajat Datta, has been extremely helpful in my return to software engineering.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Almost every iOS application has individual user preferences. Some apps also use Apple&#8217;s Settings app, some don&#8217;t. If you develop many different applications, as I do in my development consulting practice, it is tedious to code up a custom preferences class for each app. My class, DDGPreferences, is an attempt to minimize the tedium by [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,3,4],"tags":[],"class_list":["post-53","post","type-post","status-publish","format-standard","hentry","category-code","category-ddg","category-iphone"],"_links":{"self":[{"href":"http:\/\/blog.ddg.com\/index.php?rest_route=\/wp\/v2\/posts\/53","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/blog.ddg.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.ddg.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.ddg.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.ddg.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=53"}],"version-history":[{"count":5,"href":"http:\/\/blog.ddg.com\/index.php?rest_route=\/wp\/v2\/posts\/53\/revisions"}],"predecessor-version":[{"id":58,"href":"http:\/\/blog.ddg.com\/index.php?rest_route=\/wp\/v2\/posts\/53\/revisions\/58"}],"wp:attachment":[{"href":"http:\/\/blog.ddg.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=53"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.ddg.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=53"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.ddg.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=53"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}