{"id":13497645,"url":"https://github.com/symentis/Palau","last_synced_at":"2025-03-28T22:31:49.060Z","repository":{"id":62450502,"uuid":"57106121","full_name":"symentis/Palau","owner":"symentis","description":"NSUserDefaults with Wings!","archived":false,"fork":false,"pushed_at":"2017-09-26T15:00:58.000Z","size":280,"stargazers_count":383,"open_issues_count":2,"forks_count":19,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-22T13:55:01.756Z","etag":null,"topics":["nsuserdefaults","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/symentis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-04-26T07:09:42.000Z","updated_at":"2025-01-17T15:03:31.000Z","dependencies_parsed_at":"2022-11-02T01:01:28.698Z","dependency_job_id":null,"html_url":"https://github.com/symentis/Palau","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symentis%2FPalau","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symentis%2FPalau/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symentis%2FPalau/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symentis%2FPalau/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/symentis","download_url":"https://codeload.github.com/symentis/Palau/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246110258,"owners_count":20725022,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["nsuserdefaults","swift"],"created_at":"2024-07-31T20:00:35.025Z","updated_at":"2025-03-28T22:31:48.648Z","avatar_url":"https://github.com/symentis.png","language":"Swift","funding_links":[],"categories":["Database","Libs","Swift"],"sub_categories":["Data Management"],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/symentis/Palau/master/Resources/palau-logo.png\"\u003e\n\u003c/p\u003e\n\n[![Build Status](https://travis-ci.org/symentis/Palau.svg?branch=master)](https://travis-ci.org/symentis/Palau)\n[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Palau.svg)](https://img.shields.io/cocoapods/v/Palau)\n[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![Platform](https://img.shields.io/cocoapods/p/Palau.svg?style=flat)](http://cocoadocs.org/docsets/Palau)\n[![swiftyness](https://img.shields.io/badge/pure-swift-ff3f26.svg?style=flat)](https://swift.org/)\n[![@elmkretzer](https://img.shields.io/badge/twitter-@elmkretzer-blue.svg?style=flat)](http://twitter.com/elmkretzer)\n[![@madhavajay](https://img.shields.io/badge/twitter-@madhavajay-blue.svg?style=flat)](http://twitter.com/madhavajay)\n\n#Palau: NSUserDefaults with Wings!\n\n-------\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e |\n    \u003ca href=\"#already-included-types\"\u003eIncluded Types\u003c/a\u003e |\n    \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e |\n    \u003ca href=\"#custom-rules\"\u003eValidators and Defaults\u003c/a\u003e |\n    \u003ca href=\"#custom-types\"\u003eCustom Types\u003c/a\u003e |\n    \u003ca href=\"#didset-callback\"\u003eDidSet Callback\u003c/a\u003e |\n\u003c/p\u003e\n-------\n\n## Features\n\n- [x] Easily store your Custom Types in NSUserDefaults\n- [x] Most Standard Types Out-of-the-box\n- [x] Per-property based Chainable Rules\n- [x] Supports NSCoding and RawRepresentable\n- [x] 300% Type Safe :P\n- [x] 100% Unit Test Coverage\n- [x] Swift 3 features coming!\n\n## Already Included Types\n### Swift\n- [x] Bool\n- [x] Int\n- [x] UInt\n- [x] Float\n- [x] Double\n- [x] String\n- [x] Array\n- [x] Dictionary\n\n### Foundation\n- [x] NSNumber\n- [x] NSString\n- [x] NSArray\n- [x] NSDictionary\n- [x] NSDate\n- [x] NSData\n- [x] UIColor\n\n## Requirements\n- Swift 2.2\n- iOS 8.0+ / tvOS 9.0+ / watchOS 2.0+\n- Xcode 7.3+\n\n## Installation\n\n### Carthage\n\nTo integrate Palau into your project using Carthage, add to your `Cartfile`:\n\n```ogdl\ngithub \"symentis/Palau\" ~\u003e 1.0\n```\n\nRun `carthage update` to build the framework and drag the built `Palau.framework` into your Xcode project.\nSee more instructions on the [Carthage page](https://github.com/Carthage/Carthage).\n\n### CocoaPods\n\nTo integrate Palau into your project using CocoaPods, add to your `Podfile`:\n\n```ruby\nuse_frameworks!\n\npod 'Palau', '~\u003e 1.0'\n```\n\nRun `pod install` to install the framework into your Xcode workspace.\n\n## Usage\nImport Palau\n\n```swift\nimport Palau\n```\n\nOnce you import the framework you can setup PalauDefaults like:\n\n```swift\n/// Your defaults are defined as extension of PalauDefaults\n///\n/// - The generic type of your PalauDefaultsEntry must conform \n///   to the protocol PalauDefaultable.\n/// - We provide support for the most common types. \n/// - `value` is a helper function defined in PalauDefaults\n/// - The String \"backingName\" is the key used in NSUserDefaults\n/// - The empty `set` is used to please the compiler\nextension PalauDefaults {\n  /// a NSUserDefaults Entry of Type String with the key \"backingName\"\n  public static var name: PalauDefaultsEntry\u003cString\u003e {\n    get { return value(\"backingName\") }\n    set { }\n  }\n}\n```\n\n### Set\nEvery value of a `PalauDefaultsEntry` will always be optional. \nIf you want to set a value you call:\n\n```swift\nPalauDefaults.name.value = \"I am a great String value!\"\n```\n\n### Get\nGetting your value back is as easy as:\n```swift\n/// name is an Optional\u003cString\u003e\nlet name = PalauDefaults.name.value\n```\n\n### Delete\nYou can delete a property by setting it to `nil`:\n```swift\nPalauDefaults.name.value = nil\n```\nOr\n```swift\n/// skip custom rules and delete\nPalauDefaults.name.clear()\n```\n\n## Custom Rules\n\n### Providing a Default Value\nIf you want to provide a default, when there is no value set,\nyou can write a custom rule. This allows fine granular control on\nyour values.\n\nWe include two rule types by default: `whenNil` and `ensure`\n\n```swift\nimport Palau\nextension PalauDefaults {\n  public static var color: PalauDefaultsEntry\u003cUIColor\u003e {\n    /// whenNil provides a value that will be returned\n    /// when the related NSUserDefaults value is nil \n    /// (e.g. the 1st time, or after clear)\n    get { return value(\"color\").whenNil(use: UIColor.redColor())  }\n    set { }\n  }\n}\n\n/// is UIColor.redColor() \nlet color: UIColor? = PalauDefaults.color.value\n```\n\n### Providing a Validator\n\nYou can also build up arbitrary rules for your value like:\n\n```swift\n/// Custom Validator Closure\nlet lessThan10: Int? -\u003e Bool = {\n  return $0.map { $0 \u003c 10 } ?? false\n}\n\n/// the PalauDefaultsEntry with the key \"intValueMin10\" has 2 rules\n/// - 1. when the value is nil - we will get or set 10\n/// - 2. when the value is less than 10 (see lessThan10 closure) - we will also get or set 10\n/// - Add as many chainable rules as you like\npublic static var intValueMin10: PalauDefaultsEntry\u003cInt\u003e {\n  get { return value(\"intValue\")\n    .whenNil(use: 10)\n    .ensure(when: lessThan10, use: 10) }\n  set { }\n}\n\n/// try setting the property to 8\nPalauDefaults.intValueMin10.value = 8\n/// property ensured to be \u003e= 10\nassert(PalauDefaults.intValueMin10.value == 10)\n/// try setting the property to 11\nPalauDefaults.intValueMin10.value = 11\n/// property changed to 11\nassert(PalauDefaults.intValueMin10.value == 11)\n```\n\n## Custom Types\n\nIn Swift 2.2 Classes and Protocols can be used to constrain the ValueType.\nFor example this is how Palau adds support for `RawRepresentable` via an Extension:\n\n```swift\n/// Extension for RawRepresentable types aka enums\nextension PalauDefaultable where ValueType: RawRepresentable {\n\n  public static func get(key: String, from defaults: NSUD) -\u003e ValueType? {\n    guard let val = defaults.objectForKey(key) as? ValueType.RawValue else { return nil }\n    return ValueType(rawValue: val)\n  }\n\n  public static func set(value: ValueType?, forKey key: String, in defaults: NSUD) -\u003e Void {\n    guard let value = value?.rawValue as? AnyObject else { return defaults.removeObjectForKey(key) }\n    defaults.setObject(value, forKey: key)\n  }\n}\n```\n\nGenerally for Types which conform to `NSCoding` you can usually just provide an\nextension like so:\n\n```swift\n/// Make UIColor PalauDefaultable\nextension UIColor: PalauDefaultable {\n  public typealias ValueType = UIColor\n}\n```\n\n## Look Mum, even Structs!\n\nFor custom types you can provide an extension on your type for `PalauDefaultable`, \nto implement a `get` and a `get` function.\n\n```swift\n// example Struct called Structy for demonstrating we can save a Struct with Palau\npublic struct Structy {\n  let tuple: (String, String)\n}\n\n// our Structy PalauDefaultable extension allowing the mapping between PalauDefaults and the Type\n// here we just map the two values to two keys named \"1\" and \"2\"\nextension Structy: PalauDefaultable {\n  public static func get(key: String, from defaults: NSUD) -\u003e Structy? {\n    guard let d = defaults.objectForKey(key) as? [String: AnyObject] ,\n      let t1 = d[\"1\"] as? String,\n      let t2 = d[\"2\"] as? String else { return nil }\n    return Structy(tuple: (t1, t2))\n  }\n\n  public static func set(value: Structy?, forKey key: String, in defaults: NSUD) -\u003e Void {\n    guard let value = value else { return defaults.setObject(nil, forKey: key) }\n    defaults.setObject([\"1\": value.tuple.0, \"2\": value.tuple.1], forKey: key)\n  }\n}\n\n// now create a property on PalauDefaults\nextension PalauDefaults {\n  public static var structWithTuple: PalauDefaultsEntry\u003cStructy\u003e {\n    get { return value(\"structy\") }\n    set { }\n  }\n}\n```\n\n## DidSet Callback\n\nYou can easily register a `didSet` callback, which gets fired when the value has changed.\n\n```swift\nextension PalauDefaults {\n  public static var strings: PalauDefaultsEntry\u003c[String]\u003e {\n    get { return value(\"strings\").didSet({ print(\"changed to:\", $0, \"from:\", $1) }) }\n    set { }\n  }\n}\n```\n\n## Limitations for Swift 2.2\n\nWe are waiting for more Swift 3 generics features like extensions on Generic types.... yay!\n\nThen we get even more type saftey on arrays and dictionaries.\nPlus we might be able to make generic types conform to `PalauDefaultable`.\n\n## FAQ\n\n### What's the origin of the name Palau?\n\nPalau is named after the [Palau swiftlet](https://en.wikipedia.org/wiki/Palau_swiftlet), a species of swift, endemic to the island of Palau.\n\nBtw - if you really don\\`t like the name, you can use a `typealias`\n\n```swift\ntypealias Defaults = PalauDefaults\ntypealias Defaultable = PalauDefaultable\n/// for Swift 3 even:\n/// typealias DefaultsEntry\u003cT\u003e = PalauDefaultsEntry\u003cT\u003e\n```\n\n---\n\n## Credits\nPalau is owned and maintained by [Symentis GmbH](http://symentis.com).\n\nDeveloped by: Elmar Kretzer \u0026amp; Madhava Jay\n\nFollow for more Swift Goodness:\n[![Twitter](https://img.shields.io/badge/twitter-@elmkretzer-blue.svg?style=flat)](http://twitter.com/elmkretzer)\n[![Twitter](https://img.shields.io/badge/twitter-@madhavajay-blue.svg?style=flat)](http://twitter.com/madhavajay)\n\n##Logo\n\nAwesome Logo by: [4th motion](http://4thmotion.com)\n\n## String Test Fixtures\n[Markus Kuhn](http://www.cl.cam.ac.uk/~mgk25/)\n\n## License\nPalau is released under the Apache 2.0 license. See LICENSE for details.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsymentis%2FPalau","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsymentis%2FPalau","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsymentis%2FPalau/lists"}