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 :
// 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 RawRepresentableenum.
enum MyKey: PreferenceKey/*String*/ {
case Key1, Key2, ...
}
if let myValue = fromDicoLiteral.object(forKey: MyKey.Key1) {..}
var myValue = fromDicoLiteral.bool(forKey: MyKey.Key2)
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
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
}
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.
When accessing, first preferences that define a value for a specified key will respond
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
Prephirences - Preϕrences
Preferences is not only
UserDefaults
, it could be also :Bundle
NSUbiquitousKeyValueStore
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
Accessing
You can access with all methods defined in PreferencesType protocol
If you want to access using
RawRepresentable
enum
.Modifying
Modifiable preferences implement the protocol MutablePreferencesTypes
The simplest implementation is MutableDictionaryPreferences
You can append dictionary or other
PreferencesType
using operatorsYou can also remove one preference
Apply operators to one preference
You can extract a
MutablePreference
from anyMutablePreferencesTypes
and apply operators according to its value typeYou can also use some methods to change value
or transform the value type using closures
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
If you extract one preference, use
transformation
property to setup the transformationor you can use some utility functions to specify a default value when the stored value match a condition
Archiving
Archiving is particularly useful with
NSUserDefaults
becauseNSUserDefaults
can’t store all type of objects. The following functions could help by transforming the value into an other typeYou can archive into
Data
using this two methodsand unarchive using
If you extract one preference, use
transformation
property to setup archive modeNSValueTransformer
You can also apply for all objects type an
NSValueTransformer
, to transform into JSON for instanceallowsReverseTransformation
must returntrue
Store RawRepresentable objects
For
RawRepresentable
objects likeenum
you can use the computed attributepreferenceTransformation
astransformation
Some implementations
UserDefaults
UserDefaults
implementPreferencesType
and can be acceded with same methodsNSUserDefaults implement also
MutablePreferencesType
and can be modified with same methodsBundle
All
Bundle
implementPreferencesType
, 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
NSUbiquitousKeyValueStore
To store in iCloud,
NSUbiquitousKeyValueStore
implement alsoPreferencesType
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
orMutableKVCPreferences
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.You can then use all functions from
PreferencesType
Core Data
You can wrap on
NSManageObject
inManageObjectPreferences
orMutableManageObjectPreferences
Plist
There is many way to play with plist files
Plist
(with the usefulwrite
method)DictionaryPreferences
orMutableDictionaryPreferences
with plist fileset(dictionary:
on any mutable preferencesKeychain
To store into keychain, use an instance of
KeychainPreferences
then store
String
orData
Accessibility
Sharing Keychain items
NSCoder
NSCoder
is partially supported (dictionary
is not available)When you implementing NSCoding you can do
Custom implementations
Preferences
Create a custom object that conform to
PreferencesType
is very easy.Only two functions are mandatory, others are automatically mapped but can be overrided for performance or readability.
MutablePreferencesType
withset
andremoveObject(forKey:
methods.PreferencesAdapter
and implementfunc keys() -> [String]
.CollectionPreferencesAdapter
or seeNSHTTPCookieStorage
implementation.Accessing using custom key
Instead of using
string
orstring
constants, you can use anenum
to define a list of keysFirst create your
enum
withString
raw valueThen add a subscript for your key
Finally access your information
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
This allow prefixing all your preferences (user defaults) with same key
Composing
Composing allow to aggregate multiples PreferencesType objects into one PreferencesType
You can access or modify this composite preferences like any
PreferencesType
.MutableCompositePreferences
attributeaffectOnlyFirstMutable
tofalse
to affect all mutable preferences, allowing you for instance to duplicate preferences in iCloudThe 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 anyPreferencesType
intoPrephirences
as shared instance
or by providing an
Hashable
keyThen you can access it anywhere
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.
Add the project to your Podfile.
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.
Add the project to your Cartfile.
Run
carthage update
and follow the additional steps in order to add Prephirences to your project.Using xcode project
Logo
By kodlian