Coder Social home page Coder Social logo

userdefaultmacro's Introduction

UserDefaults Macro

Swift

This repo provides several macros to help reducing boilerplate code for using UserDefaults to store values.

Why use this macro?

  • Properly manages default values by using register(defaults:)
  • Uses proper methods to fetch native-supported types like integer, url and etc.
  • Reduces boilerplate code needed to handle UserDefaults for storage
  • Totally customizable. Full control over modifying keys and user default instance to use

Getting started

Add to your project

Add reference to this repo into your project. If using SwiftPM, you can use following template, otherwise, you can search for this repo in Xcode.

dependencies: [
    .package(url: "https://github.com/maniramezan/UserDefaultMacro.git", .upToNextMajor(from: "1.0.0")),
],

Macros

This package includes three macros, two of which are intended to work together:

UserDefaultDataStore

This is a high-level macro intended to be used on struct and class types generally:

@UserDefaultDataStore
struct UserDefaultsStore {
}

This macro adds following codes to any entity it's attached:

  1. Creates userDefaults property
  2. Create init(userDefaults:) method
@UserDefaultDataStore
struct UserDefaultsStore {
    // START: Added by UserDefaultDataStore
    private let userDefaults: UserDefaults

    internal init(userDefaults: UserDefaults = .standard) {
        self.userDefaults = userDefaults
    }
    // END
}

In addition, this macro marks any mutable variables @UserDefaultRecord. This expands these variables to computational variables which internally use userDefaults property defined above to store their values.

@UserDefaultDataStore
struct UserDefaultsStore {
    // START: Added by UserDefaultDataStore
    @UserDefaultRecord
    // END
    var isFirstTimeLaunching: Bool
    // START: Added by UserDefaultRecord
    {
        get {
            userDefaults.bool(forKey: "isFirstTimeLaunching")
        }
        set {
            userDefaults.set(newValue, forKey: "isFirstTimeLaunching")
        }
    }
    // END

    // ...
}

@UserDefaultDataStore macro defaults to internal for init(userDefaults:) method. Also, UserDefaults.standard is used as default value for userDefaults property. You can change these defaults by passing arguments to the macro:

extension UserDefaults {
    static let test = UserDefaults(suiteName: "test")!
}

@UserDefaultDataStore(using: .test, accessLevel: .public)
struct UserDefaultsStore {
    private let userDefaults: UserDefaults

    public init(userDefaults: UserDefaults = .test) {
        self.userDefaults = userDefaults
    }
}

Same is true for @UserDefaultRecord macro. You can pass a custom key and default value to it:

@UserDefaultDataStore(using: .test, accessLevel: .public)
struct UserDefaultsStore {
    @UserDefaultRecord(key: "isInitialLaunch", defaultValue: true)
    var isFirstTimeLaunching: Bool
    {
        get {
            userDefaults.bool(forKey: "isInitialLaunch")
        }

        set {
            userDefaults.setValue(newValue, forKey: "isInitialLaunch")
        }
    }

    private let userDefaults: UserDefaults

    public init(userDefaults: UserDefaults = .test) {
        self.userDefaults = userDefaults
        userDefaults.register(defaults: ["isInitialLaunch": true])
    }
}

Notice how adding defaultValue also modifies the init(userDefaults:) method to register default values for the entity.

UserDefaultProperty

This macro is less recommended and might be removed in future versions depending on feedback. This is very similar to @UserDefaultRecord macro, but it can be used standalone. You can pass UserDefaults, key and defaultValue to customize the generated computational property or simply use it without any arguments:

extension UserDefaults {
    static let test = UserDefaults(suiteName: "test")!
}

struct SomeEntity {
    static let key = "customized_key"
    @UserDefaultProperty(using: .test, key: Self.key, defaultValue: "Some default value")
    var randomGeneratedString: String
    {
        get {
            UserDefaults.test.register(defaults: [Self.key: "Some default value"])
            return UserDefaults.test.string(forKey: Self.key)!
        }

        set {
            UserDefaults.test.setValue(newValue, forKey: Self.key)
        }
    }
}

Notice as this supports default values as well, the generated computational property includes registering the default value in the getter. This assures that the default value is always registered in UserDefaults before fetching it.

Contact

If you have any questions, feature request, or reporting a bug, please feel free to use Issues in the repo or contact me on Twitter @maniramezan.

Contributing

If you have any idea to improve this repo, please feel free to fork and send a pull request. I'll be more than happy to review and merge it.

License

This repo is licensed under MIT license. See LICENSE for more info.

References

Great thanks for Jesse Squires for his great article on writing a better @UserDefault property wrapper.

userdefaultmacro's People

Contributors

maniramezan avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

ynie

userdefaultmacro's Issues

Support for KeyValueStore for use with iCloud.

First of all, please note that I am not very good at English and am using machine translation.
It is great to see that this repository and UserDefaults can be handled easily. I know this is not the purpose of this repository, but I would like to request KeyValueStore support.
For example, we hope to be able to work like this.

  • Specify a default storage location for the entire Class.
  • Option to save only certain properties locally.
@KVSModel(.cloud)
class Settings {
    @LocalSaveProperty(originalName: "otherValue_1")
    var value_1: Bool = false
    var value_2: Bool = false
    var value_3: Bool = false
    @CloudSaveProperty(originalName: "otherValue_2")
    var value_4: Bool = false
}

After deploying @KVSModel

class Settings {
    @LocalSaveProperty(originalName: "otherValue_1")
    var value_1: Bool = false
    @CloudSaveProperty
    var value_2: Bool = false
    @CloudSaveProperty
    var value_3: Bool = false
    @CloudSaveProperty(originalName: "otherValue_2")
    var value_4: Bool = false

     private let userDefaults: UserDefaults
     private let keyValuseStore: NSUbiquitousKeyValueStore
     internal init(userDefaults: UserDefaults = .standard, keyValueStore: NSUbiquitousKeyValueStore = .default) {
           self.userDefaults = userDefaults
           self.keyValuseStore = keyValuseStore
     }
}

Final output code.

class Settings {
    var value_1: Bool = false {
        get {
            userDefaults.bool(forKey: "otherValue_1")
        }
        set {
            userDefaults.set(newValue, forKey: "otherValue_1")
        }
    }
    var value_2: Bool = false {
        get {
            keyValuseStore.bool(forKey: "value_2")
        }
        set {
            keyValuseStore.set(newValue, forKey: "value_2")
        }
    }
    var value_3: Bool = false {
        get {
           keyValuseStore.bool(forKey: "value_3")
        }
        set {
           keyValuseStore.set(newValue, forKey: "value_3")
        }
    }
    var value_4: Bool = false {
        get {
            keyValuseStore.bool(forKey: "otherValue_2")
        }
        set {
            keyValuseStore.set(newValue, forKey: "otherValue_2")
        }
    }

     private let userDefaults: UserDefaults
     private let keyValuseStore: NSUbiquitousKeyValueStore
     internal init(userDefaults: UserDefaults = .standard, keyValueStore: NSUbiquitousKeyValueStore = .default) {
           self.userDefaults = userDefaults
           self.keyValuseStore = keyValuseStore
     }
}

Please note that this was written lightly and any errors are to be expected. Also, default values need to be considered.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.