{"id":32053,"date":"2021-04-02T16:47:45","date_gmt":"2021-04-02T20:47:45","guid":{"rendered":"https:\/\/mjtsai.com\/blog\/?p=32053"},"modified":"2025-12-16T15:49:20","modified_gmt":"2025-12-16T20:49:20","slug":"foil-userdefaults-property-wrapper","status":"publish","type":"post","link":"https:\/\/mjtsai.com\/blog\/2021\/04\/02\/foil-userdefaults-property-wrapper\/","title":{"rendered":"&ldquo;Foil&rdquo; UserDefaults Property Wrapper"},"content":{"rendered":"<p><a href=\"https:\/\/www.jessesquires.com\/blog\/2021\/03\/26\/a-better-approach-to-writing-a-userdefaults-property-wrapper\/\">Jesse Squires<\/a> (<a href=\"https:\/\/twitter.com\/jesse_squires\/status\/1375683640814940160\">tweet<\/a>):<\/p>\n<blockquote cite=\"https:\/\/www.jessesquires.com\/blog\/2021\/03\/26\/a-better-approach-to-writing-a-userdefaults-property-wrapper\/\"><p><a href=\"https:\/\/developer.apple.com\/documentation\/foundation\/userdefaults\">UserDefaults<\/a> is one of the most misused APIs on Apple platforms. Specifically, most developers do not handle default values correctly. In fact, I have never worked on a single production codebase at a company where this was done accurately. Most libraries get it wrong, too.<\/p><p>[&#8230;]<\/p><p>There are a few libraries that currently provide a property wrapper for <code>UserDefaults<\/code>. However, the ones that I know about each have a combination of the following issues: (1) default values are not registered, (2) optionals are not handled nicely, (3) the library is extremely complicated for such a simple task.<\/p><\/blockquote>\n\n<p>I like <a href=\"https:\/\/github.com\/jessesquires\/foil\">his approach<\/a>. Mine differs in that:<\/p>\n\n<ul>\n<li>Instead of casting the result of <code>UserDefaults.object(forKey:)<\/code>, it uses the type-specific methods like <code>bool(forKey:)<\/code> and <code>integer(forKey:)<\/code>, so that if the value is of an unexpected type (which can easily happen when using <code>defaults write<\/code>) it won&rsquo;t crash or return <code>false<\/code> when the string was <code>YES<\/code>.<\/li>\n<li><p>The initializer uses the magic <code>wrappedValue<\/code> parameter, so that the default value can be provided in the property declaration itself. This also allows the property&rsquo;s type to be inferred.<\/p><\/li>\n<li><p>The property wrapper caches values read from <code>UserDefaults<\/code>. I&rsquo;ve found that, even with the overhead of making the cache threadsafe, this can really speed things up.<\/p><\/li>\n<li><p>In order to make sure the cache remains valid, it observes when each default is modified. Clients can do this, too, e.g. to see when another part of the app has updated a preference.<\/p><\/li>\n<\/ul>\n\n<p>Previously:<\/p>\n<ul>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2019\/12\/13\/userdefaults-access-via-property-wrappers\/\">UserDefaults Access via Property Wrappers<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2019\/06\/25\/swift-property-wrappers\/\">Swift Property Wrappers<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2017\/10\/06\/type-safe-user-defaults-in-swift\/\">Type-Safe User Defaults in Swift<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2016\/02\/22\/use-and-misuse-of-nsuserdefaults\/\">Use and Misuse of NSUserDefaults<\/a><\/li>\n<li><a href=\"https:\/\/mjtsai.com\/blog\/2014\/01\/22\/debugging-nsuserdefaults\/\">Debugging NSUserDefaults<\/a><\/li>\n<\/ul>","protected":false},"excerpt":{"rendered":"<p>Jesse Squires (tweet): UserDefaults is one of the most misused APIs on Apple platforms. Specifically, most developers do not handle default values correctly. In fact, I have never worked on a single production codebase at a company where this was done accurately. Most libraries get it wrong, too.[&#8230;]There are a few libraries that currently provide [&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":"2021-04-02T20:47:50Z","apple_news_api_id":"040ee265-6051-4bb9-90d5-da7643bd42b9","apple_news_api_modified_at":"2025-12-16T20:49:23Z","apple_news_api_revision":"AAAAAAAAAAAAAAAAAAAAAA==","apple_news_api_share_url":"https:\/\/apple.news\/ABA7iZWBRS7mQ1dp2Q71CuQ","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,1837,30,1891,2868,74,901],"class_list":["post-32053","post","type-post","status-publish","format-standard","hentry","category-programming-category","tag-cocoa","tag-ios","tag-ios-14","tag-mac","tag-macos-11-0","tag-nsuserdefaults","tag-opensource","tag-swift-programming-language"],"apple_news_notices":[],"_links":{"self":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/32053","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=32053"}],"version-history":[{"count":1,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/32053\/revisions"}],"predecessor-version":[{"id":32054,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/posts\/32053\/revisions\/32054"}],"wp:attachment":[{"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/media?parent=32053"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/categories?post=32053"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mjtsai.com\/blog\/wp-json\/wp\/v2\/tags?post=32053"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}