{"id":19139,"date":"2017-10-06T16:34:19","date_gmt":"2017-10-06T20:34:19","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=19139"},"modified":"2025-12-16T15:49:33","modified_gmt":"2025-12-16T20:49:33","slug":"type-safe-user-defaults-in-swift","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2017\/10\/06\/type-safe-user-defaults-in-swift\/","title":{"rendered":"Type-Safe User Defaults in Swift"},"content":{"rendered":"<p><a href=\"https:\/\/mikeash.com\/pyblog\/friday-qa-2017-10-06-type-safe-user-defaults.html\">Mike Ash<\/a>:<\/p>\n<blockquote cite=\"https:\/\/mikeash.com\/pyblog\/friday-qa-2017-10-06-type-safe-user-defaults.html\">\n<p>To declare a key, write a <code>struct<\/code> conforming to the <code>TSUD<\/code> protocol. Inside, implement a single <code>static<\/code> property called <code>defaultValue<\/code> which contains the value to be returned if <code>UserDefaults<\/code> doesn't contain a value:<\/p>\n<pre>struct fontSize: TSUD {\n    static let defaultValue = 12.0\n}\n<\/pre>\n<\/blockquote>\n\n<p>This is an interesting take that uses a new type for each key and then creates the string key from the type&rsquo;s name. I have been using a simpler system that looks something like this:<\/p>\n<pre>\/\/ register\nextension MJTUserDefaults.IntDefault {\n    static let cacheSize = key(\"CacheSize\", 2000)\n}\n\n\/\/ query\nMJTDefaults[.cacheSize]\n<\/pre>\n<p>I like this because it&rsquo;s compact to register a bunch of keys in sequence and because putting them all into the same type works well for auto-completion.<\/p>\n\n<p>Update (2017-10-09): <a href=\"http:\/\/www.xs-labs.com\/en\/blog\/2017\/10\/08\/better-nsuserdefaults-with-swift\/\">Jean-David Gadina<\/a> (<a href=\"https:\/\/twitter.com\/macmade\/status\/917408618902941697\">tweet<\/a>):<\/p>\n<blockquote cite=\"http:\/\/www.xs-labs.com\/en\/blog\/2017\/10\/08\/better-nsuserdefaults-with-swift\/\"><p>The solution I propose is to automate the wrapping code on runtime, using reflection.\nThis used to be done with the Objective-C runtime.\nWith Swift, it&rsquo;s even easier through the use of the <code>Mirror<\/code> class.<\/p><\/blockquote>\n\n<p>Update (2017-10-14): <a href=\"http:\/\/bou.io\/TypeSafeUserDefaults.html\">Nicolas Bouilleaud<\/a>:<\/p>\n<blockquote cite=\"http:\/\/bou.io\/TypeSafeUserDefaults.html\"><p><code>#function<\/code>, as per the <a href=\"https:\/\/developer.apple.com\/library\/content\/documentation\/Swift\/Conceptual\/Swift_Programming_Language\/Expressions.html\">documentation<\/a>, &ldquo;inside a property getter or setter it is the name of that property&rdquo;. Unfortunately, that means we can&rsquo;t write:<\/p>\n<pre>struct Prefs_ {\n    var foo = TSUD&lt;Date&gt;()\n}<\/pre>\n<p>because in this context, <code>#function<\/code> would be <code>Prefs_<\/code>. Using <code>lazy<\/code>, I guess, implicitely creates a closure, which is called as a getter the first time the property is accessed, and <code>#function<\/code> is <code>foo<\/code>.<\/p><\/blockquote>","protected":false},"excerpt":{"rendered":"<p>Mike Ash: To declare a key, write a struct conforming to the TSUD protocol. Inside, implement a single static property called defaultValue which contains the value to be returned if UserDefaults doesn't contain a value: struct fontSize: TSUD { static let defaultValue = 12.0 } This is an interesting take that uses a new type [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"apple_news_api_created_at":"2025-12-16T20:49:36Z","apple_news_api_id":"587fa318-b6f6-468b-bd23-79e6bca3fc28","apple_news_api_modified_at":"2025-12-16T20:49:36Z","apple_news_api_revision":"AAAAAAAAAAD\/\/\/\/\/\/\/\/\/\/w==","apple_news_api_share_url":"https:\/\/apple.news\/AWH-jGLb2Rou9I3nmvKP8KA","apple_news_coverimage":0,"apple_news_coverimage_caption":"","apple_news_is_hidden":false,"apple_news_is_paid":false,"apple_news_is_preview":false,"apple_news_is_sponsored":false,"apple_news_maturity_rating":"","apple_news_metadata":"\"\"","apple_news_pullquote":"","apple_news_pullquote_position":"","apple_news_slug":"","apple_news_sections":"\"\"","apple_news_suppress_video_url":false,"apple_news_use_image_component":false,"footnotes":""},"categories":[4],"tags":[69,31,1472,30,1529,2868,71,901],"class_list":["post-19139","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-cocoa","tag-ios","tag-ios-11","tag-mac","tag-macos-10-13","tag-nsuserdefaults","tag-programming","tag-swift-programming-language"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/19139","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/comments?post=19139"}],"version-history":[{"count":4,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/19139\/revisions"}],"predecessor-version":[{"id":19223,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/19139\/revisions\/19223"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=19139"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=19139"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=19139"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}