TypedDefaults is a utility library to type-safely use NSUserDefaults.

Overview

TypedDefaults

Language CocoaPods License Issues

TypedDefaults is a utility library to type-safely use NSUserDefaults.

Motivation

The talk Keep Calm and Type Erase On by Gwendolyn Weston at try! Swift 2016 is great and it inspired me to apply the technique Type Erasure for actual cases in app development.

Installation

  • Install with CocoaPods

    use_frameworks!
    
    platform :ios, '8.0'
    
    pod 'TypedDefaults'

Requirements

  • iOS 8.0+
  • Swift 4.0
  • Xcode 9

Features

  • Custom types can be type-safely stored in NSUserDefaults
  • Dependency Injection support

Usage

Custom types

Custom types can be type-safely stored in NSUserDefaults.
Custom types only need to adopt DefaultsConvertible protocol as described later. No need to inherit NSObject.
Therefore, Swift native Class Struct and Enum are available for custom types.(Of course, subclasses of NSObject are available.)

DefaultsConvertible protocol

public protocol DefaultConvertible {

    static var key: String { get }

    init?(_ object: Any)

    func serialize() -> Any
}

Custom types are stored in NSUserDefaults as AnyObject.
serialize() is called when saving and init?(_ object:) is called when getting from NSUserDefaults.

It's assuemd each custom type and one configuration used in app is one-to-one relation.
Therefore, key is prepared as type property in order to assign key to one custom type.

Example of custom type

This is an example of the custom type with flag for saving photo to CameraRoll and Photo Size as camera configuration.

struct CameraConfig: DefaultConvertible {
    enum Size: Int {
        case Large, Medium, Small
    }

    var saveToCameraRoll: Bool
    var size: Size

    // MARK: DefaultConvertible

    static let key = "CameraConfig"

    init?(_ object: Any) {
        guard let dict = object as? [String: Any] else {
          return nil
        }

        self.saveToCameraRoll = dict["cameraRoll"] as? Bool ?? true
        if let rawSize = dict["size"] as? Int,
         let size = Size(rawValue: rawSize) {
            self.size = size
         } else {
            self.size = .Medium
        }
    }

    func serialize() -> Any {
        return ["cameraRoll": saveToCameraRoll, "size": size.rawValue]
    }
}

Saving custom type to NSUserDefaults

PersistentStore is the class to save custom types to NSUserDefaults.
Below is the sample of how to use it.

/// Specify a custom type when initializing PersistentStore
let userDefaults = PersistentStore<CameraConfig>()

// Make an instance of CameraConfig
var cs = CameraConfig([:])!

// Set
userDefaults.set(cs)
// Get
userDefaults.get()?.size // Medium

/// Change the size
cs.size = .Large

// Set
userDefaults.set(cs)
// Get
userDefaults.get()?.size // Large

Dependency Injection support

NSuserDefaults is not Unit Test friendly because it persistently stores data on file system.
TypedDefaults has the types InMemoryStore AnyStore for Dependency Injection in order to test types which behave differently depending on custom types stored in NSuserDefaults.

InMemoryStore adopts DefaultStoreType protocol as well as PersistentStore.
However, InMemoryStore retains custom types only on memory, which is different from PersistentStore.
As for AnyStore, it is the type to abstract PersistentStore and InMemoryStore.

Example

This is the example to use InMemoryStore and AnyStore instead of PersistentStore at Unit Test.

There is a class called CameraViewController which inherits UIViewController.
It has a property config to retain a custom type saved in NSuserDefaults. To support Dependency Injection, set AnyStore as the type of config.

class CameraViewController: UIViewController {
    lazy var config: AnyStore<CameraConfig> = {
        let ds = PersistentStore<CameraConfig>()
        return AnyStore(ds)
    }()

    ...
}

Because the type of config is not PersistentStore but AnyStore, it can be replaced with InMemoryStore at Unit Test as below.

class CameraViewControllerTests: XCTestCase {
    var viewController: CameraViewController!

    override func setUp() {
        viewController = CameraViewController()

        let defaultConfig = CameraConfig([:])!
        let ds = InMemoryStore<CameraConfig>()
        ds.set(defaultConfig) //
        viewController.config = AnyStore(ds)
    }
}

Release Notes

See https://github.com/tasanobu/TypedDefaults/releases

License

TypedDefaults is released under the MIT license. See LICENSE for details.

You might also like...
Swift APIs for SQLite: Type-safe down to the schema. Very, very, fast. Dependency free.
Swift APIs for SQLite: Type-safe down to the schema. Very, very, fast. Dependency free.

Lighter Lighter is a set of technologies applying code generation to access SQLite3 databases from Swift, e.g. in iOS applications or on the server. L

YapDatabase extensions for use with Swift
YapDatabase extensions for use with Swift

YapDatabaseExtensions Read my introductory blog post about YapDatabase & YapDatabaseExtensions, and a follow up on YapDatabaseExtensions 2. YapDatabas

Why not use UserDefaults to store Codable objects šŸ˜‰

tl;dr You love Swift's Codable protocol and use it everywhere, who doesn't! Here is an easy and very light way to store and retrieve -reasonable amoun

CoreDataCloudKitShare - Learn how to use Core Data CloudKit

Sharing Core Data Objects Between iCloud Users Implement the flow to share data

How to use the Zeit Online Content API with Swift 4

ZeitSuche_iOS Version 1.4.0 - September 12, 2018 This iOS app (iPhone) shows how to use the Zeit Online Content API with Swift 4. To test this app you

A stand-alone Swift wrapper around the mongo-c client library, enabling access to MongoDB servers.
A stand-alone Swift wrapper around the mongo-c client library, enabling access to MongoDB servers.

This package is deprecated in favour of the official Mongo Swift Driver. We advise users to switch to that pack

Elegant library to manage the interactions between view and model in Swift
Elegant library to manage the interactions between view and model in Swift

An assistant to manage the interactions between view and model ModelAssistant is a mediator between the view and model. This framework is tailored to

Disk is a powerful and simple file management library built with Apple's iOS Data Storage Guidelines in mind
Disk is a powerful and simple file management library built with Apple's iOS Data Storage Guidelines in mind

Disk is a powerful and simple file management library built with Apple's iOS Data Storage Guidelines in mind

KeyPathKit is a library that provides the standard functions to manipulate data along with a call-syntax that relies on typed keypaths to make the call sites as short and clean as possible.

KeyPathKit Context Swift 4 has introduced a new type called KeyPath, with allows to access the properties of an object with a very nice syntax. For in

Comments
  • Correct the spelling of CocoaPods in README

    Correct the spelling of CocoaPods in README

    This pull request corrects the spelling of CocoaPods šŸ¤“ https://github.com/CocoaPods/shared_resources/tree/master/media

    Created with cocoapods-readme.

    opened by ReadmeCritic 1
Releases(1.2.0)
Owner
Kazunobu Tasaka
Kazunobu Tasaka
šŸ“•A single value proxy for NSUserDefaults, with clean API.

OneStore A single value proxy for NSUserDefaults, with clean API. With OneStoreā€¦ Create one proxy(an OneStore object) for each NSUserDefaults value. M

Muukii 27 May 12, 2022
A lightweight wrapper over UserDefaults/NSUserDefaults with an additional layer of AES-256 encryption

SecureDefaults for iOS, macOS Requirements ā€¢ Usage ā€¢ Installation ā€¢ Contributing ā€¢ Acknowledgments ā€¢ Contributing ā€¢ Author ā€¢ License SecureDefaults is

Victor Peschenkov 216 Dec 22, 2022
Modern Swift API for NSUserDefaults

SwiftyUserDefaults Modern Swift API for NSUserDefaults SwiftyUserDefaults makes user defaults enjoyable to use by combining expressive Swifty API with

Luke 4.7k Jan 9, 2023
Save NSObject into NSUserDefaults in one-line, auto class mapping

Akaibu What is it ? Archive any class in just ONE-LINE of code. Automatically map class's properties under the hood. Drop in replacement of NSObject S

Roy Tang 16 Jun 21, 2021
NSUserDefaults with Wings!

#Palau: NSUserDefaults with Wings! Features | Included Types | Installation | Validators and Defaults | Custom Types | DidSet Callback | ------- Featu

symentis GmbH 387 Jun 29, 2022
SQLite.swift - A type-safe, Swift-language layer over SQLite3.

SQLite.swift provides compile-time confidence in SQL statement syntax and intent.

Stephen Celis 8.7k Jan 3, 2023
A protocol-centric, type and queue safe key-value workflow.

Light-weight, strict protocol-first styled PropertyKit helps you to easily and safely handle guaranteed values, keys or types on various situations of

gitmerge 12 Feb 26, 2021
A Generic CoreData Manager to accept any type of objects. Fastest way for adding a Database to your project.

QuickDB FileManager + CoreData ā—ļø Save and Retrieve any thing in JUST ONE line of code ā—ļø Fast usage dataBase to avoid struggling with dataBase comple

Behrad Kazemi 17 Sep 24, 2022
A percentage type for Swift

Percentage A percentage type for Swift Makes percentages more readable and type-safe, for example, for APIs that currently accept a fraction Double. -

Sindre Sorhus 257 Dec 23, 2022
A type-safe, protocol-based, pure Swift database offering effortless persistence of any object

There are many libraries out there that aims to help developers easily create and use SQLite databases. Unfortunately developers still have to get bogged down in simple tasks such as writing table definitions and SQL queries. SwiftyDB automatically handles everything you don't want to spend your time doing.

Ƙyvind Grimnes 489 Sep 9, 2022