• Stars
    star
    573
  • Rank 77,865 (Top 2 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 9 years ago
  • Updated over 1 year ago

Reviews

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

Repository Details

Prephirences is a Swift library that provides useful protocols and convenience methods to manage application preferences, configurations and app-state. UserDefaults

Prephirences - Preϕrences

License Platform Language Issues Cocoapod Carthage compatible Build Status

Become a Patron! Buy me a coffee

Prephirences is a Swift library that provides useful protocols and convenience methods to manage application preferences, configurations and app-state.

  @Preference(key: "enabled")
  var enabled: Bool?

  @UserDefaultsPreference(key: "my.string.pref")
  var pref: String?

  @MutablePreference(preferences: UserDefaults.standard, key: "enabled")
  var enabled: Bool?
let userDefaults = UserDefaults.standard
if let enabled = userDefaults["enabled"] as? Bool {..}
userDefaults["mycolorkey", archive] = UIColor.blue

Preferences is not only UserDefaults, it could be also :

  • Keychain to store credential
  • Any dictionary
  • Application information from Bundle
  • File stored preferences (ex: plist)
  • iCloud stored preferences NSUbiquitousKeyValueStore
  • or your own private application preferences

ie. any object which implement the simple protocol PreferencesType, which define key value store methods.

You can also combine multiples preferences and work with them transparently (see Composing)

Contents

Usage

Creating

The simplest implementation of PreferencesType is DictionaryPreferences

// From Dictionary
var fromDico = DictionaryPreferences(myDictionary)
// or literal
var fromDicoLiteral: DictionaryPreferences = ["myKey": "myValue", "bool": true]

// From filepath
if let fromFile = DictionaryPreferences(filePath: "/my/file/path") {..}
// ...in main bundle ##
if let fromFile = DictionaryPreferences(filename: "prefs", ofType: "plist") {..}

Accessing

You can access with all methods defined in PreferencesType protocol

if let myValue = fromDicoLiteral.object(forKey: "myKey") {..}
if let myValue = fromDicoLiteral["bool"] as? Bool {..}

var hasKey = fromDicoLiteral.hasObject(forKey: "myKey")
var myValue = fromDicoLiteral.bool(forKey: "myKey")
..

If you want to access using RawRepresentable enum.

enum MyKey: PreferenceKey/*String*/ {
   case Key1, Key2, ...
}
if let myValue = fromDicoLiteral.object(forKey: MyKey.Key1) {..}
var myValue = fromDicoLiteral.bool(forKey: MyKey.Key2)

⚠️ RawRepresentableKey must be imported, see setup.

Modifying

Modifiable preferences implement the protocol MutablePreferencesTypes

The simplest implementation is MutableDictionaryPreferences

var mutableFromDico: MutableDictionaryPreferences = ["myKey": "myValue"]

mutableFromDico["newKey"] = "newValue"
mutableFromDico.set("myValue", forKey: "newKey")
mutableFromDico.set(true, forKey: "newKey")
...

You can append dictionary or other PreferencesType using operators

mutableFromDico += ["newKey": "newValue", "otherKey": true]

You can also remove one preference

mutableFromDico -= "myKey"

Apply operators to one preference

You can extract a MutablePreference from any MutablePreferencesTypes and apply operators according to its value type

var intPref: MutablePreference<Int> = aPrefs.preference(forKey: "intKey")
var intPref: MutablePreference<Int> = aPrefs <| "intKey"

intPref++
intPref--
intPref += 30
intPref -= 30
intPref *= 20
intPref %= 7
intPref /= 3

switch(intPref) {
   case 1: println("one")
   case 2...10: println("not one or zero but...")
   default: println("unkwown")
}

var boolPref: MutablePreference<Bool> = aPrefs <| "boolKey")

boolPref &= false
boolPref |= true
boolPref != true

You can also use some methods to change value

var stringPref: MutablePreference<String> = userDefaults <| "stringKey"
stringPref.apply { value in
  return value?.uppercaseString
}

or transform the value type using closures

let intFromBoolPref : MutablePreference<Int> = boolPref.transform { value in
  return (value ?? false) ? 1:0
}

Transformation and archiving

Before storing or accessing the value, transformation could be applied, which conform to protocol PreferenceTransformation.

This allow to archive, to change type, return default value if nil and many more.

You can get and set value using subscript

userDefaults["aKey", myTransformation] = myObject

if let object = userDefaults["aKey", myTransformation] {...}

If you extract one preference, use transformation property to setup the transformation

var aPref: MutablePreference<MyObject> = userDefaults <| "aKey"
aPref.transformation = myTransformation

or you can use some utility functions to specify a default value when the stored value match a condition

public var intValueMin10: MutablePreference<Int> {
  get {
    return userDefaults.preference(forKey: "intKey")
          .whenNil(use: 100)
          .ensure(when: lessThan100, use: 100)
  }
  set {..}
}

Archiving

Archiving is particularly useful with NSUserDefaults because NSUserDefaults can't store all type of objects. The following functions could help by transforming the value into an other type

You can archive into Data using this two methods

userDefaults.set(objectToArchive: UIColor.blueColor(), forKey: "colorKey")
userDefaults["colorKey", .Archive] = UIColor.blueColor()

and unarchive using

if let color = userDefaults.unarchiveObject(forKey: "colorKey") as? UIColor {..}
if let color = userDefaults["colorKey", .Archive]  as? UIColor {..}

If you extract one preference, use transformation property to setup archive mode

var colorPref: MutablePreference<UIColor> = userDefaults <| "colorKey"
colorPref.transformation = TransformationKey.Archive
colorPref.value = UIColor.redColor()
if let color = colorPref.value as? UIColor {..}

NSValueTransformer

You can also apply for all objects type an NSValueTransformer, to transform into JSON for instance

userDefaults["colorKey", myValueTransformerToJson] = myComplexObject

if let object = userDefaults["colorKey", myValueTransformerToJson] {...}

⚠️ allowsReverseTransformation must return true

Store RawRepresentable objects

For RawRepresentable objects like enum you can use the computed attribute preferenceTransformation as transformation

enum PrefEnum: String {
    case One, Two, Three
}
var pref: MutablePreference<PrefEnum> = preferences <| "enumKey"
pref.transformation = PrefEnum.preferenceTransformation
pref.value = PrefEnum.Two

Some implementations

UserDefaults

UserDefaults implement PreferencesType and can be acceded with same methods

let userDefaults = UserDefaults.standard

if let myValue = userDefaults["mykey"] as? Bool {..}

NSUserDefaults implement also MutablePreferencesType and can be modified with same methods

userDefaults["mykey"] = "myvalue"
// with type to archive
userDefaults["mykey", .Archive] = UIColor.blueColor()

Bundle

All Bundle implement PreferencesType, allowing to access Info.plist file.

For instance the Bundle.main contains many useful informations about your application.

Prephirences framework come with some predefined enums described in apple documentations and defined in PropertyListKeys.swift

let bundle = Bundle.main
let applicationName = bundle[.CFBundleName] as? String

NSUbiquitousKeyValueStore

To store in iCloud, NSUbiquitousKeyValueStore implement also PreferencesType

See composing chapter to merge and synchronize iCloud preferences with other preferences.

Key Value Coding

Foundation classes

You can wrap an object respond to implicit protocol NSKeyValueCoding in KVCPreferences or MutableKVCPreferences

let kvcPref = MutableKVCPreferences(myObject)

Be sure to affect the correct object type

Swift classes

Using ReflectingPreferences you can easily access to a struct or swift class. Just add extension.

struct PreferenceStruct {
    var color: String = "red"
    var age: Int
    let enabled: Bool = true
}
extension PreferenceStruct: ReflectingPreferences {}

You can then use all functions from PreferencesType

var pref = PreferenceStruct(color: "red", age: 33)
if pref["color"] as? String { .. }

Core Data

You can wrap on NSManageObject in ManageObjectPreferences or MutableManageObjectPreferences

let managedPref = ManageObjectPreferences(myManagedObject)

Plist

There is many way to play with plist files

  • You can use Plist (with the useful write method)
  • You can init DictionaryPreferences or MutableDictionaryPreferences with plist file
  • You can read dictionary from plist file and use set(dictionary: on any mutable preferences

Keychain

To store into keychain, use an instance of KeychainPreferences

KeychainPreferences.sharedInstance // default instance with main bundle id
var keychain = KeychainPreferences(service: "com.github.example")

then store String or Data

keychain["anUserName"] = "password-encoded"

if let pass = keychain.stringForKey("anUserName") {..}

Accessibility

keychain.accessibility = .AccessibleAfterFirstUnlock

Sharing Keychain items

keychain.accessGroup = "AKEY.shared"

NSCoder

NSCoder is partially supported (dictionary is not available)

When you implementing NSCoding you can do

init?(coder decoder: NSCoder) {
  self.init()
  self.intVar = decoder["intVarKey"] as? Int ?? 0
  // or self.intVar = decoder.integer(forKey: "intVar")
  self.stringVar = decoder["stringVarKey"] as? String ?? ""
}

func encodeWithCoder(coder: NSCoder) {
  coder["intVarKey"] = self.intVar
  coder["stringVarKey"] = self.stringVar
}

Custom implementations

Preferences

Create a custom object that conform to PreferencesType is very easy.

extension MyCustomPreferences: PreferencesType {
    func object(forKey: String) -> Any? {
        // return an object according to key
    }
    func dictionary() -> [String : Any] {
        // return a full dictionary of key value
    }
}

Only two functions are mandatory, others are automatically mapped but can be overrided for performance or readability.

  • In the same way you can implement MutablePreferencesType with set and removeObject(forKey: methods.
  • If you structure give a list of keys instead of a full dictionary, you can instead conform to PreferencesAdapter and implement func keys() -> [String].
  • You have a collection of object with each object could define a key and a value take a look at CollectionPreferencesAdapter or see NSHTTPCookieStorage implementation.

Accessing using custom key

Instead of using string or string constants, you can use an enum to define a list of keys

First create your enum with String raw value

enum MyEnum: String {
  case MyFirstKey
  case MySecondKey
}

Then add a subscript for your key

extension PreferencesType {
    subscript(key: MyEnum) -> Any? {
        return self[key.rawValue]
    }
}

Finally access your information

if let firstValue = bundle[.MyFirstKey] {..}

You can do the same with MutablePreferencesType

Proxying preferences with prefix

You can defined a subcategory of preferences prefixed with your own string like that

let myAppPrefs = MutableProxyPreferences(preferences: userDefaults, key: "myAppKey.")
// We have :
userDefaults["myAppKey.myKey"] == myAppPrefs["myKey"] // is true

This allow prefixing all your preferences (user defaults) with same key

Composing

Composing allow to aggregate multiples PreferencesType objects into one PreferencesType

let myPreferences = CompositePreferences([fromDico, fromFile, userDefaults])
// With array literal
let myPreferences: CompositePreferences = [fromDico, fromFile, userDefaults]

// Mutable, only first mutable will be affected
let myPreferences: MutableCompositePreferences = [fromDico, fromFile, userDefaults]

You can access or modify this composite preferences like any PreferencesType.

  1. When accessing, first preferences that define a value for a specified key will respond
  2. When modifying, first mutable preferences will be affected by default, but you can set MutableCompositePreferences attribute affectOnlyFirstMutable to false to affect all mutable preferences, allowing you for instance to duplicate preferences in iCloud

The main goal is to define read-only preferences for your app (in code or files) and some mutable preferences (like UserDefaults, NSUbiquitousKeyValueStore). You can then access to one preference value without care about the origin.

Managing preferences instances

If you want to use Prephirences into a framework or want to get a Preferences without adding dependencies between classes, you can register any PreferencesType into Prephirences

as shared instance

Prephirences.sharedInstance = myPreferences

or by providing an Hashable key

Prephirences.register(preferences: myPreferences, forKey: "myKey")
Prephirences.instances()["myKey"] = myPreferences
Prephirences.instances()[NSStringFromClass(self.dynamicType)] = currentClassPreferences

Then you can access it anywhere

if let pref = Prephirences.instance(forKey: "myKey") {..}
if let pref = Prephirences.instances()["myKey"] {..}

Remote preferences

By using remote preferences you can remotely control the behavior of your app.

If you use Alamofire, Alamofire-Prephirences will help you to load preferences from remote JSON or Plist

Encrypt your preferences

You can use framework CryptoPrephirences to encrypt/decrypt your preferences using cipher from CryptoSwift

Setup

Using Cocoapods

CocoaPods is a centralized dependency manager for Objective-C and Swift. Go here to learn more.

  1. Add the project to your Podfile.

    use_frameworks!
    
    pod 'Prephirences'
  2. Run pod install and open the .xcworkspace file to launch Xcode.

For core data

Add pod 'Prephirences/CoreData'

For RawRepresentable key

Add pod 'Prephirences/RawRepresentableKey'

For PropertyListKeys

Add pod 'Prephirences/Keys'

Using Carthage

Carthage is a decentralized dependency manager for Objective-C and Swift.

  1. Add the project to your Cartfile.

    github "phimage/Prephirences"
    
  2. Run carthage update and follow the additional steps in order to add Prephirences to your project.

Using xcode project

  1. Drag Prephirences.xcodeproj to your project/workspace or open it to compile it
  2. Add the Prephirences framework to your project

Logo

By kodlian

More Repositories

1

Erik

Erik is an headless browser based on WebKit. An headless browser allow to run functional tests, to access and manipulate webpages using javascript.
Swift
564
star
2

CallbackURLKit

Implementation of x-callback-url (Inter app communication) in swift
Swift
323
star
3

CustomSegue

Custom segue for OSX Storyboards with slide and cross fade effects (NSViewControllerTransitionOptions)
Swift
124
star
4

morphi

Shapes for SwiftUI ♡☾
Swift
71
star
5

Arithmosophi

A set of protocols for Arithmetic, Statistics and Logical operations
Swift
66
star
6

ApplicationGroupKit

Share informations betweens your applications and your extensions using group identifier
Swift
38
star
7

MacModelDump

Dump apple website to get mac model identifier and image url
Swift
26
star
8

Alamofire-Prephirences

Remote preference and configuration for your application
Swift
22
star
9

XcodeProjKit

Parse project file and write it to open step format.
Swift
16
star
10

CryptoPrephirences

Add some encryption to your sensitive preferences
Swift
11
star
11

Phiole

Allow to write or read from standards stream or files for script or CLI application
Swift
8
star
12

SLF4Swift

Simple Logging Facade for Swift serves as a simple facade for logging frameworks allowing the end user to plug in the desired logging framework at deployment time
Swift
8
star
13

ValueTransformerKit

ValueTransformer toolkit for swift
Swift
8
star
14

AlertController

An NSViewController to display an alert message to the user. This class replaces the NSAlert class.
Swift
8
star
15

Notarize

Command line utility to notarize apple application
Swift
7
star
16

MomXML

Create or parse CoreData managed object model XMLs
Swift
6
star
17

RandomDistributionKit

RandomKit extension to produce random numbers using different distribution
Swift
5
star
18

PhiLipsStack

(do not use) PhiLipsStack aims to create a CoreData stack from model to context and provide some functions on your managed object which use by default the default stack context but not only
Swift
5
star
19

plistconvert

Convert plist from different format: xml, json, binary, openStep
Shell
4
star
20

Alamofire-YamlSwift

Add Yaml response serializer to Alamofire
Swift
4
star
21

action-swift-cli-build

Build swift cli tool on macOS or ubuntu
3
star
22

FileZipKit

FileKit and ZipFoundation utility methods
Swift
3
star
23

NotarizeProcess

Utility object to launch `xcrun altool` to get notarization information
Swift
3
star
24

punic

Add third party project sources into your workspace instead of Carthage binary
Swift
3
star
25

swift-cli-template

Shell
2
star
26

Appify

Generate simple macOS `.app` from Swift Package `.executable`.
Swift
2
star
27

sebulba

Swift command line to remove de-integrate reference to cocoa pods
Swift
2
star
28

tanit

Command line application to manage Carthage binaries
Swift
1
star
29

NotarizationInfo

Decode info from apple notarization process
Swift
1
star
30

NotarizationAuditLog

Decode JSON audit log from notarization process
Swift
1
star
31

phimage

1
star
32

xprojup

Swift
1
star
33

cd2sql

Convert CoreData model into SQL
Swift
1
star