• Stars
    star
    457
  • Rank 95,775 (Top 2 %)
  • Language
    Swift
  • License
    MIT License
  • Created almost 4 years ago
  • Updated about 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A lightweight property wrapper for UserDefaults done right

Foil Actions Status

A lightweight property wrapper for UserDefaults done right


About

Read the post: A better approach to writing a UserDefaults Property Wrapper

Why the name?

Foil, as in "let me quickly and easily wrap and store this leftover food in some foil so I can eat it later." 🌯 😉

Foil:
noun
North America
A very thin, pliable, easily torn sheet of aluminum used for cooking, packaging, cosmetics, and insulation.

Usage

You can use @WrappedDefault for non-optional values and @WrappedDefaultOptional for optional ones. You may wish to store all your user defaults in one place, however, that is not necessary. Any property on any type can use this wrapper.

final class AppSettings {
    static let shared = AppSettings()

    @WrappedDefault(key: "flagEnabled")
    var flagEnabled = true

    @WrappedDefault(key: "totalCount")
    var totalCount = 0

    @WrappedDefaultOptional(key: "timestamp")
    var timestamp: Date?
}

// Usage

func userDidToggleSetting(_ sender: UISwitch) {
    AppSettings.shared.flagEnabled = sender.isOn
}

There is also an included example app project.

Using enum keys

If you prefer using an enum for the keys, writing an extension specific to your app is easy. However, this is not required. In fact, unless you have a specific reason to reference the keys, this is completely unnecessary.

enum AppSettingsKey: String, CaseIterable {
    case flagEnabled
    case totalCount
    case timestamp
}

extension WrappedDefault {
    init(wrappedValue: T, _ key: AppSettingsKey) {
        self.init(wrappedValue: wrappedValue, key: key.rawValue)
    }
}

extension WrappedDefaultOptional {
    init(_ key: AppSettingsKey) {
        self.init(key: key.rawValue)
    }
}

Observing changes

There are many ways to observe property changes. The most common are by using Key-Value Observing or a Combine Publisher. KVO observing requires the object with the property to inherit from NSObject and the property must be declared as @objc dynamic.

final class AppSettings: NSObject {
    static let shared = AppSettings()

    @WrappedDefaultOptional(key: "userId")
    @objc dynamic var userId: String?

    @WrappedDefaultOptional(key: "average")
    var average: Double?
}

Using KVO

let observer = AppSettings.shared.observe(\.userId, options: [.new]) { settings, change in
    print(change)
}

Using Combine

Note: that average does not need the @objc dynamic annotation, .receiveValue will fire immediately with the current value of average and on every change after.

AppSettings.shared.$average
    .sink {
        print($0)
    }
    .store(in: &cancellable)

Combine Alternative with KVO

Note: in this case, userId needs the @objc dynamic annotation and AppSettings needs to inherit from NSObject. Then receiveValue will fire only on changes to wrapped object's value. It will not publish the initial value as in the example above.

AppSettings.shared
    .publisher(for: \.userId, options: [.new])
    .sink {
        print($0)
    }
    .store(in: &cancellable)

Supported types

The following types are supported by default for use with @WrappedDefault.

Adding support for custom types is possible by conforming to UserDefaultsSerializable. However, this is highly discouraged. UserDefaults is not intended for storing complex data structures and object graphs. You should probably be using a proper database (or serializing to disk via Codable) instead.

  • Bool
  • Int
  • UInt
  • Float
  • Double
  • String
  • URL
  • Date
  • Data
  • Array
  • Set
  • Dictionary
  • RawRepresentable types

Additional Resources

Supported Platforms

  • iOS 13.0+
  • tvOS 13.0+
  • watchOS 6.0+
  • macOS 11+

Requirements

Installation

CocoaPods

pod 'Foil', '~> 4.0.0'

Swift Package Manager

dependencies: [
    .package(url: "https://github.com/jessesquires/Foil.git", from: "4.0.0")
]

Alternatively, you can add the package directly via Xcode.

Documentation

You can read the documentation here. Generated with jazzy. Hosted by GitHub Pages.

Contributing

Interested in making contributions to this project? Please review the guides below.

Also consider sponsoring this project or buying my apps! ✌️

Credits

Created and maintained by Jesse Squires.

License

Released under the MIT License. See LICENSE for details.

Copyright © 2021-present Jesse Squires.

More Repositories

1

JSQMessagesViewController

An elegant messages UI library for iOS
Objective-C
11,142
star
2

JSQDataSourcesKit

⚠️ Deprecated ⚠️
Swift
683
star
3

JSQCoreDataKit

A swifter Core Data stack
Swift
615
star
4

JSQSystemSoundPlayer

A fancy Obj-C wrapper for Cocoa System Sound Services
Objective-C
581
star
5

PresenterKit

⚠️ Deprecated ⚠️
Swift
551
star
6

JSQWebViewController

[Deprecated] A lightweight Swift WebKit view controller for iOS
Swift
305
star
7

TIL

Things I've learned and/or things I want to remember. Notes, links, advice, example code, etc.
282
star
8

JSQNotificationObserverKit

[DEPRECATED] Generic notifications and observers for Cocoa and CocoaTouch
Swift
167
star
9

Nine41

Automate overriding the status bars for all running iOS simulators
Swift
160
star
10

JSQFlatButton

[DEPRECATED] A light-weight, flat design UIButton for iOS
Objective-C
120
star
11

DefaultStringConvertible

[DEPRECATED] A default CustomStringConvertible implementation for Swift types
Swift
119
star
12

swift-sorts

A collection of sorting algorithms implemented in Swift
Swift
106
star
13

esoteric-swift

A collection of obscure Swift programs. The less comprehensible, the better — just like normal code.
Swift
96
star
14

app-icons-script

Photoshop script to generate all iOS and macOS app icons
JavaScript
84
star
15

ReactiveCollectionsKit

Data-driven, declarative, reactive, diffable collections (and lists!) for iOS. A modern, fast, and flexible library for UICollectionView done right.
Swift
81
star
16

swift-proposal-analyzer

An analysis of Swift Evolution proposals
Swift
58
star
17

objc-sorts

A collection of sorting algorithms implemented in Objective-C
Objective-C
52
star
18

gh-workflows

A collection of useful generic GitHub Actions workflows
50
star
19

safari-tabs-to-omnifocus

AppleScript to export current Safari tabs to OmniFocus inbox items
49
star
20

jessesquires.com

Turing complete with a stack of 0xdeadbeef
HTML
45
star
21

JSQActivityKit

[DEPRECATED] Swift UIActivities for iOS
Swift
43
star
22

delete-uber

"There is no place for ethics in this business sweetheart." — a list of news articles (with excerpts) on Uber.
33
star
23

.github

Default community health files for all of my open-source projects
29
star
24

GrandSugarDispatch

[DEPRECATED] Syntactic sugar for Grand Central Dispatch (GCD)
Swift
27
star
25

ios-watchdog

Main thread watchdog for iOS
Objective-C
26
star
26

talks

Slides and materials for talks that I've given
Rich Text Format
25
star
27

template-iOS-macOS

Template repository for my iOS and macOS projects
Ruby
24
star
28

xcode-settings-backup

Backup of my Xcode configuration and various settings
Shell
21
star
29

icloud-backup-script

Backup your iCloud Drive documents using rsync
Shell
20
star
30

wwdc-notes

Taking notes on WWDC videos, new APIs, and announcements
13
star
31

drumpf

Safari Extensions are dead 💀 RIP.
JavaScript
10
star
32

playdate-learning

Learning how to make games for Playdate https://play.date
8
star
33

template-jekyll-site

Template repository for Jekyll websites
HTML
8
star
34

c-sorts

A collection of sorting algorithms implemented in C
C
8
star
35

FreedomFarts

Join the Fart Party! In God, We Fart.
Objective-C
5
star
36

rdar-19368054

Example project showing Swift compiler bug, rdar://19368054
Swift
2
star
37

likeyoungrecords

Website for Like Young Records. RIP.
PHP
2
star
38

a-world-without-police

A world without police
2
star
39

15puzzle.js

A 15 puzzle written in javascript - just a fun coding exercise
JavaScript
1
star
40

jessesquires.github.io

Open source projects homepage
1
star
41

sandbox-gh-workflows

A sandbox repo for testing GitHub Actions workflows
Ruby
1
star