├── .github └── FUNDING.yml ├── .gitignore ├── .swiftlint.yml ├── .travis.yml ├── CHANGELOG.md ├── Cartfile.private ├── Cartfile.resolved ├── Cartfile_4.2.private ├── Cartfile_4.2.resolved ├── Dangerfile.swift ├── FrameworkSpec ├── LICENSE ├── MigrationGuides ├── migration_3_to_4.md ├── migration_4_alpha_1_to_4_alpha_2.md └── migration_4_to_5.md ├── Package.resolved ├── Package.swift ├── Package@swift-4.2.resolved ├── Package@swift-4.2.swift ├── README.md ├── Sources ├── BuiltIns.swift ├── Defaults+Observing.swift ├── Defaults+StringToBool.swift ├── Defaults+Subscripts.swift ├── Defaults.swift ├── DefaultsAdapter.swift ├── DefaultsBridges.swift ├── DefaultsKey.swift ├── DefaultsKeys.swift ├── DefaultsObserver.swift ├── DefaultsSerializable.swift ├── Info.plist ├── OptionalType.swift ├── PropertyWrappers.swift └── SwiftyUserDefaults.h ├── SwiftyUserDefaults.podspec ├── SwiftyUserDefaults.xcodeproj ├── Nimble_Info.plist ├── QuickSpecBase_Info.plist ├── Quick_Info.plist ├── SwiftyUserDefaultsTests_Info.plist ├── SwiftyUserDefaults_Info.plist ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ ├── IDETemplateMacros.plist │ └── xcschemes │ ├── SwiftyUserDefaults-Package.xcscheme │ ├── SwiftyUserDefaults.xcscheme │ └── SwiftyUserDefaultsTests.xcscheme ├── Tests ├── Info.plist ├── LinuxMain.swift └── SwiftyUserDefaultsTests │ ├── .swiftlint.yml │ ├── Built-ins │ ├── Defaults+Bool.swift │ ├── Defaults+Data.swift │ ├── Defaults+Date.swift │ ├── Defaults+Dictionary.swift │ ├── Defaults+Double.swift │ ├── Defaults+Int.swift │ ├── Defaults+String.swift │ └── Defaults+URL.swift │ ├── External types │ ├── Defaults+BestFroggiesEnum.swift │ ├── Defaults+Color.swift │ ├── Defaults+FrogCodable.swift │ ├── Defaults+FrogCustomSerializable.swift │ └── Defaults+FrogSerializable.swift │ └── TestHelpers │ ├── DefaultsSerializableSpec.swift │ ├── TestHelper.swift │ └── UserDefaults+PropertyList.swift └── scripts ├── carthage_integration_step.sh ├── cocoapods_integration_step.sh ├── copy-carthage-frameworks.sh └── spm_integration_step.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [sunshinejr] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | *~.nib 4 | 5 | /build/ 6 | .project 7 | *.mode1 8 | *.mode1v3 9 | *.mode2v3 10 | *.perspective 11 | *.perspectivev3 12 | *.pbxuser 13 | xcuserdata/ 14 | *.xcuserstate 15 | *.xccheckout 16 | /Build/ 17 | /DerivedData/ 18 | 19 | /Pods/ 20 | /.build/ 21 | /www 22 | 23 | /Carthage 24 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | excluded: 2 | - .build/ 3 | - Carthage/ 4 | - Tests/ 5 | disabled_rules: 6 | - identifier_name 7 | - line_length 8 | - trailing_comma 9 | - type_name -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | matrix: 3 | include: 4 | - os: osx 5 | osx_image: xcode11.3 6 | name: Danger 7 | env: 8 | - CACHE_NAME=SWIFT5_1 9 | - SWIFT_VERSION=5.0 10 | - SWIFT_TOOLS_VERSION=5.1 11 | - XCODEGEN_VERSION=2.5.0 12 | - IOS_SIMULATOR='name=iPhone 11,OS=13.2' 13 | - IOS_SDK=iphonesimulator13.2 14 | - TVOS_SIMULATOR='name=Apple TV 4K (at 1080p)' 15 | - TVOS_SDK=appletvsimulator13.2 16 | - WATCHOS_SIMULATOR='name=Apple Watch Series 4 - 44mm' 17 | - WATCHOS_SDK=watchsimulator6.1 18 | before_install: 19 | - brew install danger/tap/danger-swift 20 | script: DEBUG="*" danger-swift ci 21 | - os: osx 22 | osx_image: xcode11.3 23 | name: Swift 5.1 compatibility 24 | env: 25 | - CACHE_NAME=SWIFT5_1 26 | - SWIFT_VERSION=5.0 27 | - SWIFT_TOOLS_VERSION=5.1 28 | - XCODEGEN_VERSION=2.5.0 29 | - IOS_SIMULATOR='name=iPhone 11,OS=13.0' 30 | - IOS_SDK=iphonesimulator13.0 31 | - TVOS_SIMULATOR='name=Apple TV 4K (at 1080p)' 32 | - TVOS_SDK=appletvsimulator13.0 33 | - WATCHOS_SIMULATOR='name=Apple Watch Series 4 - 44mm' 34 | - WATCHOS_SDK=watchsimulator6.0 35 | - os: osx 36 | osx_image: xcode10.2 37 | name: Swift 5.0 compatibility 38 | env: 39 | - CACHE_NAME=SWIFT5_0 40 | - SWIFT_VERSION=5.0 41 | - SWIFT_TOOLS_VERSION=5.0 42 | - XCODEGEN_VERSION=2.5.0 43 | - IOS_SIMULATOR='name=iPhone 6s,OS=12.2' 44 | - IOS_SDK=iphonesimulator12.2 45 | - TVOS_SIMULATOR='name=Apple TV 4K (at 1080p)' 46 | - TVOS_SDK=appletvsimulator12.2 47 | - WATCHOS_SIMULATOR='name=Apple Watch Series 4 - 44mm' 48 | - WATCHOS_SDK=watchsimulator5.2 49 | # - os: linux 50 | # sudo: required 51 | # dist: trusty 52 | # name: Swift 5.0 compatibility 53 | # env: 54 | # - CACHE_NAME=LINUX_SWIFT5_0 55 | # - SWIFT_VERSION=5.0-dev 56 | # before_install: 57 | # - eval "$(curl -sL https://swiftenv.fuller.li/install.sh)" 58 | # script: 59 | # - swift test 60 | - os: osx 61 | osx_image: xcode10.1 62 | name: Swift 4.2 compatibility 63 | env: 64 | - CACHE_NAME=SWIFT4_2 65 | - SWIFT_VERSION=4.2 66 | - SWIFT_TOOLS_VERSION=4.2 67 | - XCODEGEN_VERSION=2.3.0 68 | - IOS_SIMULATOR='name=iPhone 6s,OS=12.1' 69 | - IOS_SDK=iphonesimulator12.1 70 | - TVOS_SIMULATOR='name=Apple TV 4K (at 1080p)' 71 | - TVOS_SDK=appletvsimulator12.1 72 | - WATCHOS_SIMULATOR='name=Apple Watch - 42mm' 73 | - WATCHOS_SDK=watchsimulator5.1 74 | # - os: linux 75 | # sudo: required 76 | # dist: trusty 77 | # name: Swift 4.2 compatibility 78 | # env: 79 | # - CACHE_NAME=LINUX_SWIFT4_2 80 | # - SWIFT_VERSION=4.2 81 | # before_install: 82 | # - eval "$(curl -sL https://swiftenv.fuller.li/install.sh)" 83 | # script: 84 | # - swift test 85 | - os: osx 86 | osx_image: xcode10.1 87 | name: Swift 4.1 compatibility 88 | env: 89 | - CACHE_NAME=SWIFT4_1 90 | - SWIFT_VERSION=4.0 91 | - SWIFT_TOOLS_VERSION=4.0 92 | - XCODEGEN_VERSION=2.3.0 93 | - IOS_SIMULATOR='name=iPhone 6s,OS=12.1' 94 | - IOS_SDK=iphonesimulator12.1 95 | - TVOS_SIMULATOR='name=Apple TV 4K (at 1080p)' 96 | - TVOS_SDK=appletvsimulator12.1 97 | - WATCHOS_SIMULATOR='name=Apple Watch - 42mm' 98 | - WATCHOS_SDK=watchsimulator5.1 99 | 100 | notifications: 101 | email: false 102 | 103 | cache: 104 | edge: true 105 | directories: 106 | - Carthage 107 | - .build 108 | 109 | env: 110 | global: 111 | - LC_CTYPE=en_US.UTF-8 112 | - LANG=en_US.UTF-8 113 | - PROJECT=SwiftyUserDefaults.xcodeproj 114 | 115 | before_install: 116 | - gem install cocoapods -v 1.6.0 117 | - gem install xcpretty 118 | - git clone https://github.com/yonaskolb/XcodeGen.git && cd XcodeGen && git checkout $XCODEGEN_VERSION && make && cd ../ 119 | 120 | script: 121 | - set -o pipefail 122 | # - if [[ "$SWIFT_VERSION" != "5.0" ]]; then mv Cartfile_4.2.private Cartfile.private; fi 123 | # - if [[ "$SWIFT_VERSION" != "5.0" ]]; then mv Cartfile_4.2.resolved Cartfile.resolved; fi 124 | # - carthage update --no-use-binaries --cache-builds --verbose 125 | # - xcodebuild -version 126 | # - xcodebuild -showsdks 127 | 128 | - xcodebuild -project "$PROJECT" -scheme 'SwiftyUserDefaults' -sdk $IOS_SDK -destination "$IOS_SIMULATOR" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO SWIFT_VERSION=$SWIFT_VERSION build | xcpretty 129 | - xcodebuild -project "$PROJECT" -scheme 'SwiftyUserDefaults' -sdk $TVOS_SDK -destination "$TVOS_SIMULATOR" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO SWIFT_VERSION=$SWIFT_VERSION build | xcpretty 130 | - xcodebuild -project "$PROJECT" -scheme 'SwiftyUserDefaults' -sdk $WATCHOS_SDK -destination "$WATCHOS_SIMULATOR" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO SWIFT_VERSION=$SWIFT_VERSION build | xcpretty 131 | - xcodebuild -project "$PROJECT" -scheme 'SwiftyUserDefaults' ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO SWIFT_VERSION=$SWIFT_VERSION build | xcpretty 132 | 133 | # - xcodebuild -project "$PROJECT" -scheme 'SwiftyUserDefaults' -sdk $IOS_SDK -destination "$IOS_SIMULATOR" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO SWIFT_VERSION=$SWIFT_VERSION test | xcpretty 134 | # - xcodebuild -project "$PROJECT" -scheme 'SwiftyUserDefaults' -sdk $TVOS_SDK -destination "$TVOS_SIMULATOR" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO SWIFT_VERSION=$SWIFT_VERSION test | xcpretty 135 | # - xcodebuild -project "$PROJECT" -scheme 'SwiftyUserDefaults' ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO test | xcpretty 136 | 137 | - swift build 138 | - TEST=1 swift test 139 | - pod lib lint 140 | 141 | - scripts/cocoapods_integration_step.sh 142 | - scripts/carthage_integration_step.sh 143 | - scripts/spm_integration_step.sh $SWIFT_VERSION 144 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### Next 2 | 3 | ### 5.3.0 (2021-02-24) 4 | * Renamed `OptionalType.empty` to `OptionalType.__swifty_empty`. Also removed the `OptionalType.wrapped` since it wasn't used in the framework anymore. Please note that this still shouldn't be something you rely on tho, we're gonna explore ways to remove the public `OptionalType` in a future releases. [@sunshinejr](https://github.com/sunshinejr) 5 | 6 | ### 5.2.0 (2021-02-23) 7 | * DefaultsAdapter's subscript setters are now `nonmutating`. This shouldn't change much on the client side, but it does fix the issue with simultaneous access (#241, #247). [@sunshinejr](https://github.com/sunshinejr) 8 | * Added `DefaultsProviding` protocol that `DefaultsAdapter` implements. It should help with DI and creating test adapters (#268). [@sunshinejr](https://github.com/sunshinejr) 9 | 10 | ### 5.1.0 (2020-12-14) 11 | * Increase min deployment target to iOS 9.0 due to Xcode 12 requirements [@laevandus](https://github.com/laevandus) 12 | 13 | ### 5.0.0 (2019-12-31) 14 | * 🚀 15 | 16 | ### 5.0.0-beta.5 (2019-10-05) 17 | 18 | * Removed Combine extensions for now. Due to problems with weak-linking the framework, it's too difficult to support it with ease using all package managers and also without breaking backwards-compatibility. Probably gonna introduce it once we only support Xcode 11. [@sunshinejr](https://github.com/sunshinejr) 19 | 20 | ### 5.0.0-beta.4 (2019-09-27) 21 | 22 | * Fixed an issue with Xcode freezing, never-finishing indexing/building the project when we used `Defaults[\.keyPath]` in conditional statement. Unfortunately we had to add `key` label to `Defaults[key: DefaultsKey...]` where you wouldn't have to add the label to the subscript before. [@sunshinejr](https://github.com/sunshinejr) 23 | 24 | ### 5.0.0-beta.3 (2019-09-25) 25 | 26 | * Fixed an issue with SPM integration - it no longer fetches testing libraries & doesn't create runtime crashes or Xcode Preview crashes anymore. [@sunshinejr](https://github.com/sunshinejr) 27 | * Fixed an issue where Carthage users using Xcode 11 couldn't install SwiftyUserDefaults 5. We added weak-linking for the xcodeproj so you might need additional steps for Xcode 10 + Carthage + SwiftyUserDefaults 5.* users. [@sunshinejr](https://github.com/sunshinejr) 28 | 29 | ### 5.0.0-beta.2 (2019-09-09) 30 | 31 | * Added extensions for `Combine`! If you can `import Combine` and use it, check the `publisher(for:)` method on `DefaultsAdapter`. [@sunshinejr](https://github.com/sunshinejr) 32 | 33 | ### 5.0.0-beta.1 (2019-09-05) 34 | 35 | * Introduced `DefaultsAdapter` thats the main object for user defaults and the `Defaults` global variable. [@marty-suzuki](https://github.com/marty-suzuki) 36 | * Thanks to `DefaultsAdapter`, if you are using Swift 5.1 you can use dyanmic member lookup! This allows you to use 37 | `Defaults.yourKey` instead of `Defaults[.yourKey]`. In case you are not using Swift 5.1, you would need to transition to `Defaults[\.yourKey]` instead of `Defaults[.yourKey]`. [@marty-suzuki](https://github.com/marty-suzuki) 38 | * There is a new protocol, `DefaultsKeyStore` that `DefaultsKeys` conform to. This key store is then accepted by the `DefaultsAdapter` so you can have multiple key stores for multiple adapters! [@marty-suzuki](https://github.com/marty-suzuki) 39 | * Unfortunately the above means that you need to declare your keys as a computed properties instead of static stored ones.[@marty-suzuki](https://github.com/marty-suzuki) 40 | * `DefaultsBridge` is now a struct, not a class. You need to use composition instead of inheritance to compose them. [@Z-JaDe](https://github.com/Z-JaDe) 41 | * `DefaultsBridge` changed a little bit, there is no `isSerialized` property anymore, if you create your own bridge you need to provide `deserialize()` method as well. [@Z-JaDe](https://github.com/Z-JaDe) 42 | * Added `@SwiftyUserDefault` property wrapper for Swift 5.1 users! It uses key paths and has options to cache/observe your defaults as well. [@sunshinejr](https://github.com/sunshinejr) 43 | * Updated project to recommended settings of Xcode 10.2. [@philippec-ls](https://github.com/philippec-ls) 44 | 45 | ### 4.0.0 (2019-04-26) 46 | 47 | * Updated `DefaultsKey.defaultValue` access level to `public`. [@DivineDominion](https://github.com/DivineDominion) 48 | * Updated accesslevel of all bridges to `open` from `public`. [@fredpi](https://github.com/fredpi ) 49 | 50 | ### 4.0.0-beta.2 (2019-03-09) 51 | 52 | * Regenerated the project as a potential fix to Carthage linking problems in beta 1. [@sunshinejr](https://github.com/sunshinejr) 53 | 54 | ### 4.0.0-beta.1 (2019-02-25) 55 | 56 | * Added support for launch arguments/plist for `Bool`, `Double`, `Int`, `String` values. [@sunshinejr](https://github.com/sunshinejr) 57 | * Added support for KVO! [DivineDominion](https://github.com/DivineDominion), [toshi0383](https://github.com/toshi0383), [@sunshinejr](https://github.com/sunshinejr) 58 | * Brought back dictionary support for `[String: Any]`/`[String: String]` and corresponding array version of it `[[String: Any]]`/`[[String: String]]`. [@sunshinejr](https://github.com/sunshinejr) 59 | 60 | ### 4.0.0-alpha.3 (2019-02-19) 61 | 62 | * Fixed a non-optional vs optional comparison bug ([#176](https://github.com/radex/SwiftyUserDefaults/issues/176)). [@z3bi](https://github.com/z3bi) and [@sunshinejr](https://github.com/sunshinejr) 63 | * Fixed an invalid Info.plist error ([#173](https://github.com/radex/SwiftyUserDefaults/issues/173)). [@sunshinejr](https://github.com/sunshinejr) 64 | 65 | ### 4.0.0-alpha.2 (2019-02-18) 66 | 67 | * Swift 4.2 support. [@sunshinejr](https://github.com/sunshinejr) 68 | * Early Swift 5.0 support! [@sunshinejr](https://github.com/sunshinejr) 69 | * Rewritten core. We use `DefaultsBridges` now to define getters/setters for given type. [@sunshinejr](https://github.com/sunshinejr) 70 | * Fixed a bug where you couldn't extend non-final class like `NSColor`. [@sunshinejr](https://github.com/sunshinejr) 71 | * Removed type-based default values. This means you need to use key-based defaultValue or use an optional `DefaultsKey` from now on. [@sunshinejr](https://github.com/sunshinejr) 72 | * Improved CI infra: Swift 4.1/4.2/5.0 builds with CocoaPods/Carthage/SPM integration scripts. [@sunshinejr](https://github.com/sunshinejr) 73 | 74 | 75 | ### 4.0.0-alpha.1 (2018-05-08) 76 | 77 | * Swift 4.1 support [@sunshinejr](https://github.com/sunshinejr) 78 | * Added `Codable` support! [@sunshinejr](https://github.com/sunshinejr) 79 | * Added generic subscripts support (better `DefaultsKey` init diagnostics and accessing `Defaults[.key]`) [@sunshinejr](https://github.com/sunshinejr) 80 | * Added default values protocols (`DefaultsDefaultValueType`, `DefaultsDefaultArrayValueType`) - this means that you can extend any type with default value so you can create non-optional `DefaultsKey` afterwards! [@sunshinejr](https://github.com/sunshinejr) 81 | * Added default values in `DefaultsKey`, e.g. `DefaultsKey("test", defaultValue: "default value")` [@sunshinejr](https://github.com/sunshinejr) 82 | * Added better support for custom types: using `DefaultsSerializable`, when your type implements `NSCoding`, `RawRepresentable` (enums as well) or `Codable`, you get default implementations for free! [@sunshinejr](https://github.com/sunshinejr) 83 | * Added automatic array support for any type that is available to `SwiftyUserDefaults` (means custom with `DefaultsSerializable` as well!) [@sunshinejr](https://github.com/sunshinejr) 84 | * Added Swift Package Manager support! [@sunshinejr](https://github.com/sunshinejr) 85 | * Added `[URL]` built-in support! [@sunshinejr](https://github.com/sunshinejr) 86 | * A lot of infrastructure changes (CI, project), around 350 tests to make sure all of the changes work properly! [@sunshinejr](https://github.com/sunshinejr) 87 | * Removed legacy strings based API (`Defaults["test"]`), `Dictionary` and `Any` support (sorry, with all the changes in the library we had to, but you _probably_ can bring it back with `DefaultsSerializable` anyways 😅) [@sunshinejr](https://github.com/sunshinejr) 88 | 89 | ### 3.0.1 (2016-11-12) 90 | 91 | * Fix for Swift Package Manager #114 @max-potapov 92 | 93 | ### 3.0.0 (2016-09-14) 94 | 95 | This is the Swift 3 update version. 96 | 97 | It contains no major changes in the library itself, however it does change some APIs because of Swift 3 requirements. 98 | 99 | * Update documentation and README for Swift 3 100 | * Updated for Swift 3 and Xcode 8 compatibility #91 @askari01 101 | * Updated for Swift 3 beta 4 #102 @rinatkhanov 102 | * Updated for Swift 3 beta 6 #106 @ldiqual 103 | 104 | --- 105 | 106 | ### 2.2.1 (2016-08-03) 107 | 108 | * `NSUserDefaults.set()` is now public (useful for adding support for custom types) #85 @goktugyil 109 | * Support for Xcode 8 (Swift 2.3) for Carthage users #100 @KevinVitale 110 | 111 | ### 2.2.0 (2016-04-10) 112 | 113 | * Support for `archive()` and `unarchive()` on `RawRepresentable` types 114 | * Improved documentation 115 | 116 | ### 2.1.3 (2016-03-02) 117 | 118 | * Fix Carthage build 119 | * Suppress deprecation warnings in tests 120 | 121 | ### 2.1.2 (2016-03-01) 122 | 123 | * Fixed infinite loop bug 124 | * Added Travis CI integration 125 | * Added Swift Package Manager support 126 | 127 | ### 2.1.1 (2016-02-29) 128 | 129 | * Documentation improvements 130 | 131 | ### 2.1.0 (2016-02-29) 132 | 133 | * Added `removeAll()` 134 | * Added tvOS and watchOS support 135 | * Fixed error when linking SwiftyUserDefaults with app extension targets 136 | * Minor tweaks and fixes 137 | 138 | ### 2.0.0 (2015-09-18) 139 | 140 | * Introducing statically-typed keys 141 | * Define keys using `DefaultsKey` 142 | * Extend magic `DefaultsKeys` class to get access to `Defaults[.foo]` shortcut 143 | * Support for all basic types, both in optional and non-optional forms 144 | * Support for arrays of basic types, such as `[Double]` or `[String]?` 145 | * Support for basic `[String: AnyObject]` dictionaries 146 | * `hasKey()` and `remove()` for static keys 147 | * You can define support for static keys of custom `NSCoder`-compliant types 148 | * Support for `NSURL` in statically-typed keys 149 | * [Carthage] Added OS X support 150 | 151 | **Deprecations** 152 | 153 | * `+=`, `++`, `?=` operators are now deprecated in favor of statically-typed keys 154 | 155 | --- 156 | 157 | ### 1.3.0 (2015-06-29) 158 | 159 | * Added non-optional `Proxy` getters 160 | * string -> stringValue, etc. 161 | * non-optional support for all except NSObject and NSDate getters 162 | * Fixed Carthage (Set iOS Deployment target to 8.0) 163 | * Converted tests to XCTest 164 | 165 | ### 1.2.0 (2015-06-15) 166 | 167 | * Carthage support 168 | 169 | ### 1.1.0 (2015-04-13) 170 | 171 | * Swift 1.2 compatibility 172 | * Fixed podspec 173 | 174 | ### 1.0.0 (2015-01-26) 175 | 176 | * Initial release 177 | * `Proxy` getters: 178 | * String, Int, Double, Bool 179 | * NSArray, NSDictionary 180 | * NSDate, NSData 181 | * NSNumber, NSObject 182 | * subscript setter 183 | * `hasKey()` 184 | * `remove()` 185 | * `?=`, `+=`, `++` operators on `Proxy` 186 | * global `Defaults` shortcut 187 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "Quick/Quick" ~> 2.0.0 2 | github "Quick/Nimble" ~> 8.0.1 -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Quick/Nimble" "v8.0.5" 2 | github "Quick/Quick" "v2.2.0" 3 | -------------------------------------------------------------------------------- /Cartfile_4.2.private: -------------------------------------------------------------------------------- 1 | github "Quick/Quick" ~> 1.1.0 2 | github "Quick/Nimble" ~> 7.0.0 -------------------------------------------------------------------------------- /Cartfile_4.2.resolved: -------------------------------------------------------------------------------- 1 | github "Quick/Nimble" "v7.3.4" 2 | github "Quick/Quick" "v1.3.0" -------------------------------------------------------------------------------- /Dangerfile.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Danger 3 | let danger = Danger() 4 | 5 | let allSourceFiles = danger.git.modifiedFiles + danger.git.createdFiles 6 | 7 | let changelogChanged = allSourceFiles.contains("CHANGELOG.md") 8 | let sourceChanges = allSourceFiles.first(where: { $0.hasPrefix("Sources") }) 9 | let isTrivial = danger.github.pullRequest.title.contains("#trivial") 10 | 11 | if danger.git.createdFiles.count + danger.git.modifiedFiles.count - danger.git.deletedFiles.count > 10 { 12 | warn("Big PR, try to keep changes smaller if you can") 13 | } 14 | 15 | if !isTrivial && !changelogChanged && sourceChanges != nil { 16 | warn(""" 17 | Any changes to library code should be reflected in the Changelog. 18 | """) 19 | } 20 | 21 | if danger.github.pullRequest.title.contains("WIP") { 22 | warn("PR is classed as Work in Progress") 23 | } 24 | 25 | let onlyPodspec = allSourceFiles.contains("SwiftyUserDefaults.podspec") && !allSourceFiles.contains("Package.swift") 26 | let onlyPackage = !allSourceFiles.contains("SwiftyUserDefaults.podspec") && allSourceFiles.contains("Package.swift") 27 | if onlyPodspec != onlyPackage { 28 | warn("Only one of either the podspec or SPM package was changed. This might be unintentional – double check.") 29 | } 30 | 31 | // Workaround for SwiftLint bug https://github.com/ashfurrow/danger-swiftlint/issues/4 32 | SwiftLint.lint(inline: true, directory: "Sources", configFile: ".swiftlint.yml") 33 | SwiftLint.lint(inline: true, directory: "Tests", configFile: "Tests/SwiftyUserDefaultsTests/.swiftlint.yml") 34 | -------------------------------------------------------------------------------- /FrameworkSpec: -------------------------------------------------------------------------------- 1 | language = swift("4.0") 2 | platforms = [ 3 | macos("10.11"), 4 | ios("9.0"), 5 | tvos("9.0"), 6 | watchos("2.0") 7 | ] 8 | 9 | swifty_user_defaults_tests = new_target do |target| 10 | target.name = "SwiftyUserDefaultsTests" 11 | target.platforms = platforms 12 | target.language = language 13 | target.info_plist = "Tests/Info.plist" 14 | target.bundle_id = "io.radex.SwiftyUserDefaultsTests" 15 | target.include_files = ["Tests/**/*.swift"] 16 | target.exclude_files = [] 17 | target.resource_files = ["Tests/**/*.png"] 18 | target.dependencies = ["Quick", "Nimble"] 19 | target.type = :unit_test_bundle 20 | target.enable_code_coverage = true 21 | end 22 | 23 | swifty_user_defaults = new_target do |target| 24 | target.name = "SwiftyUserDefaults" 25 | target.platforms = platforms 26 | target.language = language 27 | target.info_plist = "Sources/Info.plist" 28 | target.bundle_id = "io.radex.SwiftyUserDefaults" 29 | target.header = "Sources/SwiftyUserDefaults.h" 30 | target.include_files = ["Sources/**/*.swift"] 31 | target.exclude_files = [] 32 | target.is_safe_for_extensions = true 33 | target.test_target = swifty_user_defaults_tests 34 | end 35 | 36 | project.name = "SwiftyUserDefaults" 37 | project.scripts_directory = "scripts" 38 | project.targets = [ 39 | swifty_user_defaults, 40 | swifty_user_defaults_tests 41 | ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MigrationGuides/migration_3_to_4.md: -------------------------------------------------------------------------------- 1 | # Migration guide from 3.x to 4.x 2 | 3 | ### Legacy APIs 4 | 5 | We've removed the support for legacy APIs using `String` values as keys in favor of `DefaultsKey`. 6 | So if you've used `SwiftyUserDefaults` similar to: 7 | 8 | ```swift 9 | let value = Defaults["key"].intValue 10 | ``` 11 | 12 | You need to either migrate to using `DefaultsKey` (and get all benefits of statically typed keys): 13 | 14 | ```swift 15 | let key = DefaultsKey("key") 16 | let value = Defaults[key] 17 | ``` 18 | 19 | or use `UserDefaults` instead. 20 | 21 | If you used `Any` as a type of the `DefaultsKey`, you also need to migrate to use a proper type instead. 22 | 23 | 24 | ### Removed default values for certain types 25 | 26 | You might've used this syntax before: 27 | ```swift 28 | let key = DefaultsKey("test1") // this might be nil when you access it, and so we were using some default values in the past like empty string 29 | ``` 30 | 31 | These defaults were quite confusing for some people and we decided to remove it in version 4 (+ it was really heavy in terms of codebase spaghetti). Don't worry though, as you can still define your own default values in version 4.0: 32 | ```swift 33 | let key = DefaultsKey("test1", defaultValue: "") 34 | ``` 35 | 36 | Or might try to create some custom inits/factories. If you can't migrate in your use-case, please provide details and create an issue so we can help with that! 37 | 38 | ### NSCoding, RawRepresentable and Custom Types 39 | 40 | If you used custom types with SwiftyUserDefaults, fear no more: you still can use them! 41 | Now, you don't need your own `subcript` so remove it and add `DefaultsSerializable` protocol to your type! 42 | 43 | Example. Let's say you had a class `Froggy` that conformed to the `NSCoding` protocol and you had your own subscript: 44 | 45 | ```swift 46 | final class Froggy: NSObject, NSCoding { ... } 47 | 48 | extension UserDefaults { 49 | subscript(key: DefaultsKey) -> NSColor? { 50 | get { return unarchive(key) } 51 | set { archive(key, newValue) } 52 | } 53 | } 54 | ``` 55 | 56 | Replace it with the code below: 57 | 58 | ```swift 59 | final class Froggy: NSObject, NSCoding, DefaultsSerializable { ... } 60 | ``` 61 | 62 | And that's it! You have free custom types if you implement `NSCoding`, `RawRepresentable`(e.g. enums as well) or `Codable`. 63 | 64 | Or if you want to add your own custom type that we don't support yet, no worries! We've got your covered as well. We use `DefaultsBridge`s of many kinds to specify how you get/set values and arrays of values. When you look at `DefaultsSerializable` protocol, it expects two properties in each type: `_defaults` and `_defaultsArray` which are of type `DefaultsBridge`. 65 | 66 | For instance, this is a bridge for single value data storing/retrieving using `NSKeyedArchiver`/`NSKeyedUnarchiver`: 67 | ```swift 68 | public final class DefaultsKeyedArchiverBridge: DefaultsBridge { 69 | 70 | public override func get(key: String, userDefaults: UserDefaults) -> T? { 71 | guard let data = userDefaults.data(forKey: key) else { 72 | return nil 73 | } 74 | return deserialize(data) 75 | } 76 | 77 | public override func save(key: String, value: T?, userDefaults: UserDefaults) { 78 | guard let value = value else { 79 | userDefaults.removeObject(forKey: key) 80 | return 81 | } 82 | 83 | userDefaults.set(NSKeyedArchiver.archivedData(withRootObject: value), forKey: key) 84 | } 85 | 86 | public override func isSerialized() -> Bool { 87 | return true 88 | } 89 | 90 | public override func deserialize(_ object: Any) -> T? { 91 | guard let data = object as? Data else { return nil } 92 | 93 | return NSKeyedUnarchiver.unarchiveObject(with: data) as? T 94 | } 95 | } 96 | ``` 97 | 98 | And for a simple case of storing/retrieving an array values: 99 | ```swift 100 | public final class DefaultsArrayBridge: DefaultsBridge { 101 | public override func save(key: String, value: T?, userDefaults: UserDefaults) { 102 | userDefaults.set(value, forKey: key) 103 | } 104 | 105 | public override func get(key: String, userDefaults: UserDefaults) -> T? { 106 | return userDefaults.array(forKey: key) as? T 107 | } 108 | } 109 | ``` 110 | 111 | Now, if you want to create a custom type and want to use `DefaultsKeyedArchiverBridge`: 112 | ```swift 113 | struct FrogCustomSerializable: DefaultsSerializable { 114 | 115 | static var _defaults: DefaultsBridge { return DefaultsKeyedArchiverBridge() } 116 | static var _defaultsArray: DefaultsBridge<[FrogCustomSerializable]> { return DefaultsKeyedArchiverBridge() } 117 | 118 | let name: String 119 | } 120 | ``` 121 | 122 | You have to remember though, that these built-in bridges are for specific use cases and you probably will end up writing your own bridges: 123 | ```swift 124 | final class DefaultsFrogBridge: DefaultsBridge { 125 | override func get(key: String, userDefaults: UserDefaults) -> FrogCustomSerializable? { 126 | let name = userDefaults.string(forKey: key) 127 | return name.map(FrogCustomSerializable.init) 128 | } 129 | 130 | override func save(key: String, value: FrogCustomSerializable?, userDefaults: UserDefaults) { 131 | userDefaults.set(value?.name, forKey: key) 132 | } 133 | 134 | public override func isSerialized() -> Bool { 135 | return true 136 | } 137 | 138 | public override func deserialize(_ object: Any) -> FrogCustomSerializable? { 139 | guard let name = object as? String else { return nil } 140 | 141 | return FrogCustomSerializable(name: name) 142 | } 143 | } 144 | 145 | final class DefaultsFrogArrayBridge: DefaultsBridge<[FrogCustomSerializable]> { 146 | override func get(key: String, userDefaults: UserDefaults) -> [FrogCustomSerializable]? { 147 | return userDefaults.array(forKey: key)? 148 | .compactMap { $0 as? String } 149 | .map(FrogCustomSerializable.init) 150 | } 151 | 152 | override func save(key: String, value: [FrogCustomSerializable]?, userDefaults: UserDefaults) { 153 | let values = value?.map { $0.name } 154 | userDefaults.set(values, forKey: key) 155 | } 156 | 157 | public override func isSerialized() -> Bool { 158 | return true 159 | } 160 | 161 | public override func deserialize(_ object: Any) -> [FrogCustomSerializable]? { 162 | guard let names = object as? [String] else { return nil } 163 | 164 | return names.map(FrogCustomSerializable.init) 165 | } 166 | } 167 | ``` 168 | 169 | and then provide them in your custom type: 170 | ```swift 171 | struct FrogCustomSerializable: DefaultsSerializable, Equatable { 172 | 173 | static var _defaults: DefaultsBridge { return DefaultsFrogBridge() } 174 | static var _defaultsArray: DefaultsBridge<[FrogCustomSerializable]> { return DefaultsFrogArrayBridge() } 175 | 176 | let name: String 177 | } 178 | ``` 179 | 180 | But, you can also extend an existing type! 181 | ```swift 182 | extension Data: DefaultsSerializable { 183 | public static var _defaults: DefaultsBridge { return DefaultsDataBridge() } 184 | public static var _defaultsArray: DefaultsBridge<[Data]> { return DefaultsArrayBridge() } 185 | } 186 | ``` 187 | 188 | Also, take a look at our source code (or tests) to look at more examples or make an issue and we will try to help you out in need! -------------------------------------------------------------------------------- /MigrationGuides/migration_4_alpha_1_to_4_alpha_2.md: -------------------------------------------------------------------------------- 1 | # Migration guide from 4.0.0-alpha.1 to 4.0.0-alpha.2 2 | 3 | Because there was a long delay in releasing a new version, and some things had changed, here is a quick migration guide. 4 | 5 | ### Removed type-based default value types 6 | Oh well it didn't end up being that useful and in result we got both: our code got a lot more complicated and we got [major bug](https://github.com/radex/SwiftyUserDefaults/issues/162) to fix. 7 | 8 | Right now if you want to have a default value for your key, you need to specify it in the key _only_: 9 | ```swift 10 | let key = DefaultsKey("test1", defaultValue: "") 11 | ``` 12 | 13 | ### Updated a way of introducing custom retrieving/saving the values from a type: 14 | Now we use `DefaultsBridge`s of many kinds to specify how you get/set values and arrays of values. When you look at `DefaultsSerializable` protocol, it expects two properties in each type: `_defaults` and `_defaultsArray` which are of type `DefaultsBridge`. 15 | 16 | For instance, this is a bridge for single value data storing/retrieving using `NSKeyedArchiver`/`NSKeyedUnarchiver`: 17 | ```swift 18 | public final class DefaultsKeyedArchiverBridge: DefaultsBridge { 19 | 20 | public override func get(key: String, userDefaults: UserDefaults) -> T? { 21 | guard let data = userDefaults.data(forKey: key) else { 22 | return nil 23 | } 24 | return deserialize(data) 25 | } 26 | 27 | public override func save(key: String, value: T?, userDefaults: UserDefaults) { 28 | guard let value = value else { 29 | userDefaults.removeObject(forKey: key) 30 | return 31 | } 32 | 33 | userDefaults.set(NSKeyedArchiver.archivedData(withRootObject: value), forKey: key) 34 | } 35 | 36 | public override func isSerialized() -> Bool { 37 | return true 38 | } 39 | 40 | public override func deserialize(_ object: Any) -> T? { 41 | guard let data = object as? Data else { return nil } 42 | 43 | return NSKeyedUnarchiver.unarchiveObject(with: data) as? T 44 | } 45 | } 46 | ``` 47 | 48 | And for a simple case of storing/retrieving an array values: 49 | ```swift 50 | public final class DefaultsArrayBridge: DefaultsBridge { 51 | public override func save(key: String, value: T?, userDefaults: UserDefaults) { 52 | userDefaults.set(value, forKey: key) 53 | } 54 | 55 | public override func get(key: String, userDefaults: UserDefaults) -> T? { 56 | return userDefaults.array(forKey: key) as? T 57 | } 58 | } 59 | ``` 60 | 61 | Now, if you want to create a custom type and want to use `DefaultsKeyedArchiverBridge`: 62 | ```swift 63 | struct FrogCustomSerializable: DefaultsSerializable { 64 | 65 | static var _defaults: DefaultsBridge { return DefaultsKeyedArchiverBridge() } 66 | static var _defaultsArray: DefaultsBridge<[FrogCustomSerializable]> { return DefaultsKeyedArchiverBridge() } 67 | 68 | let name: String 69 | } 70 | ``` 71 | 72 | You have to remember though, that these built-in bridges are for specific use cases and you probably will end up writing your own bridges: 73 | ```swift 74 | final class DefaultsFrogBridge: DefaultsBridge { 75 | override func get(key: String, userDefaults: UserDefaults) -> FrogCustomSerializable? { 76 | let name = userDefaults.string(forKey: key) 77 | return name.map(FrogCustomSerializable.init) 78 | } 79 | 80 | override func save(key: String, value: FrogCustomSerializable?, userDefaults: UserDefaults) { 81 | userDefaults.set(value?.name, forKey: key) 82 | } 83 | 84 | public override func isSerialized() -> Bool { 85 | return true 86 | } 87 | 88 | public override func deserialize(_ object: Any) -> FrogCustomSerializable? { 89 | guard let name = object as? String else { return nil } 90 | 91 | return FrogCustomSerializable(name: name) 92 | } 93 | } 94 | 95 | final class DefaultsFrogArrayBridge: DefaultsBridge<[FrogCustomSerializable]> { 96 | override func get(key: String, userDefaults: UserDefaults) -> [FrogCustomSerializable]? { 97 | return userDefaults.array(forKey: key)? 98 | .compactMap { $0 as? String } 99 | .map(FrogCustomSerializable.init) 100 | } 101 | 102 | override func save(key: String, value: [FrogCustomSerializable]?, userDefaults: UserDefaults) { 103 | let values = value?.map { $0.name } 104 | userDefaults.set(values, forKey: key) 105 | } 106 | 107 | public override func isSerialized() -> Bool { 108 | return true 109 | } 110 | 111 | public override func deserialize(_ object: Any) -> [FrogCustomSerializable]? { 112 | guard let names = object as? [String] else { return nil } 113 | 114 | return names.map(FrogCustomSerializable.init) 115 | } 116 | } 117 | ``` 118 | 119 | and then provide them in your custom type: 120 | ```swift 121 | struct FrogCustomSerializable: DefaultsSerializable, Equatable { 122 | 123 | static var _defaults: DefaultsBridge { return DefaultsFrogBridge() } 124 | static var _defaultsArray: DefaultsBridge<[FrogCustomSerializable]> { return DefaultsFrogArrayBridge() } 125 | 126 | let name: String 127 | } 128 | ``` 129 | 130 | But, you can also extend an existing type! 131 | ```swift 132 | extension Data: DefaultsSerializable { 133 | public static var _defaults: DefaultsBridge { return DefaultsDataBridge() } 134 | public static var _defaultsArray: DefaultsBridge<[Data]> { return DefaultsArrayBridge() } 135 | } 136 | ``` 137 | 138 | Also, take a look at our source code (or tests) to look at more examples or make an issue and we will try to help you out in need! -------------------------------------------------------------------------------- /MigrationGuides/migration_4_to_5.md: -------------------------------------------------------------------------------- 1 | # Migration guide from 4.x to 5.x 2 | 3 | ### Changes to `Defaults` 4 | `Defaults` is now a global variable, a `DefaultsAdapter` object, not `UserDefaults` typealias as it was before. `DefaultsAdapter` is our new object that enables key path access or dynamic access instead of relying on the keys. 5 | Meaning you will use: 6 | ```swift 7 | Defaults[\.yourKey] 8 | 9 | // or if you use Swift 5.1, you can also: 10 | Defaults.yourKey 11 | ``` 12 | 13 | instead of 14 | ```swift 15 | Defaults[.yourKey] 16 | ``` 17 | 18 | Additionally when you want to access your key without a keyPath, but with a `DefaultsKey` as an argument, you would use: 19 | ```swift 20 | let key = DefaultsKey("userThemeName") 21 | Defaults[key: key] 22 | ``` 23 | 24 | instead of 25 | ```swift 26 | let key = DefaultsKey("userThemeName") 27 | Defaults[key] 28 | ``` 29 | 30 | We had to do this because otherwise the compiler crashed and indexing never finished... Track this issue here: https://bugs.swift.org/browse/SR-11529 31 | 32 | ### Changes to `DefaultsKeys` 33 | Now you can create your own key store and `DefaultsKeys` is a default object that conforms to `DefaultsKeyStore` and is passed to the global `Defaults`. 34 | 35 | Meaning you will now use: 36 | ```swift 37 | extension DefaultsKeys { 38 | var userThemeName: DefaultsKey { .init("userThemeName") } 39 | } 40 | ``` 41 | 42 | instead of: 43 | ```swift 44 | extension DefaultsKeys { 45 | static let userThemeName = DefaultsKey("userThemeName") 46 | } 47 | ``` 48 | 49 | ### Changes to `DefaultsBridge` 50 | First of all, it's a struct, not a class anymore! So no more inheritance, it's now all about composition when reusing the bridge. 51 | 52 | Secondly we removed the `isSerializable` property as well, so you can remove it. Now you need to always provide `deserialize()` method when implementing your own custom bridge. 53 | 54 | If you want to look at the bridge composition example (when using other bridges), see `DefaultsOptionalBridge` in `Sources/DefaultsBridges.swift`. -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Nimble", 6 | "repositoryURL": "https://github.com/Quick/Nimble.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "f8657642dfdec9973efc79cc68bcef43a653a2bc", 10 | "version": "8.0.2" 11 | } 12 | }, 13 | { 14 | "package": "Quick", 15 | "repositoryURL": "https://github.com/Quick/Quick.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "94df9b449508344667e5afc7e80f8bcbff1e4c37", 19 | "version": "2.1.0" 20 | } 21 | } 22 | ] 23 | }, 24 | "version": 1 25 | } 26 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | import class Foundation.ProcessInfo 6 | 7 | let shouldTest = ProcessInfo.processInfo.environment["TEST"] == "1" 8 | 9 | func resolveDependencies() -> [Package.Dependency] { 10 | guard shouldTest else { return [] } 11 | 12 | return [ 13 | .package(url: "https://github.com/Quick/Quick.git", .upToNextMajor(from: "2.0.0")), 14 | .package(url: "https://github.com/Quick/Nimble.git", .upToNextMajor(from: "8.0.0")), 15 | ] 16 | } 17 | 18 | func resolveTargets() -> [Target] { 19 | let baseTarget = Target.target(name: "SwiftyUserDefaults", dependencies: [], path: "Sources") 20 | let testTarget = Target.testTarget(name: "SwiftyUserDefaultsTests", dependencies: ["SwiftyUserDefaults", "Quick", "Nimble"]) 21 | 22 | return shouldTest ? [baseTarget, testTarget] : [baseTarget] 23 | } 24 | 25 | 26 | let package = Package( 27 | name: "SwiftyUserDefaults", 28 | platforms: [ 29 | .macOS(.v10_11), .iOS(.v9), .tvOS(.v9), .watchOS(.v2) 30 | ], 31 | products: [ 32 | .library(name: "SwiftyUserDefaults", targets: ["SwiftyUserDefaults"]), 33 | ], 34 | dependencies: resolveDependencies(), 35 | targets: resolveTargets() 36 | ) 37 | -------------------------------------------------------------------------------- /Package@swift-4.2.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "Nimble", 6 | "repositoryURL": "https://github.com/Quick/Nimble.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "9a281b1cfa1c53d1e8bd92e1798e4e473af8d263", 10 | "version": "7.3.3" 11 | } 12 | }, 13 | { 14 | "package": "Quick", 15 | "repositoryURL": "https://github.com/Quick/Quick.git", 16 | "state": { 17 | "branch": null, 18 | "revision": "f2b5a06440ea87eba1a167cab37bf6496646c52e", 19 | "version": "1.3.4" 20 | } 21 | } 22 | ] 23 | }, 24 | "version": 1 25 | } 26 | -------------------------------------------------------------------------------- /Package@swift-4.2.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | import class Foundation.ProcessInfo 6 | 7 | let shouldTest = ProcessInfo.processInfo.environment["TEST"] == "1" 8 | 9 | func resolveDependencies() -> [Package.Dependency] { 10 | guard shouldTest else { return [] } 11 | 12 | return [ 13 | .package(url: "https://github.com/Quick/Quick.git", .upToNextMajor(from: "1.3.0")), 14 | .package(url: "https://github.com/Quick/Nimble.git", .upToNextMajor(from: "7.1.0")) 15 | ] 16 | } 17 | 18 | func resolveTargets() -> [Target] { 19 | let baseTarget = Target.target(name: "SwiftyUserDefaults", dependencies: [], path: "Sources") 20 | let testTarget = Target.testTarget(name: "SwiftyUserDefaultsTests", dependencies: ["SwiftyUserDefaults", "Quick", "Nimble"]) 21 | 22 | return shouldTest ? [baseTarget, testTarget] : [baseTarget] 23 | } 24 | 25 | 26 | let package = Package( 27 | name: "SwiftyUserDefaults", 28 | products: [ 29 | .library( 30 | name: "SwiftyUserDefaults", 31 | targets: ["SwiftyUserDefaults"]), 32 | ], 33 | dependencies: resolveDependencies(), 34 | targets: resolveTargets(), 35 | swiftLanguageVersions: [.v4_2] 36 | ) 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftyUserDefaults 2 | 3 | ![Platforms](https://img.shields.io/badge/platforms-ios%20%7C%20osx%20%7C%20watchos%20%7C%20tvos-lightgrey.svg) 4 | [![CI Status](https://api.travis-ci.org/sunshinejr/SwiftyUserDefaults.svg?branch=master)](https://travis-ci.org/sunshinejr/SwiftyUserDefaults) 5 | [![CocoaPods compatible](https://img.shields.io/badge/CocoaPods-compatible-4BC51D.svg?style=flat)](#cocoapods) 6 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](#carthage) 7 | [![SPM compatible](https://img.shields.io/badge/SPM-compatible-4BC51D.svg?style=flat)](#swift-package-manager) 8 | ![Swift version](https://img.shields.io/badge/swift-4.1-orange.svg) 9 | ![Swift version](https://img.shields.io/badge/swift-4.2-orange.svg) 10 | ![Swift version](https://img.shields.io/badge/swift-5.0-orange.svg) 11 | ![Swift version](https://img.shields.io/badge/swift-5.1-orange.svg) 12 | 13 | #### Modern Swift API for `NSUserDefaults` 14 | ###### SwiftyUserDefaults makes user defaults enjoyable to use by combining expressive Swifty API with the benefits of static typing. Define your keys in one place, use value types easily, and get extra safety and convenient compile-time checks for free. 15 | 16 | Previous versions' documentation: [Version 4.0.0](https://github.com/sunshinejr/SwiftyUserDefaults/blob/566ace16ee91242b61e2e9da6cdbe7dfdadd926c/README.md), [Version 3.0.1](https://github.com/sunshinejr/SwiftyUserDefaults/blob/14b629b035bf6355b46ece22c3851068a488a895/README.md)
17 | Migration guides: [from 4.x to 5.x](MigrationGuides/migration_4_to_5.md), [from 4.0.0-alpha.1 to 4.0.0-alpha.3](MigrationGuides/migration_4_alpha_1_to_4_alpha_2.md), [from 3.x to 4.x](MigrationGuides/migration_3_to_4.md) 18 | 19 | # Version 5.0.0 20 | 21 |

22 | Features • 23 | Usage • 24 | Codable • 25 | NSCoding • 26 | RawRepresentable • 27 | Extending existing types • 28 | Custom types 29 |

30 |

31 | Property wrappers • 32 | KVO • 33 | dynamicMemberLookup • 34 | Launch arguments • 35 | Utils • 36 | Installation 37 |

38 | 39 | ## Features 40 | 41 | **There's only one step to start using SwiftyUserDefaults:** 42 | 43 | Define your keys! 44 | 45 | ```swift 46 | extension DefaultsKeys { 47 | var username: DefaultsKey { .init("username") } 48 | var launchCount: DefaultsKey { .init("launchCount", defaultValue: 0) } 49 | } 50 | ``` 51 | 52 | And just use it ;-) 53 | 54 | ```swift 55 | // Get and set user defaults easily 56 | let username = Defaults[\.username] 57 | Defaults[\.hotkeyEnabled] = true 58 | 59 | // Modify value types in place 60 | Defaults[\.launchCount] += 1 61 | Defaults[\.volume] -= 0.1 62 | Defaults[\.strings] += "… can easily be extended!" 63 | 64 | // Use and modify typed arrays 65 | Defaults[\.libraries].append("SwiftyUserDefaults") 66 | Defaults[\.libraries][0] += " 2.0" 67 | 68 | // Easily work with custom serialized types 69 | Defaults[\.color] = NSColor.white 70 | Defaults[\.color]?.whiteComponent // => 1.0 71 | ``` 72 | 73 | If you use Swift 5.1 - good news! You can also use keyPath `dynamicMemberLookup`: 74 | ```swift 75 | Defaults.color = NSColor.white 76 | ``` 77 | 78 | See more at the KeyPath dynamicMemberLookup section. 79 | 80 | ## Usage 81 | 82 | ### Define your keys 83 | 84 | To get the most out of SwiftyUserDefaults, define your user defaults keys ahead of time: 85 | 86 | ```swift 87 | let colorKey = DefaultsKey("color", defaultValue: "") 88 | ``` 89 | 90 | Just create a `DefaultsKey` object, put the type of the value you want to store in angle brackets, the key name in parentheses, and you're good to go. If you want to have a non-optional value, just provide a `defaultValue` in the key (look at the example above). 91 | 92 | You can now use the `Defaults` shortcut to access those values: 93 | 94 | ```swift 95 | Defaults[key: colorKey] = "red" 96 | Defaults[key: colorKey] // => "red", typed as String 97 | ``` 98 | 99 | The compiler won't let you set a wrong value type, and fetching conveniently returns `String`. 100 | 101 | ### Take shortcuts 102 | 103 | For extra convenience, define your keys by extending magic `DefaultsKeys` class and adding static properties: 104 | 105 | ```swift 106 | extension DefaultsKeys { 107 | var username: DefaultsKey { .init("username") } 108 | var launchCount: DefaultsKey { .init("launchCount", defaultValue: 0) } 109 | } 110 | ``` 111 | 112 | And use the shortcut dot syntax: 113 | 114 | ```swift 115 | Defaults[\.username] = "joe" 116 | Defaults[\.launchCount] += 1 117 | ``` 118 | 119 | ### Supported types 120 | 121 | SwiftyUserDefaults supports all of the standard `NSUserDefaults` types, like strings, numbers, booleans, arrays and dictionaries. 122 | 123 | Here's a full table of built-in single value defaults: 124 | 125 | | Single value | Array | 126 | | ---------------- | -------------------- | 127 | | `String` | `[String]` | 128 | | `Int` | `[Int]` | 129 | | `Double` | `[Double]` | 130 | | `Bool` | `[Bool]` | 131 | | `Data` | `[Data]` | 132 | | `Date` | `[Date]` | 133 | | `URL` | `[URL]` | 134 | | `[String: Any]` | `[[String: Any]]` | 135 | 136 | But that's not all! 137 | 138 | ## Codable 139 | 140 | Since version 4, `SwiftyUserDefaults` support `Codable`! Just conform to `DefaultsSerializable` in your type: 141 | ```swift 142 | final class FrogCodable: Codable, DefaultsSerializable { 143 | let name: String 144 | } 145 | ``` 146 | 147 | No implementation needed! By doing this you will get an option to specify an optional `DefaultsKey`: 148 | ```swift 149 | let frog = DefaultsKey("frog") 150 | ``` 151 | 152 | Additionally, you've got an array support for free: 153 | ```swift 154 | let froggies = DefaultsKey<[FrogCodable]?>("froggies") 155 | ``` 156 | 157 | ## NSCoding 158 | 159 | `NSCoding` was supported before version 4, but in this version we take the support on another level. No need for custom subscripts anymore! 160 | Support your custom `NSCoding` type the same way as with `Codable` support: 161 | ``` 162 | final class FrogSerializable: NSObject, NSCoding, DefaultsSerializable { ... } 163 | ``` 164 | 165 | No implementation needed as well! By doing this you will get an option to specify an optional `DefaultsKey`: 166 | ```swift 167 | let frog = DefaultsKey("frog") 168 | ``` 169 | 170 | Additionally, you've got an array support also for free: 171 | ```swift 172 | let froggies = DefaultsKey<[FrogSerializable]?>("froggies") 173 | ``` 174 | 175 | ## RawRepresentable 176 | 177 | And the last but not least, `RawRepresentable` support! Again, the same situation like with `NSCoding` and `Codable`: 178 | ```swift 179 | enum BestFroggiesEnum: String, DefaultsSerializable { 180 | case Andy 181 | case Dandy 182 | } 183 | ``` 184 | 185 | No implementation needed as well! By doing this you will get an option to specify an optional `DefaultsKey`: 186 | ```swift 187 | let frog = DefaultsKey("frog") 188 | ``` 189 | 190 | Additionally, you've got an array support also for free: 191 | ```swift 192 | let froggies = DefaultsKey<[BestFroggiesEnum]?>("froggies") 193 | ``` 194 | 195 | ## Extending existing types 196 | 197 | Let's say you want to extend a support `UIColor` or any other type that is `NSCoding`, `Codable` or `RawRepresentable`. 198 | Extending it to be `SwiftyUserDefaults`-friendly should be as easy as: 199 | ```swift 200 | extension UIColor: DefaultsSerializable {} 201 | ``` 202 | 203 | If it's not, we have two options:
204 | a) It's a custom type that we don't know how to serialize, in this case at [Custom types](#custom-types)
205 | b) It's a bug and it should be supported, in this case please file an issue (+ you can use [custom types](#custom-types) method as a workaround in the meantime)
206 | 207 | ## Custom types 208 | 209 | If you want to add your own custom type that we don't support yet, we've got you covered. We use `DefaultsBridge`s of many kinds to specify how you get/set values and arrays of values. When you look at `DefaultsSerializable` protocol, it expects two properties in each type: `_defaults` and `_defaultsArray`, where both are of type `DefaultsBridge`. 210 | 211 | For instance, this is a bridge for single value data storing/retrieving using `NSKeyedArchiver`/`NSKeyedUnarchiver`: 212 | ```swift 213 | public struct DefaultsKeyedArchiverBridge: DefaultsBridge { 214 | 215 | public func get(key: String, userDefaults: UserDefaults) -> T? { 216 | userDefaults.data(forKey: key).flatMap(NSKeyedUnarchiver.unarchiveObject) as? T 217 | } 218 | 219 | public func save(key: String, value: T?, userDefaults: UserDefaults) { 220 | userDefaults.set(NSKeyedArchiver.archivedData(withRootObject: value), forKey: key) 221 | } 222 | 223 | public func deserialize(_ object: Any) -> T? { 224 | guard let data = object as? Data else { return nil } 225 | return NSKeyedUnarchiver.unarchiveObject(with: data) as? T 226 | } 227 | } 228 | ``` 229 | 230 | Bridge for default storing/retrieving array values: 231 | ```swift 232 | public struct DefaultsArrayBridge: DefaultsBridge { 233 | public func save(key: String, value: T?, userDefaults: UserDefaults) { 234 | userDefaults.set(value, forKey: key) 235 | } 236 | 237 | public func get(key: String, userDefaults: UserDefaults) -> T? { 238 | userDefaults.array(forKey: key) as? T 239 | } 240 | 241 | public func deserialize(_ object: Any) -> T? { 242 | nil 243 | } 244 | } 245 | ``` 246 | 247 | Now, to use these bridges in our type we simply declare it as follows: 248 | ```swift 249 | struct FrogCustomSerializable: DefaultsSerializable { 250 | 251 | static var _defaults: DefaultsKeyedArchiverBridge( { DefaultsKeyedArchiverBridge() } 252 | static var _defaultsArray: DefaultsKeyedArchiverBridge { DefaultsKeyedArchiverBridge() } 253 | 254 | let name: String 255 | } 256 | ``` 257 | 258 | Unfortunately, if you find yourself in a situation where you need a custom bridge, you'll probably need to write your own: 259 | ```swift 260 | final class DefaultsFrogBridge: DefaultsBridge { 261 | func get(key: String, userDefaults: UserDefaults) -> FrogCustomSerializable? { 262 | let name = userDefaults.string(forKey: key) 263 | return name.map(FrogCustomSerializable.init) 264 | } 265 | 266 | func save(key: String, value: FrogCustomSerializable?, userDefaults: UserDefaults) { 267 | userDefaults.set(value?.name, forKey: key) 268 | } 269 | 270 | func deserialize(_ object: Any) -> FrogCustomSerializable? { 271 | guard let name = object as? String else { return nil } 272 | 273 | return FrogCustomSerializable(name: name) 274 | } 275 | } 276 | 277 | final class DefaultsFrogArrayBridge: DefaultsBridge { 278 | func get(key: String, userDefaults: UserDefaults) -> [FrogCustomSerializable]? { 279 | userDefaults.array(forKey: key)? 280 | .compactMap { $0 as? String } 281 | .map(FrogCustomSerializable.init) 282 | } 283 | 284 | func save(key: String, value: [FrogCustomSerializable]?, userDefaults: UserDefaults) { 285 | let values = value?.map { $0.name } 286 | userDefaults.set(values, forKey: key) 287 | } 288 | 289 | func deserialize(_ object: Any) -> [FrogCustomSerializable]? { 290 | guard let names = object as? [String] else { return nil } 291 | 292 | return names.map(FrogCustomSerializable.init) 293 | } 294 | } 295 | 296 | struct FrogCustomSerializable: DefaultsSerializable, Equatable { 297 | 298 | static var _defaults: DefaultsFrogBridge { DefaultsFrogBridge() } 299 | static var _defaultsArray: DefaultsFrogArrayBridge { DefaultsFrogArrayBridge() } 300 | 301 | let name: String 302 | } 303 | ``` 304 | 305 | To support existing types with different bridges, you can extend it similarly: 306 | ```swift 307 | extension Data: DefaultsSerializable { 308 | public static var _defaultsArray: DefaultsArrayBridge<[T]> { DefaultsArrayBridge() } 309 | public static var _defaults: DefaultsDataBridge { DefaultsDataBridge() } 310 | } 311 | ``` 312 | 313 | Also, take a look at our source code (or tests) to see more examples of bridges. If you find yourself confused with all these bridges, please [create an issue](https://github.com/sunshinejr/SwiftyUserDefaults/issues/new) and we will figure something out. 314 | 315 | ## Property wrappers 316 | 317 | SwiftyUserDefaults provides property wrappers for Swift 5.1! The property wrapper, `@SwiftyUserDefault`, provides an option to use it with key path and options: caching or observing. 318 | 319 | *Caching* means that we will store the value for you and do not hit the `UserDefaults` for value almost never, only for the first value fetch. 320 | 321 | *Observing* means we will observe, via KVO, your property so you don't have to worry if it was saved somewhere else and you use caching. 322 | 323 | Now usage! Given keys: 324 | ```swift 325 | extension DefaultsKeys { 326 | var userColorScheme: DefaultsKey { .init("userColorScheme", defaultValue: "default") } 327 | var userThemeName: DefaultsKey { .init("userThemeName") } 328 | var userLastLoginDate: DefaultsKey { .init("userLastLoginDate") } 329 | } 330 | ``` 331 | 332 | You can declare a `Settings` struct: 333 | ```swift 334 | struct Settings { 335 | @SwiftyUserDefault(keyPath: \.userColorScheme) 336 | var userColorScheme: String 337 | 338 | @SwiftyUserDefault(keyPath: \.userThemeName, options: .cached) 339 | var userThemeName: String? 340 | 341 | @SwiftyUserDefault(keyPath: \.userLastLoginDate, options: [.cached, .observed]) 342 | var userLastLoginDate: Date? 343 | } 344 | ``` 345 | 346 | ## KVO 347 | 348 | KVO is supported for all the types that are `DefaultsSerializable`. However, if you have a custom type, it needs to have correctly defined bridges and serialization in them. 349 | 350 | To observe a value for local DefaultsKey: 351 | ```swift 352 | let nameKey = DefaultsKey("name", defaultValue: "") 353 | Defaults.observe(key: nameKey) { update in 354 | // here you can access `oldValue`/`newValue` and few other properties 355 | } 356 | ``` 357 | 358 | To observe a value for a key defined in DefaultsKeys extension: 359 | ```swift 360 | Defaults.observe(\.nameKey) { update in 361 | // here you can access `oldValue`/`newValue` and few other properties 362 | } 363 | ``` 364 | 365 | 366 | By default we are using `[.old, .new]` options for observing, but you can provide your own: 367 | ```swift 368 | Defaults.observe(key: nameKey, options: [.initial, .old, .new]) { _ in } 369 | ``` 370 | 371 | ## KeyPath dynamicMemberLookup 372 | 373 | SwiftyUserDefaults makes KeyPath dynamicMemberLookup usable in Swift 5.1! 374 | 375 | ```swift 376 | extension DefaultsKeys { 377 | var username: DefaultsKey { .init("username") } 378 | var launchCount: DefaultsKey { .init("launchCount", defaultValue: 0) } 379 | } 380 | ``` 381 | 382 | And just use it ;-) 383 | 384 | ```swift 385 | // Get and set user defaults easily 386 | let username = Defaults.username 387 | Defaults.hotkeyEnabled = true 388 | 389 | // Modify value types in place 390 | Defaults.launchCount += 1 391 | Defaults.volume -= 0.1 392 | Defaults.strings += "… can easily be extended!" 393 | 394 | // Use and modify typed arrays 395 | Defaults.libraries.append("SwiftyUserDefaults") 396 | Defaults.libraries[0] += " 2.0" 397 | 398 | // Easily work with custom serialized types 399 | Defaults.color = NSColor.white 400 | Defaults.color?.whiteComponent // => 1.0 401 | ``` 402 | 403 | ## Launch arguments 404 | 405 | Do you like to customize your app/script/tests by UserDefaults? Now it's fully supported on our side, statically typed of course. 406 | 407 | _Note: for now we support only `Bool`, `Double`, `Int`, `String` values, but if you have any other requests for that feature, please open an issue or PR and we can talk about implementing it in new versions._ 408 | 409 | ### You can pass your arguments in your schema: 410 | Pass launch arguments in Xcode Schema editor. 411 | 412 | ### Or you can use launch arguments in XCUIApplication: 413 | ```swift 414 | func testExample() { 415 | let app = XCUIApplication() 416 | app.launchArguments = ["-skipLogin", "true", "-loginTries", "3", "-lastGameTime", "61.3", "-nickname", "sunshinejr"] 417 | app.launch() 418 | } 419 | ``` 420 | ### Or pass them as command line arguments! 421 | ```bash 422 | ./script -skipLogin true -loginTries 3 -lastGameTime 61.3 -nickname sunshinejr 423 | ``` 424 | 425 | ## Utils 426 | 427 | ### Remove all keys 428 | 429 | To reset user defaults, use `removeAll` method. 430 | 431 | ```swift 432 | Defaults.removeAll() 433 | ``` 434 | 435 | ### Shared user defaults 436 | 437 | If you're sharing your user defaults between different apps or an app and its extensions, you can use SwiftyUserDefaults by overriding the `Defaults` shortcut with your own. Just add in your app: 438 | 439 | ```swift 440 | var Defaults = DefaultsAdapter(defaults: UserDefaults(suiteName: "com.my.app")!, keyStore: .init()) 441 | ``` 442 | 443 | ### Check key 444 | 445 | If you want to check if we've got a value for `DefaultsKey`: 446 | ```swift 447 | let hasKey = Defaults.hasKey(\.skipLogin) 448 | ``` 449 | 450 | ## Installation 451 | 452 | ### Requirements 453 | **Swift** version **>= 4.1**
454 | **iOS** version **>= 9.0**
455 | **macOS** version **>= 10.11**
456 | **tvOS** version **>= 9.0**
457 | **watchOS** version **>= 2.0** 458 | 459 | ### CocoaPods 460 | 461 | If you're using CocoaPods, just add this line to your Podfile: 462 | 463 | ```ruby 464 | pod 'SwiftyUserDefaults', '~> 5.0' 465 | ``` 466 | 467 | Install by running this command in your terminal: 468 | 469 | ```sh 470 | pod install 471 | ``` 472 | 473 | Then import the library in all files where you use it: 474 | 475 | ```swift 476 | import SwiftyUserDefaults 477 | ``` 478 | 479 | ### Carthage 480 | 481 | Just add to your Cartfile: 482 | 483 | ```ruby 484 | github "sunshinejr/SwiftyUserDefaults" ~> 5.0 485 | ``` 486 | 487 | ### Swift Package Manager 488 | 489 | Just add to your `Package.swift` under dependencies: 490 | ```swift 491 | let package = Package( 492 | name: "MyPackage", 493 | products: [...], 494 | dependencies: [ 495 | .package(url: "https://github.com/sunshinejr/SwiftyUserDefaults.git", .upToNextMajor(from: "5.0.0")) 496 | ], 497 | targets: [...] 498 | ) 499 | ``` 500 | 501 | ## More like this 502 | 503 | If you like SwiftyUserDefaults, check out [SwiftyTimer](https://github.com/radex/SwiftyTimer), which applies the same swifty approach to `NSTimer`. 504 | 505 | You might also be interested in my blog posts which explain the design process behind those libraries: 506 | - [Swifty APIs: NSUserDefaults](http://radex.io/swift/nsuserdefaults/) 507 | - [Statically-typed NSUserDefaults](http://radex.io/swift/nsuserdefaults/static) 508 | - [Swifty APIs: NSTimer](http://radex.io/swift/nstimer/) 509 | - [Swifty methods](http://radex.io/swift/methods/) 510 | 511 | ## Contributing 512 | 513 | If you have comments, complaints or ideas for improvements, feel free to open an issue or a pull request. 514 | 515 | ## Authors and license 516 | 517 | *Maintainer:* Łukasz Mróz 518 | * [github.com/sunshinejr](http://github.com/sunshinejr) 519 | * [twitter.com/thesunshinejr](http://twitter.com/thesunshinejr) 520 | * [sunshinejr.com](https://sunshinejr.com) 521 | 522 | *Created by:* Radek Pietruszewski 523 | 524 | * [github.com/radex](http://github.com/radex) 525 | * [twitter.com/radexp](http://twitter.com/radexp) 526 | * [radex.io](http://radex.io) 527 | * this.is@radex.io 528 | 529 | SwiftyUserDefaults is available under the MIT license. See the LICENSE file for more info. 530 | -------------------------------------------------------------------------------- /Sources/BuiltIns.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension DefaultsSerializable { 28 | public static var _defaultsArray: DefaultsArrayBridge<[T]> { return DefaultsArrayBridge() } 29 | } 30 | extension Date: DefaultsSerializable { 31 | public static var _defaults: DefaultsObjectBridge { return DefaultsObjectBridge() } 32 | } 33 | extension String: DefaultsSerializable { 34 | public static var _defaults: DefaultsStringBridge { return DefaultsStringBridge() } 35 | } 36 | extension Int: DefaultsSerializable { 37 | public static var _defaults: DefaultsIntBridge { return DefaultsIntBridge() } 38 | } 39 | extension Double: DefaultsSerializable { 40 | public static var _defaults: DefaultsDoubleBridge { return DefaultsDoubleBridge() } 41 | } 42 | extension Bool: DefaultsSerializable { 43 | public static var _defaults: DefaultsBoolBridge { return DefaultsBoolBridge() } 44 | } 45 | extension Data: DefaultsSerializable { 46 | public static var _defaults: DefaultsDataBridge { return DefaultsDataBridge() } 47 | } 48 | 49 | extension URL: DefaultsSerializable { 50 | #if os(Linux) 51 | public static var _defaults: DefaultsKeyedArchiverBridge { return DefaultsKeyedArchiverBridge() } 52 | #else 53 | public static var _defaults: DefaultsUrlBridge { return DefaultsUrlBridge() } 54 | #endif 55 | public static var _defaultsArray: DefaultsKeyedArchiverBridge<[URL]> { return DefaultsKeyedArchiverBridge() } 56 | } 57 | 58 | extension DefaultsSerializable where Self: Codable { 59 | public static var _defaults: DefaultsCodableBridge { return DefaultsCodableBridge() } 60 | public static var _defaultsArray: DefaultsCodableBridge<[Self]> { return DefaultsCodableBridge() } 61 | } 62 | 63 | extension DefaultsSerializable where Self: RawRepresentable { 64 | public static var _defaults: DefaultsRawRepresentableBridge { return DefaultsRawRepresentableBridge() } 65 | public static var _defaultsArray: DefaultsRawRepresentableArrayBridge<[Self]> { return DefaultsRawRepresentableArrayBridge() } 66 | } 67 | 68 | extension DefaultsSerializable where Self: NSCoding { 69 | public static var _defaults: DefaultsKeyedArchiverBridge { return DefaultsKeyedArchiverBridge() } 70 | public static var _defaultsArray: DefaultsKeyedArchiverBridge<[Self]> { return DefaultsKeyedArchiverBridge() } 71 | } 72 | 73 | extension Dictionary: DefaultsSerializable where Key == String { 74 | public typealias T = [Key: Value] 75 | public typealias Bridge = DefaultsObjectBridge 76 | public typealias ArrayBridge = DefaultsArrayBridge<[T]> 77 | public static var _defaults: Bridge { return Bridge() } 78 | public static var _defaultsArray: ArrayBridge { return ArrayBridge() } 79 | } 80 | extension Array: DefaultsSerializable where Element: DefaultsSerializable { 81 | public typealias T = [Element.T] 82 | public typealias Bridge = Element.ArrayBridge 83 | public typealias ArrayBridge = DefaultsObjectBridge<[T]> 84 | public static var _defaults: Bridge { 85 | return Element._defaultsArray 86 | } 87 | public static var _defaultsArray: ArrayBridge { 88 | fatalError("Multidimensional arrays are not supported yet") 89 | } 90 | } 91 | 92 | extension Optional: DefaultsSerializable where Wrapped: DefaultsSerializable { 93 | public typealias Bridge = DefaultsOptionalBridge 94 | public typealias ArrayBridge = DefaultsOptionalBridge 95 | 96 | public static var _defaults: DefaultsOptionalBridge { return DefaultsOptionalBridge(bridge: Wrapped._defaults) } 97 | public static var _defaultsArray: DefaultsOptionalBridge { return DefaultsOptionalBridge(bridge: Wrapped._defaultsArray) } 98 | } 99 | -------------------------------------------------------------------------------- /Sources/Defaults+Observing.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | #if !os(Linux) 28 | 29 | public extension DefaultsAdapter { 30 | 31 | func observe(_ key: DefaultsKey, 32 | options: NSKeyValueObservingOptions = [.new, .old], 33 | handler: @escaping (DefaultsObserver.Update) -> Void) -> DefaultsDisposable { 34 | return defaults.observe(key, options: options, handler: handler) 35 | } 36 | 37 | func observe(_ keyPath: KeyPath>, 38 | options: NSKeyValueObservingOptions = [.old, .new], 39 | handler: @escaping (DefaultsObserver.Update) -> Void) -> DefaultsDisposable { 40 | return defaults.observe(keyStore[keyPath: keyPath], 41 | options: options, 42 | handler: handler) 43 | } 44 | } 45 | 46 | public extension UserDefaults { 47 | 48 | func observe(_ key: DefaultsKey, options: NSKeyValueObservingOptions = [.old, .new], handler: @escaping (DefaultsObserver.Update) -> Void) -> DefaultsDisposable { 49 | return DefaultsObserver(key: key, userDefaults: self, options: options, handler: handler) 50 | } 51 | } 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /Sources/Defaults+StringToBool.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | // We don't want to use `NSString.boolValue` as it's converting every miss to `false`, instead of `nil` 28 | internal extension String { 29 | var bool: Bool? { 30 | switch self.lowercased() { 31 | case "true", "t", "yes", "y", "1": 32 | return true 33 | case "false", "f", "no", "n", "0": 34 | return false 35 | default: 36 | return nil 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Defaults+Subscripts.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | public protocol DefaultsProviding { 28 | associatedtype KeyStore: DefaultsKeyStore 29 | 30 | subscript(key key: DefaultsKey) -> T.T where T: OptionalType, T.T == T { get nonmutating set } 31 | subscript(key key: DefaultsKey) -> T.T where T.T == T { get nonmutating set } 32 | subscript(keyPath: KeyPath>) -> T.T where T: OptionalType, T.T == T { get nonmutating set } 33 | subscript(keyPath: KeyPath>) -> T.T where T.T == T { get nonmutating set } 34 | subscript(dynamicMember keyPath: KeyPath>) -> T.T where T: OptionalType, T.T == T { get nonmutating set } 35 | subscript(dynamicMember keyPath: KeyPath>) -> T.T where T.T == T { get nonmutating set } 36 | } 37 | 38 | extension DefaultsAdapter: DefaultsProviding { 39 | public subscript(key key: DefaultsKey) -> T.T where T: OptionalType, T.T == T { 40 | get { 41 | return defaults[key] 42 | } 43 | nonmutating set { 44 | defaults[key] = newValue 45 | } 46 | } 47 | 48 | public subscript(key key: DefaultsKey) -> T.T where T.T == T { 49 | get { 50 | return defaults[key] 51 | } 52 | nonmutating set { 53 | defaults[key] = newValue 54 | } 55 | } 56 | 57 | public subscript(keyPath: KeyPath>) -> T.T where T: OptionalType, T.T == T { 58 | get { 59 | return defaults[keyStore[keyPath: keyPath]] 60 | } 61 | nonmutating set { 62 | defaults[keyStore[keyPath: keyPath]] = newValue 63 | } 64 | } 65 | 66 | public subscript(keyPath: KeyPath>) -> T.T where T.T == T { 67 | get { 68 | return defaults[keyStore[keyPath: keyPath]] 69 | } 70 | nonmutating set { 71 | defaults[keyStore[keyPath: keyPath]] = newValue 72 | } 73 | } 74 | 75 | // Weird flex, but needed these two for the dynamicMemberLookup :shrug: 76 | 77 | public subscript(dynamicMember keyPath: KeyPath>) -> T.T where T: OptionalType, T.T == T { 78 | get { 79 | return self[keyPath] 80 | } 81 | nonmutating set { 82 | self[keyPath] = newValue 83 | } 84 | } 85 | 86 | public subscript(dynamicMember keyPath: KeyPath>) -> T.T where T.T == T { 87 | get { 88 | return self[keyPath] 89 | } 90 | nonmutating set { 91 | self[keyPath] = newValue 92 | } 93 | } 94 | } 95 | 96 | public extension UserDefaults { 97 | 98 | subscript(key: DefaultsKey) -> T.T where T: OptionalType, T.T == T { 99 | get { 100 | if let value = T._defaults.get(key: key._key, userDefaults: self), let _value = value as? T.T.Wrapped { 101 | // swiftlint:disable:next force_cast 102 | return _value as! T 103 | } else if let defaultValue = key.defaultValue { 104 | return defaultValue 105 | } else { 106 | return T.T.__swifty_empty 107 | } 108 | } 109 | set { 110 | T._defaults.save(key: key._key, value: newValue, userDefaults: self) 111 | } 112 | } 113 | 114 | subscript(key: DefaultsKey) -> T.T where T.T == T { 115 | get { 116 | if let value = T._defaults.get(key: key._key, userDefaults: self) { 117 | return value 118 | } else if let defaultValue = key.defaultValue { 119 | return defaultValue 120 | } else { 121 | fatalError("Shouldn't happen, please report!") 122 | } 123 | } 124 | set { 125 | T._defaults.save(key: key._key, value: newValue, userDefaults: self) 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Sources/Defaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Global shortcut for `UserDefaults.standard` 28 | /// 29 | /// **Pro-Tip:** If you want to use shared user defaults, just 30 | /// redefine this global shortcut in your app target, like so: 31 | /// ~~~ 32 | /// var Defaults = DefaultsAdapter(defaults: UserDefaults(suiteName: "com.my.app")!, keyStore: DefaultsKeys()) 33 | /// ~~~ 34 | 35 | public var Defaults = DefaultsAdapter(defaults: .standard, keyStore: .init()) 36 | 37 | public extension UserDefaults { 38 | 39 | /// Returns `true` if `key` exists 40 | func hasKey(_ key: DefaultsKey) -> Bool { 41 | return object(forKey: key._key) != nil 42 | } 43 | 44 | /// Removes value for `key` 45 | func remove(_ key: DefaultsKey) { 46 | removeObject(forKey: key._key) 47 | } 48 | 49 | /// Removes all keys and values from user defaults 50 | /// Use with caution! 51 | /// - Note: This method only removes keys on the receiver `UserDefaults` object. 52 | /// System-defined keys will still be present afterwards. 53 | func removeAll() { 54 | for (key, _) in dictionaryRepresentation() { 55 | removeObject(forKey: key) 56 | } 57 | } 58 | } 59 | 60 | internal extension UserDefaults { 61 | 62 | func number(forKey key: String) -> NSNumber? { 63 | return object(forKey: key) as? NSNumber 64 | } 65 | 66 | func decodable(forKey key: String) -> T? { 67 | guard let decodableData = data(forKey: key) else { return nil } 68 | 69 | return try? JSONDecoder().decode(T.self, from: decodableData) 70 | } 71 | 72 | /// Encodes passed `encodable` and saves the resulting data into the user defaults for the key `key`. 73 | /// Any error encoding will result in an assertion failure. 74 | func set(encodable: T, forKey key: String) { 75 | do { 76 | let data = try JSONEncoder().encode(encodable) 77 | set(data, forKey: key) 78 | } catch { 79 | assertionFailure("Failure encoding encodable of type \(T.self): \(error.localizedDescription)") 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Sources/DefaultsAdapter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// A UserDefaults wrapper. It makes KeyPath dynamicMemberLookup usable with UserDefaults in Swift 5.1 or greater. 28 | /// If Swift 5.0 or less, It works as ordinary SwiftyUserDefaults. 29 | /// 30 | /// - seealso: https://github.com/apple/swift-evolution/blob/master/proposals/0252-keypath-dynamic-member-lookup.md 31 | /// 32 | /// Here is a example: 33 | /// 34 | /// ``` 35 | /// extension DefaultsKeys { 36 | /// var launchCount: DefaultsKey { 37 | /// return .init("launchCount", defaultValue: 0) 38 | /// } 39 | /// } 40 | /// 41 | /// Defaults.launchCount += 1 42 | /// ``` 43 | @dynamicMemberLookup 44 | public struct DefaultsAdapter { 45 | 46 | public let defaults: UserDefaults 47 | public let keyStore: KeyStore 48 | 49 | public init(defaults: UserDefaults, keyStore: KeyStore) { 50 | self.defaults = defaults 51 | self.keyStore = keyStore 52 | } 53 | 54 | @available(*, unavailable) 55 | public subscript(dynamicMember member: String) -> Never { 56 | fatalError() 57 | } 58 | 59 | public func hasKey(_ key: DefaultsKey) -> Bool { 60 | return defaults.hasKey(key) 61 | } 62 | 63 | public func hasKey(_ keyPath: KeyPath>) -> Bool { 64 | return defaults.hasKey(keyStore[keyPath: keyPath]) 65 | } 66 | 67 | public func remove(_ key: DefaultsKey) { 68 | defaults.remove(key) 69 | } 70 | 71 | public func remove(_ keyPath: KeyPath>) { 72 | defaults.remove(keyStore[keyPath: keyPath]) 73 | } 74 | 75 | public func removeAll() { 76 | defaults.removeAll() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/DefaultsBridges.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | public protocol DefaultsBridge { 28 | 29 | associatedtype T 30 | 31 | /// This method provides a way of saving your data in UserDefaults. Usually needed 32 | /// when you want to create your custom Bridge, so you'll have to override it. 33 | func get(key: String, userDefaults: UserDefaults) -> T? 34 | 35 | /// This method provides a way of saving your data in UserDefaults. Usually needed 36 | /// when you want to create your custom Bridge, so you'll have to override it. 37 | func save(key: String, value: T?, userDefaults: UserDefaults) 38 | 39 | /// Override this function if your data is represented differently in UserDefaults 40 | /// and you map it in save/get methods. 41 | /// 42 | /// For instance, if you store it as Data in UserDefaults, but your type is not Data in your 43 | /// defaults key, then you need to provide `deserialize(_:)` method as well. 44 | /// 45 | /// Similar if you store your array of type as e.g. `[String]` but the type you use is actually `[SomeClassThatHasOnlyOneStringProperty]`. 46 | /// 47 | /// See `DefaultsRawRepresentableBridge` or `DefaultsCodableBridge` for examples. 48 | func deserialize(_ object: Any) -> T? 49 | } 50 | 51 | public struct DefaultsObjectBridge: DefaultsBridge { 52 | 53 | public init() {} 54 | 55 | public func save(key: String, value: T?, userDefaults: UserDefaults) { 56 | userDefaults.set(value, forKey: key) 57 | } 58 | 59 | public func get(key: String, userDefaults: UserDefaults) -> T? { 60 | return userDefaults.object(forKey: key) as? T 61 | } 62 | 63 | public func deserialize(_ object: Any) -> T? { 64 | return nil 65 | } 66 | } 67 | 68 | public struct DefaultsArrayBridge: DefaultsBridge { 69 | 70 | public init() {} 71 | 72 | public func save(key: String, value: T?, userDefaults: UserDefaults) { 73 | userDefaults.set(value, forKey: key) 74 | } 75 | 76 | public func get(key: String, userDefaults: UserDefaults) -> T? { 77 | return userDefaults.array(forKey: key) as? T 78 | } 79 | 80 | public func deserialize(_ object: Any) -> T? { 81 | return nil 82 | } 83 | } 84 | 85 | public struct DefaultsStringBridge: DefaultsBridge { 86 | 87 | public init() {} 88 | 89 | public func save(key: String, value: String?, userDefaults: UserDefaults) { 90 | userDefaults.set(value, forKey: key) 91 | } 92 | 93 | public func get(key: String, userDefaults: UserDefaults) -> String? { 94 | return userDefaults.string(forKey: key) 95 | } 96 | 97 | public func deserialize(_ object: Any) -> String? { 98 | return nil 99 | } 100 | } 101 | 102 | public struct DefaultsIntBridge: DefaultsBridge { 103 | 104 | public init() {} 105 | 106 | public func save(key: String, value: Int?, userDefaults: UserDefaults) { 107 | userDefaults.set(value, forKey: key) 108 | } 109 | 110 | public func get(key: String, userDefaults: UserDefaults) -> Int? { 111 | if let int = userDefaults.number(forKey: key)?.intValue { 112 | return int 113 | } 114 | 115 | // Fallback for launch arguments 116 | if let string = userDefaults.object(forKey: key) as? String, 117 | let int = Int(string) { 118 | return int 119 | } 120 | 121 | return nil 122 | } 123 | 124 | public func deserialize(_ object: Any) -> Int? { 125 | return nil 126 | } 127 | } 128 | 129 | public struct DefaultsDoubleBridge: DefaultsBridge { 130 | 131 | public init() {} 132 | 133 | public func save(key: String, value: Double?, userDefaults: UserDefaults) { 134 | userDefaults.set(value, forKey: key) 135 | } 136 | 137 | public func get(key: String, userDefaults: UserDefaults) -> Double? { 138 | if let double = userDefaults.number(forKey: key)?.doubleValue { 139 | return double 140 | } 141 | 142 | // Fallback for launch arguments 143 | if let string = userDefaults.object(forKey: key) as? String, 144 | let double = Double(string) { 145 | return double 146 | } 147 | 148 | return nil 149 | } 150 | 151 | public func deserialize(_ object: Any) -> Double? { 152 | return nil 153 | } 154 | } 155 | 156 | public struct DefaultsBoolBridge: DefaultsBridge { 157 | 158 | public init() {} 159 | 160 | public func save(key: String, value: Bool?, userDefaults: UserDefaults) { 161 | userDefaults.set(value, forKey: key) 162 | } 163 | 164 | public func get(key: String, userDefaults: UserDefaults) -> Bool? { 165 | // @warning we use number(forKey:) instead of bool(forKey:), because 166 | // bool(forKey:) will always return a value, even if it's not set 167 | // 168 | // Now, let's see if there is value in defaults that converts to Bool first: 169 | if let bool = userDefaults.number(forKey: key)?.boolValue { 170 | return bool 171 | } 172 | 173 | // If not, fallback for values saved in a plist (e.g. for testing) 174 | // For instance, few of the string("YES", "true", "NO", "false") convert to Bool from a property list 175 | return (userDefaults.object(forKey: key) as? String)?.bool 176 | } 177 | 178 | public func deserialize(_ object: Any) -> Bool? { 179 | return nil 180 | } 181 | } 182 | 183 | public struct DefaultsDataBridge: DefaultsBridge { 184 | 185 | public init() {} 186 | 187 | public func save(key: String, value: Data?, userDefaults: UserDefaults) { 188 | userDefaults.set(value, forKey: key) 189 | } 190 | 191 | public func get(key: String, userDefaults: UserDefaults) -> Data? { 192 | return userDefaults.data(forKey: key) 193 | } 194 | 195 | public func deserialize(_ object: Any) -> Data? { 196 | return nil 197 | } 198 | } 199 | 200 | public struct DefaultsUrlBridge: DefaultsBridge { 201 | 202 | public init() {} 203 | 204 | public func save(key: String, value: URL?, userDefaults: UserDefaults) { 205 | userDefaults.set(value, forKey: key) 206 | } 207 | 208 | public func get(key: String, userDefaults: UserDefaults) -> URL? { 209 | return userDefaults.url(forKey: key) 210 | } 211 | 212 | public func deserialize(_ object: Any) -> URL? { 213 | if let object = object as? URL { 214 | return object 215 | } 216 | 217 | if let object = object as? Data { 218 | return NSKeyedUnarchiver.unarchiveObject(with: object) as? URL 219 | } 220 | 221 | if let object = object as? NSString { 222 | let path = object.expandingTildeInPath 223 | return URL(fileURLWithPath: path) 224 | } 225 | 226 | return nil 227 | } 228 | } 229 | 230 | public struct DefaultsCodableBridge: DefaultsBridge { 231 | 232 | public init() {} 233 | 234 | public func save(key: String, value: T?, userDefaults: UserDefaults) { 235 | guard let value = value else { 236 | userDefaults.removeObject(forKey: key) 237 | return 238 | } 239 | userDefaults.set(encodable: value, forKey: key) 240 | } 241 | 242 | public func get(key: String, userDefaults: UserDefaults) -> T? { 243 | guard let data = userDefaults.data(forKey: key) else { 244 | return nil 245 | } 246 | return deserialize(data) 247 | } 248 | 249 | public func deserialize(_ object: Any) -> T? { 250 | guard let data = object as? Data else { return nil } 251 | 252 | return try? JSONDecoder().decode(T.self, from: data) 253 | } 254 | } 255 | 256 | public struct DefaultsKeyedArchiverBridge: DefaultsBridge { 257 | 258 | public init() {} 259 | 260 | public func save(key: String, value: T?, userDefaults: UserDefaults) { 261 | guard let value = value else { 262 | userDefaults.removeObject(forKey: key) 263 | return 264 | } 265 | // Needed because Quick/Nimble have min target 10.10... 266 | if #available(OSX 10.11, *) { 267 | userDefaults.set(NSKeyedArchiver.archivedData(withRootObject: value), forKey: key) 268 | } else { 269 | fatalError("Shouldn't really happen. We do not support macOS 10.10, if it happened to you please report your use-case on GitHub issues.") 270 | } 271 | } 272 | 273 | public func get(key: String, userDefaults: UserDefaults) -> T? { 274 | guard let data = userDefaults.data(forKey: key) else { 275 | return nil 276 | } 277 | return deserialize(data) 278 | } 279 | 280 | public func deserialize(_ object: Any) -> T? { 281 | guard let data = object as? Data else { return nil } 282 | return NSKeyedUnarchiver.unarchiveObject(with: data) as? T 283 | } 284 | } 285 | 286 | public struct DefaultsRawRepresentableBridge: DefaultsBridge { 287 | 288 | public init() {} 289 | 290 | public func save(key: String, value: T?, userDefaults: UserDefaults) { 291 | userDefaults.set(value?.rawValue, forKey: key) 292 | } 293 | 294 | public func get(key: String, userDefaults: UserDefaults) -> T? { 295 | guard let object = userDefaults.object(forKey: key) else { return nil } 296 | return deserialize(object) 297 | } 298 | 299 | public func deserialize(_ object: Any) -> T? { 300 | guard let rawValue = object as? T.RawValue else { return nil } 301 | return T(rawValue: rawValue) 302 | } 303 | } 304 | 305 | public struct DefaultsRawRepresentableArrayBridge: DefaultsBridge where T.Element: RawRepresentable { 306 | 307 | public init() {} 308 | 309 | public func save(key: String, value: T?, userDefaults: UserDefaults) { 310 | let raw = value?.map { $0.rawValue } 311 | userDefaults.set(raw, forKey: key) 312 | } 313 | 314 | public func get(key: String, userDefaults: UserDefaults) -> T? { 315 | guard let object = userDefaults.array(forKey: key) else { return nil } 316 | return deserialize(object) 317 | } 318 | 319 | public func deserialize(_ object: Any) -> T? { 320 | guard let rawValue = object as? [T.Element.RawValue] else { return nil } 321 | return rawValue.compactMap { T.Element(rawValue: $0) } as? T 322 | } 323 | } 324 | 325 | public struct DefaultsOptionalBridge: DefaultsBridge { 326 | 327 | public typealias T = Bridge.T? 328 | 329 | private let bridge: Bridge 330 | 331 | init(bridge: Bridge) { 332 | self.bridge = bridge 333 | } 334 | 335 | public func get(key: String, userDefaults: UserDefaults) -> T? { 336 | return bridge.get(key: key, userDefaults: userDefaults) 337 | } 338 | 339 | public func save(key: String, value: T?, userDefaults: UserDefaults) { 340 | bridge.save(key: key, value: value as? Bridge.T, userDefaults: userDefaults) 341 | } 342 | 343 | public func deserialize(_ object: Any) -> T? { 344 | return bridge.deserialize(object) 345 | } 346 | } 347 | 348 | public struct DefaultsOptionalArrayBridge: DefaultsBridge where Bridge.T: Collection { 349 | 350 | public typealias T = Bridge.T? 351 | 352 | private let bridge: Bridge 353 | 354 | init(bridge: Bridge) { 355 | self.bridge = bridge 356 | } 357 | 358 | public func get(key: String, userDefaults: UserDefaults) -> T? { 359 | return bridge.get(key: key, userDefaults: userDefaults) 360 | } 361 | 362 | public func save(key: String, value: T?, userDefaults: UserDefaults) { 363 | bridge.save(key: key, value: value as? Bridge.T, userDefaults: userDefaults) 364 | } 365 | 366 | public func deserialize(_ object: Any) -> T? { 367 | return bridge.deserialize(object) 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /Sources/DefaultsKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | // MARK: - Static keys 28 | 29 | /// Specialize with value type 30 | /// and pass key name to the initializer to create a key. 31 | public struct DefaultsKey { 32 | 33 | public let _key: String 34 | public let defaultValue: ValueType.T? 35 | internal var isOptional: Bool 36 | 37 | public init(_ key: String, defaultValue: ValueType.T) { 38 | self._key = key 39 | self.defaultValue = defaultValue 40 | self.isOptional = false 41 | } 42 | 43 | // Couldn't figure out a way of how to pass a nil/none value from extension, thus this initializer. 44 | // Used for creating an optional key (without defaultValue) 45 | private init(key: String) { 46 | self._key = key 47 | self.defaultValue = nil 48 | self.isOptional = true 49 | } 50 | 51 | @available(*, unavailable, message: "This key needs a `defaultValue` parameter. If this type does not have a default value, consider using an optional key.") 52 | public init(_ key: String) { 53 | fatalError() 54 | } 55 | } 56 | 57 | public extension DefaultsKey where ValueType: DefaultsSerializable, ValueType: OptionalType, ValueType.Wrapped: DefaultsSerializable { 58 | 59 | init(_ key: String) { 60 | self.init(key: key) 61 | } 62 | 63 | init(_ key: String, defaultValue: ValueType.T) { 64 | self._key = key 65 | self.defaultValue = defaultValue 66 | self.isOptional = true 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Sources/DefaultsKeys.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | public protocol DefaultsKeyStore {} 28 | 29 | public struct DefaultsKeys: DefaultsKeyStore { 30 | public init() {} 31 | } 32 | -------------------------------------------------------------------------------- /Sources/DefaultsObserver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | public protocol DefaultsDisposable { 28 | func dispose() 29 | } 30 | 31 | #if !os(Linux) 32 | 33 | public final class DefaultsObserver: NSObject, DefaultsDisposable where T == T.T { 34 | 35 | public struct Update { 36 | public let kind: NSKeyValueChange 37 | public let indexes: IndexSet? 38 | public let isPrior: Bool 39 | public let newValue: T.T? 40 | public let oldValue: T.T? 41 | 42 | init(dict: [NSKeyValueChangeKey: Any], key: DefaultsKey) { 43 | // swiftlint:disable:next force_cast 44 | kind = NSKeyValueChange(rawValue: dict[.kindKey] as! UInt)! 45 | indexes = dict[.indexesKey] as? IndexSet 46 | isPrior = dict[.notificationIsPriorKey] as? Bool ?? false 47 | oldValue = Update.deserialize(dict[.oldKey], for: key) ?? key.defaultValue 48 | newValue = Update.deserialize(dict[.newKey], for: key) ?? key.defaultValue 49 | } 50 | 51 | private static func deserialize(_ value: Any?, for key: DefaultsKey) -> T.T? where T.T == T { 52 | guard let value = value else { return nil } 53 | 54 | let deserialized = T._defaults.deserialize(value) 55 | 56 | let ret: T.T? 57 | if key.isOptional, let _deserialized = deserialized, let __deserialized = _deserialized as? OptionalTypeCheck, !__deserialized.isNil { 58 | ret = __deserialized as? T.T 59 | } else if !key.isOptional { 60 | ret = deserialized ?? value as? T.T 61 | } else { 62 | ret = value as? T.T 63 | } 64 | 65 | return ret 66 | } 67 | } 68 | 69 | private let key: DefaultsKey 70 | private let userDefaults: UserDefaults 71 | private let handler: ((Update) -> Void) 72 | private var didRemoveObserver = false 73 | 74 | init(key: DefaultsKey, userDefaults: UserDefaults, options: NSKeyValueObservingOptions, handler: @escaping ((Update) -> Void)) { 75 | self.key = key 76 | self.userDefaults = userDefaults 77 | self.handler = handler 78 | super.init() 79 | 80 | userDefaults.addObserver(self, forKeyPath: key._key, options: options, context: nil) 81 | } 82 | 83 | deinit { 84 | dispose() 85 | } 86 | 87 | // swiftlint:disable:next block_based_kvo 88 | public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { 89 | guard let change = change, object != nil, keyPath == key._key else { 90 | return 91 | } 92 | 93 | let update = Update(dict: change, key: key) 94 | handler(update) 95 | } 96 | 97 | public func dispose() { 98 | // We use this local property because when you use `removeObserver` when you are 99 | // not actually observing anymore, you'll receive a runtime error. 100 | if didRemoveObserver { return } 101 | 102 | didRemoveObserver = true 103 | userDefaults.removeObserver(self, forKeyPath: key._key, context: nil) 104 | } 105 | } 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /Sources/DefaultsSerializable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | public protocol DefaultsSerializable { 28 | // swiftlint:disable:next type_name 29 | typealias T = Bridge.T 30 | associatedtype Bridge: DefaultsBridge 31 | associatedtype ArrayBridge: DefaultsBridge 32 | 33 | static var _defaults: Bridge { get } 34 | static var _defaultsArray: ArrayBridge { get } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Sources/OptionalType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | protocol OptionalTypeCheck { 26 | var isNil: Bool { get } 27 | } 28 | 29 | public protocol OptionalType { 30 | associatedtype Wrapped 31 | 32 | static var __swifty_empty: Self { get } 33 | } 34 | 35 | extension Optional: OptionalType, OptionalTypeCheck { 36 | public static var __swifty_empty: Optional { 37 | return nil 38 | } 39 | 40 | var isNil: Bool { 41 | return self == nil 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/PropertyWrappers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | #if swift(>=5.1) 26 | public struct SwiftyUserDefaultOptions: OptionSet { 27 | 28 | public static let cached = SwiftyUserDefaultOptions(rawValue: 1 << 0) 29 | public static let observed = SwiftyUserDefaultOptions(rawValue: 1 << 2) 30 | 31 | public let rawValue: Int 32 | 33 | public init(rawValue: Int) { 34 | self.rawValue = rawValue 35 | } 36 | } 37 | 38 | @propertyWrapper 39 | public final class SwiftyUserDefault where T.T == T { 40 | 41 | public let key: DefaultsKey 42 | public let options: SwiftyUserDefaultOptions 43 | 44 | public var wrappedValue: T { 45 | get { 46 | if options.contains(.cached) { 47 | return _value ?? Defaults[key: key] 48 | } else { 49 | return Defaults[key: key] 50 | } 51 | } 52 | set { 53 | _value = newValue 54 | Defaults[key: key] = newValue 55 | } 56 | } 57 | 58 | private var _value: T.T? 59 | private var observation: DefaultsDisposable? 60 | 61 | public init(keyPath: KeyPath>, adapter: DefaultsAdapter, options: SwiftyUserDefaultOptions = []) { 62 | self.key = adapter.keyStore[keyPath: keyPath] 63 | self.options = options 64 | 65 | if options.contains(.observed) { 66 | observation = adapter.observe(key) { [weak self] update in 67 | self?._value = update.newValue 68 | } 69 | } 70 | } 71 | 72 | public init(keyPath: KeyPath>, options: SwiftyUserDefaultOptions = []) { 73 | self.key = Defaults.keyStore[keyPath: keyPath] 74 | self.options = options 75 | 76 | if options.contains(.observed) { 77 | observation = Defaults.observe(key) { [weak self] update in 78 | self?._value = update.newValue 79 | } 80 | } 81 | } 82 | 83 | deinit { 84 | observation?.dispose() 85 | } 86 | } 87 | #endif 88 | -------------------------------------------------------------------------------- /Sources/SwiftyUserDefaults.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | FOUNDATION_EXPORT double SwiftyUserDefaultsVersionNumber; 4 | FOUNDATION_EXPORT const unsigned char SwiftyUserDefaultsVersionString[]; 5 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'SwiftyUserDefaults' 3 | s.version = '5.3.0' 4 | s.license = 'MIT' 5 | s.summary = 'Swifty API for UserDefaults' 6 | s.homepage = 'https://github.com/sunshinejr/SwiftyUserDefaults' 7 | s.authors = { 'Radek Pietruszewski' => 'this.is@radex.io', 'Łukasz Mróz' => 'thesunshinejr@gmail.com' } 8 | s.source = { :git => 'https://github.com/radex/SwiftyUserDefaults.git', :tag => s.version } 9 | 10 | s.requires_arc = true 11 | s.ios.deployment_target = '9.0' 12 | s.osx.deployment_target = '10.11' 13 | s.tvos.deployment_target = '9.0' 14 | s.watchos.deployment_target = '2.0' 15 | 16 | if s.respond_to? 'swift_version' 17 | s.swift_version = "4.2" 18 | end 19 | if s.respond_to? 'swift_versions' 20 | s.swift_versions = ['4.1', '4.2', '5.0', '5.1'] 21 | end 22 | s.cocoapods_version = '>= 1.4.0' 23 | 24 | s.source_files = 'Sources/*.swift' 25 | end 26 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.xcodeproj/Nimble_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.xcodeproj/QuickSpecBase_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.xcodeproj/Quick_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.xcodeproj/SwiftyUserDefaultsTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.xcodeproj/SwiftyUserDefaults_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 037280C1C6C68BDB8E3759E6 /* DefaultsKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 706E690EF2E4010781480303 /* DefaultsKey.swift */; }; 11 | 0E71124AE86362E623F9C4DA /* Defaults+FrogCustomSerializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F86AA1AB9F6F553C03DBF0F9 /* Defaults+FrogCustomSerializable.swift */; }; 12 | 171A4CE31F41418FF55DA46D /* TestHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0EE0402DB7DB637D79FEC2B /* TestHelper.swift */; }; 13 | 18B46EAC09475D8404C7DF3A /* DefaultsSerializableSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EFFE22277FA6C163E34F4D2 /* DefaultsSerializableSpec.swift */; }; 14 | 1C21A8A584B33AF419FCE1A5 /* OptionalType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85096B65E07A3BC7F856F0D1 /* OptionalType.swift */; }; 15 | 2410BBF7DE7518FD5DD73BB4 /* Defaults+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = A574FF49CBCEBCCEAE9BD42E /* Defaults+String.swift */; }; 16 | 30366F181855444010BCC954 /* Defaults+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = F15D6CCB785C552A0B39B16A /* Defaults+Color.swift */; }; 17 | 311F24C3509BE924078AD67E /* DefaultsObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9701991125BC1345631E831E /* DefaultsObserver.swift */; }; 18 | 499B0191487C22E03028ED3C /* Defaults+Int.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041FE3F6B58F219CAAE0AD64 /* Defaults+Int.swift */; }; 19 | 4B604921053C4F31217DCE9E /* Defaults+Observing.swift in Sources */ = {isa = PBXBuildFile; fileRef = F910BE3FABE99289E0545D95 /* Defaults+Observing.swift */; }; 20 | 7EE5BB8911DFD47B6D6ED8E5 /* Defaults+Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F97C9DDE34857798EA6519B /* Defaults+Date.swift */; }; 21 | 80C1C495919531D69B34BA6F /* Defaults+Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78703A0F180A4B7C450CD191 /* Defaults+Data.swift */; }; 22 | 858E15D0231FEC2F00DC1418 /* PropertyWrappers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858E15CF231FEC2F00DC1418 /* PropertyWrappers.swift */; }; 23 | 88573499C0B9C4F466D7EC2D /* DefaultsSerializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F98C7EF97445788836162CD0 /* DefaultsSerializable.swift */; }; 24 | 89AF35E8D3CF0B18777540CC /* Defaults+Bool.swift in Sources */ = {isa = PBXBuildFile; fileRef = E57735ECA0C2F413E8EBC2E4 /* Defaults+Bool.swift */; }; 25 | 8ED3B92CC68C3F2B7448D868 /* Defaults+FrogCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBE434F1CEE20652BC414284 /* Defaults+FrogCodable.swift */; }; 26 | 9092B7096C074A8FE5B1324B /* UserDefaults+PropertyList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63C197A55B797D98D9603C98 /* UserDefaults+PropertyList.swift */; }; 27 | 965E729E5D4814BDFEC42A97 /* Defaults+FrogSerializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 161E961FF7D84853F6369DEE /* Defaults+FrogSerializable.swift */; }; 28 | 9FA427F1D84703CE8EC6CD38 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE155E06F856DFEF78FD63F7 /* Defaults.swift */; }; 29 | A3CAE6734DDE8FD952CE2CF2 /* Defaults+Subscripts.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF92C9F82ABF7807D4D4DDDD /* Defaults+Subscripts.swift */; }; 30 | ACE14E08E7B3EECD55B932B3 /* Defaults+Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D8980553F1615D4ACB4AA43 /* Defaults+Dictionary.swift */; }; 31 | B31C4E974609611EF7360E51 /* Defaults+Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8790CE5B53F91DAD51975EDC /* Defaults+Double.swift */; }; 32 | C1EF5E679553F49B4C163561 /* SwiftyUserDefaults.h in Headers */ = {isa = PBXBuildFile; fileRef = B134969522555B348C06FD6C /* SwiftyUserDefaults.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33 | CC4CF6A63F6AA1B932D6757E /* DefaultsBridges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20EBA39F1E25782FDC1CFC36 /* DefaultsBridges.swift */; }; 34 | D102C1011A4314AFF773DCD5 /* Defaults+StringToBool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11FDEA0BD7078D2633741F60 /* Defaults+StringToBool.swift */; }; 35 | EA37551964DBE6344BC4178A /* Defaults+URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6494A812C2386E7351405E72 /* Defaults+URL.swift */; }; 36 | ED39909122B0204E0046F502 /* DefaultsKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED39909022B0204E0046F502 /* DefaultsKeys.swift */; }; 37 | ED9D95BF22AD74630006FE67 /* DefaultsAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED9D95BD22AD74630006FE67 /* DefaultsAdapter.swift */; }; 38 | F0340276AF034C710E8C81A7 /* BuiltIns.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB55DC75BF69CEA7DB03750E /* BuiltIns.swift */; }; 39 | F73433D64DE45C8219A51BED /* Defaults+BestFroggiesEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A5BEEC51B2A01157F9965F /* Defaults+BestFroggiesEnum.swift */; }; 40 | /* End PBXBuildFile section */ 41 | 42 | /* Begin PBXContainerItemProxy section */ 43 | 37EED1FB2EAE89DAEE25751E /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = 311AD9CED9DA95F1D1E5D269 /* Project object */; 46 | proxyType = 1; 47 | remoteGlobalIDString = 61A78F3463F2610F6773A31E; 48 | remoteInfo = SwiftyUserDefaults; 49 | }; 50 | /* End PBXContainerItemProxy section */ 51 | 52 | /* Begin PBXFileReference section */ 53 | 041FE3F6B58F219CAAE0AD64 /* Defaults+Int.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+Int.swift"; path = "Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+Int.swift"; sourceTree = ""; }; 54 | 11FDEA0BD7078D2633741F60 /* Defaults+StringToBool.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+StringToBool.swift"; path = "Sources/Defaults+StringToBool.swift"; sourceTree = ""; }; 55 | 161E961FF7D84853F6369DEE /* Defaults+FrogSerializable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+FrogSerializable.swift"; path = "Tests/SwiftyUserDefaultsTests/External types/Defaults+FrogSerializable.swift"; sourceTree = ""; }; 56 | 1A9683391DA938C5FBDD29A7 /* SwiftyUserDefaults.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftyUserDefaults.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 20EBA39F1E25782FDC1CFC36 /* DefaultsBridges.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DefaultsBridges.swift; path = Sources/DefaultsBridges.swift; sourceTree = ""; }; 58 | 2EFFE22277FA6C163E34F4D2 /* DefaultsSerializableSpec.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DefaultsSerializableSpec.swift; path = Tests/SwiftyUserDefaultsTests/TestHelpers/DefaultsSerializableSpec.swift; sourceTree = ""; }; 59 | 2F97C9DDE34857798EA6519B /* Defaults+Date.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+Date.swift"; path = "Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+Date.swift"; sourceTree = ""; }; 60 | 3D8980553F1615D4ACB4AA43 /* Defaults+Dictionary.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+Dictionary.swift"; path = "Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+Dictionary.swift"; sourceTree = ""; }; 61 | 63C197A55B797D98D9603C98 /* UserDefaults+PropertyList.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "UserDefaults+PropertyList.swift"; path = "Tests/SwiftyUserDefaultsTests/TestHelpers/UserDefaults+PropertyList.swift"; sourceTree = ""; }; 62 | 6494A812C2386E7351405E72 /* Defaults+URL.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+URL.swift"; path = "Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+URL.swift"; sourceTree = ""; }; 63 | 6C48B95D407E4F720507F3D8 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = Info.plist; path = Sources/Info.plist; sourceTree = ""; }; 64 | 706E690EF2E4010781480303 /* DefaultsKey.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DefaultsKey.swift; path = Sources/DefaultsKey.swift; sourceTree = ""; }; 65 | 78703A0F180A4B7C450CD191 /* Defaults+Data.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+Data.swift"; path = "Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+Data.swift"; sourceTree = ""; }; 66 | 85096B65E07A3BC7F856F0D1 /* OptionalType.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OptionalType.swift; path = Sources/OptionalType.swift; sourceTree = ""; }; 67 | 858E15CF231FEC2F00DC1418 /* PropertyWrappers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PropertyWrappers.swift; path = Sources/PropertyWrappers.swift; sourceTree = ""; }; 68 | 8790CE5B53F91DAD51975EDC /* Defaults+Double.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+Double.swift"; path = "Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+Double.swift"; sourceTree = ""; }; 69 | 9701991125BC1345631E831E /* DefaultsObserver.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DefaultsObserver.swift; path = Sources/DefaultsObserver.swift; sourceTree = ""; }; 70 | A574FF49CBCEBCCEAE9BD42E /* Defaults+String.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+String.swift"; path = "Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+String.swift"; sourceTree = ""; }; 71 | A5A5BEEC51B2A01157F9965F /* Defaults+BestFroggiesEnum.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+BestFroggiesEnum.swift"; path = "Tests/SwiftyUserDefaultsTests/External types/Defaults+BestFroggiesEnum.swift"; sourceTree = ""; }; 72 | B134969522555B348C06FD6C /* SwiftyUserDefaults.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = SwiftyUserDefaults.h; path = Sources/SwiftyUserDefaults.h; sourceTree = ""; }; 73 | BF92C9F82ABF7807D4D4DDDD /* Defaults+Subscripts.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+Subscripts.swift"; path = "Sources/Defaults+Subscripts.swift"; sourceTree = ""; }; 74 | CB55DC75BF69CEA7DB03750E /* BuiltIns.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = BuiltIns.swift; path = Sources/BuiltIns.swift; sourceTree = ""; }; 75 | CBE434F1CEE20652BC414284 /* Defaults+FrogCodable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+FrogCodable.swift"; path = "Tests/SwiftyUserDefaultsTests/External types/Defaults+FrogCodable.swift"; sourceTree = ""; }; 76 | D0EE0402DB7DB637D79FEC2B /* TestHelper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = TestHelper.swift; path = Tests/SwiftyUserDefaultsTests/TestHelpers/TestHelper.swift; sourceTree = ""; }; 77 | E57735ECA0C2F413E8EBC2E4 /* Defaults+Bool.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+Bool.swift"; path = "Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+Bool.swift"; sourceTree = ""; }; 78 | ED39909022B0204E0046F502 /* DefaultsKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DefaultsKeys.swift; path = Sources/DefaultsKeys.swift; sourceTree = ""; }; 79 | ED9D95BD22AD74630006FE67 /* DefaultsAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DefaultsAdapter.swift; path = Sources/DefaultsAdapter.swift; sourceTree = ""; }; 80 | EE155E06F856DFEF78FD63F7 /* Defaults.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Defaults.swift; path = Sources/Defaults.swift; sourceTree = ""; }; 81 | F067CFEA88F84E726E835422 /* SwiftyUserDefaultsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftyUserDefaultsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 82 | F15D6CCB785C552A0B39B16A /* Defaults+Color.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+Color.swift"; path = "Tests/SwiftyUserDefaultsTests/External types/Defaults+Color.swift"; sourceTree = ""; }; 83 | F27CAFFE349FD36596D02780 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = Info.plist; path = Tests/Info.plist; sourceTree = ""; }; 84 | F86AA1AB9F6F553C03DBF0F9 /* Defaults+FrogCustomSerializable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+FrogCustomSerializable.swift"; path = "Tests/SwiftyUserDefaultsTests/External types/Defaults+FrogCustomSerializable.swift"; sourceTree = ""; }; 85 | F910BE3FABE99289E0545D95 /* Defaults+Observing.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "Defaults+Observing.swift"; path = "Sources/Defaults+Observing.swift"; sourceTree = ""; }; 86 | F98C7EF97445788836162CD0 /* DefaultsSerializable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DefaultsSerializable.swift; path = Sources/DefaultsSerializable.swift; sourceTree = ""; }; 87 | /* End PBXFileReference section */ 88 | 89 | /* Begin PBXFrameworksBuildPhase section */ 90 | 45BA314EA94FF6F031F55232 /* Frameworks */ = { 91 | isa = PBXFrameworksBuildPhase; 92 | buildActionMask = 2147483647; 93 | files = ( 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | 4BD71FF395A0DF4F8F256DDA /* Frameworks */ = { 98 | isa = PBXFrameworksBuildPhase; 99 | buildActionMask = 2147483647; 100 | files = ( 101 | ); 102 | runOnlyForDeploymentPostprocessing = 0; 103 | }; 104 | /* End PBXFrameworksBuildPhase section */ 105 | 106 | /* Begin PBXGroup section */ 107 | 030EF47B968BC320EF6F10A3 /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 1A9683391DA938C5FBDD29A7 /* SwiftyUserDefaults.framework */, 111 | F067CFEA88F84E726E835422 /* SwiftyUserDefaultsTests.xctest */, 112 | ); 113 | name = Products; 114 | sourceTree = ""; 115 | }; 116 | 1A0E1BA11455E9E44F192EAA /* Sources */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | CB55DC75BF69CEA7DB03750E /* BuiltIns.swift */, 120 | EE155E06F856DFEF78FD63F7 /* Defaults.swift */, 121 | F910BE3FABE99289E0545D95 /* Defaults+Observing.swift */, 122 | 11FDEA0BD7078D2633741F60 /* Defaults+StringToBool.swift */, 123 | BF92C9F82ABF7807D4D4DDDD /* Defaults+Subscripts.swift */, 124 | ED9D95BD22AD74630006FE67 /* DefaultsAdapter.swift */, 125 | 20EBA39F1E25782FDC1CFC36 /* DefaultsBridges.swift */, 126 | 706E690EF2E4010781480303 /* DefaultsKey.swift */, 127 | ED39909022B0204E0046F502 /* DefaultsKeys.swift */, 128 | 9701991125BC1345631E831E /* DefaultsObserver.swift */, 129 | F98C7EF97445788836162CD0 /* DefaultsSerializable.swift */, 130 | 6C48B95D407E4F720507F3D8 /* Info.plist */, 131 | 85096B65E07A3BC7F856F0D1 /* OptionalType.swift */, 132 | 858E15CF231FEC2F00DC1418 /* PropertyWrappers.swift */, 133 | B134969522555B348C06FD6C /* SwiftyUserDefaults.h */, 134 | ); 135 | name = Sources; 136 | sourceTree = ""; 137 | }; 138 | 2C65AD74F5B4ACA76F7FCA8E /* SwiftyUserDefaultsTests */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 5BF9A4C7B374362259AAD1B0 /* Built-ins */, 142 | FDEC308DFF94442CB5C60B0F /* TestHelpers */, 143 | 34D4FD3AE00280F32A6FD6B1 /* External types */, 144 | ); 145 | name = SwiftyUserDefaultsTests; 146 | sourceTree = ""; 147 | }; 148 | 34D4FD3AE00280F32A6FD6B1 /* External types */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | F86AA1AB9F6F553C03DBF0F9 /* Defaults+FrogCustomSerializable.swift */, 152 | CBE434F1CEE20652BC414284 /* Defaults+FrogCodable.swift */, 153 | 161E961FF7D84853F6369DEE /* Defaults+FrogSerializable.swift */, 154 | F15D6CCB785C552A0B39B16A /* Defaults+Color.swift */, 155 | A5A5BEEC51B2A01157F9965F /* Defaults+BestFroggiesEnum.swift */, 156 | ); 157 | name = "External types"; 158 | sourceTree = ""; 159 | }; 160 | 360E42BFF4D0B60A2966BBFB = { 161 | isa = PBXGroup; 162 | children = ( 163 | 030EF47B968BC320EF6F10A3 /* Products */, 164 | ECEEA08FA9788F3518C84E77 /* Frameworks */, 165 | 1A0E1BA11455E9E44F192EAA /* Sources */, 166 | C14BA19AA073B0F4B6ED9A3D /* Tests */, 167 | ); 168 | sourceTree = ""; 169 | }; 170 | 5BF9A4C7B374362259AAD1B0 /* Built-ins */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 6494A812C2386E7351405E72 /* Defaults+URL.swift */, 174 | 041FE3F6B58F219CAAE0AD64 /* Defaults+Int.swift */, 175 | E57735ECA0C2F413E8EBC2E4 /* Defaults+Bool.swift */, 176 | 8790CE5B53F91DAD51975EDC /* Defaults+Double.swift */, 177 | 3D8980553F1615D4ACB4AA43 /* Defaults+Dictionary.swift */, 178 | 78703A0F180A4B7C450CD191 /* Defaults+Data.swift */, 179 | 2F97C9DDE34857798EA6519B /* Defaults+Date.swift */, 180 | A574FF49CBCEBCCEAE9BD42E /* Defaults+String.swift */, 181 | ); 182 | name = "Built-ins"; 183 | sourceTree = ""; 184 | }; 185 | C14BA19AA073B0F4B6ED9A3D /* Tests */ = { 186 | isa = PBXGroup; 187 | children = ( 188 | F27CAFFE349FD36596D02780 /* Info.plist */, 189 | 2C65AD74F5B4ACA76F7FCA8E /* SwiftyUserDefaultsTests */, 190 | ); 191 | name = Tests; 192 | sourceTree = ""; 193 | }; 194 | ECEEA08FA9788F3518C84E77 /* Frameworks */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | ); 198 | name = Frameworks; 199 | sourceTree = ""; 200 | }; 201 | FDEC308DFF94442CB5C60B0F /* TestHelpers */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | D0EE0402DB7DB637D79FEC2B /* TestHelper.swift */, 205 | 2EFFE22277FA6C163E34F4D2 /* DefaultsSerializableSpec.swift */, 206 | 63C197A55B797D98D9603C98 /* UserDefaults+PropertyList.swift */, 207 | ); 208 | name = TestHelpers; 209 | sourceTree = ""; 210 | }; 211 | /* End PBXGroup section */ 212 | 213 | /* Begin PBXHeadersBuildPhase section */ 214 | F92BDABE668F3B65E7201232 /* Headers */ = { 215 | isa = PBXHeadersBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | C1EF5E679553F49B4C163561 /* SwiftyUserDefaults.h in Headers */, 219 | ); 220 | runOnlyForDeploymentPostprocessing = 0; 221 | }; 222 | /* End PBXHeadersBuildPhase section */ 223 | 224 | /* Begin PBXNativeTarget section */ 225 | 61A78F3463F2610F6773A31E /* SwiftyUserDefaults */ = { 226 | isa = PBXNativeTarget; 227 | buildConfigurationList = DD6606403CD62287E39DEABD /* Build configuration list for PBXNativeTarget "SwiftyUserDefaults" */; 228 | buildPhases = ( 229 | F92BDABE668F3B65E7201232 /* Headers */, 230 | B5A60BC0CC05B1F0610A3621 /* Sources */, 231 | 94C7D032B09810AD9B2C7A98 /* Resources */, 232 | 4BD71FF395A0DF4F8F256DDA /* Frameworks */, 233 | ); 234 | buildRules = ( 235 | ); 236 | dependencies = ( 237 | ); 238 | name = SwiftyUserDefaults; 239 | productName = SwiftyUserDefaults; 240 | productReference = 1A9683391DA938C5FBDD29A7 /* SwiftyUserDefaults.framework */; 241 | productType = "com.apple.product-type.framework"; 242 | }; 243 | B72688BB7DC2421580BAD9EF /* SwiftyUserDefaultsTests */ = { 244 | isa = PBXNativeTarget; 245 | buildConfigurationList = A6B5E4AD6295A8E8C20F6BEE /* Build configuration list for PBXNativeTarget "SwiftyUserDefaultsTests" */; 246 | buildPhases = ( 247 | 16A9BB4A74FF81FB0107A54A /* Sources */, 248 | 2EAA390C62DA2CDF229CD439 /* Resources */, 249 | 45BA314EA94FF6F031F55232 /* Frameworks */, 250 | EBC5C4E999341A1EF568F193 /* Copy Carthage Frameworks */, 251 | ); 252 | buildRules = ( 253 | ); 254 | dependencies = ( 255 | 4686E7C751F7B20D8E067DAB /* PBXTargetDependency */, 256 | ); 257 | name = SwiftyUserDefaultsTests; 258 | productName = SwiftyUserDefaultsTests; 259 | productReference = F067CFEA88F84E726E835422 /* SwiftyUserDefaultsTests.xctest */; 260 | productType = "com.apple.product-type.bundle.unit-test"; 261 | }; 262 | /* End PBXNativeTarget section */ 263 | 264 | /* Begin PBXProject section */ 265 | 311AD9CED9DA95F1D1E5D269 /* Project object */ = { 266 | isa = PBXProject; 267 | attributes = { 268 | LastSwiftUpdateCheck = 0930; 269 | LastUpgradeCheck = 1020; 270 | }; 271 | buildConfigurationList = E3B831884380EEF15D197700 /* Build configuration list for PBXProject "SwiftyUserDefaults" */; 272 | compatibilityVersion = "Xcode 3.2"; 273 | developmentRegion = en; 274 | hasScannedForEncodings = 0; 275 | knownRegions = ( 276 | English, 277 | en, 278 | Base, 279 | ); 280 | mainGroup = 360E42BFF4D0B60A2966BBFB; 281 | productRefGroup = 030EF47B968BC320EF6F10A3 /* Products */; 282 | projectDirPath = ""; 283 | projectRoot = ""; 284 | targets = ( 285 | 61A78F3463F2610F6773A31E /* SwiftyUserDefaults */, 286 | B72688BB7DC2421580BAD9EF /* SwiftyUserDefaultsTests */, 287 | ); 288 | }; 289 | /* End PBXProject section */ 290 | 291 | /* Begin PBXResourcesBuildPhase section */ 292 | 2EAA390C62DA2CDF229CD439 /* Resources */ = { 293 | isa = PBXResourcesBuildPhase; 294 | buildActionMask = 2147483647; 295 | files = ( 296 | ); 297 | runOnlyForDeploymentPostprocessing = 0; 298 | }; 299 | 94C7D032B09810AD9B2C7A98 /* Resources */ = { 300 | isa = PBXResourcesBuildPhase; 301 | buildActionMask = 2147483647; 302 | files = ( 303 | ); 304 | runOnlyForDeploymentPostprocessing = 0; 305 | }; 306 | /* End PBXResourcesBuildPhase section */ 307 | 308 | /* Begin PBXShellScriptBuildPhase section */ 309 | EBC5C4E999341A1EF568F193 /* Copy Carthage Frameworks */ = { 310 | isa = PBXShellScriptBuildPhase; 311 | buildActionMask = 2147483647; 312 | files = ( 313 | ); 314 | inputFileListPaths = ( 315 | ); 316 | inputPaths = ( 317 | Quick.framework, 318 | Nimble.framework, 319 | ); 320 | name = "Copy Carthage Frameworks"; 321 | outputFileListPaths = ( 322 | ); 323 | outputPaths = ( 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | shellPath = /bin/sh; 327 | shellScript = " exec \"${SRCROOT}/scripts/copy-carthage-frameworks.sh\""; 328 | }; 329 | /* End PBXShellScriptBuildPhase section */ 330 | 331 | /* Begin PBXSourcesBuildPhase section */ 332 | 16A9BB4A74FF81FB0107A54A /* Sources */ = { 333 | isa = PBXSourcesBuildPhase; 334 | buildActionMask = 2147483647; 335 | files = ( 336 | EA37551964DBE6344BC4178A /* Defaults+URL.swift in Sources */, 337 | 499B0191487C22E03028ED3C /* Defaults+Int.swift in Sources */, 338 | 89AF35E8D3CF0B18777540CC /* Defaults+Bool.swift in Sources */, 339 | B31C4E974609611EF7360E51 /* Defaults+Double.swift in Sources */, 340 | ACE14E08E7B3EECD55B932B3 /* Defaults+Dictionary.swift in Sources */, 341 | 80C1C495919531D69B34BA6F /* Defaults+Data.swift in Sources */, 342 | 7EE5BB8911DFD47B6D6ED8E5 /* Defaults+Date.swift in Sources */, 343 | 2410BBF7DE7518FD5DD73BB4 /* Defaults+String.swift in Sources */, 344 | 171A4CE31F41418FF55DA46D /* TestHelper.swift in Sources */, 345 | 18B46EAC09475D8404C7DF3A /* DefaultsSerializableSpec.swift in Sources */, 346 | 9092B7096C074A8FE5B1324B /* UserDefaults+PropertyList.swift in Sources */, 347 | 0E71124AE86362E623F9C4DA /* Defaults+FrogCustomSerializable.swift in Sources */, 348 | 8ED3B92CC68C3F2B7448D868 /* Defaults+FrogCodable.swift in Sources */, 349 | 965E729E5D4814BDFEC42A97 /* Defaults+FrogSerializable.swift in Sources */, 350 | 30366F181855444010BCC954 /* Defaults+Color.swift in Sources */, 351 | F73433D64DE45C8219A51BED /* Defaults+BestFroggiesEnum.swift in Sources */, 352 | ); 353 | runOnlyForDeploymentPostprocessing = 0; 354 | }; 355 | B5A60BC0CC05B1F0610A3621 /* Sources */ = { 356 | isa = PBXSourcesBuildPhase; 357 | buildActionMask = 2147483647; 358 | files = ( 359 | 858E15D0231FEC2F00DC1418 /* PropertyWrappers.swift in Sources */, 360 | D102C1011A4314AFF773DCD5 /* Defaults+StringToBool.swift in Sources */, 361 | 037280C1C6C68BDB8E3759E6 /* DefaultsKey.swift in Sources */, 362 | CC4CF6A63F6AA1B932D6757E /* DefaultsBridges.swift in Sources */, 363 | ED39909122B0204E0046F502 /* DefaultsKeys.swift in Sources */, 364 | A3CAE6734DDE8FD952CE2CF2 /* Defaults+Subscripts.swift in Sources */, 365 | 4B604921053C4F31217DCE9E /* Defaults+Observing.swift in Sources */, 366 | 311F24C3509BE924078AD67E /* DefaultsObserver.swift in Sources */, 367 | F0340276AF034C710E8C81A7 /* BuiltIns.swift in Sources */, 368 | 1C21A8A584B33AF419FCE1A5 /* OptionalType.swift in Sources */, 369 | ED9D95BF22AD74630006FE67 /* DefaultsAdapter.swift in Sources */, 370 | 88573499C0B9C4F466D7EC2D /* DefaultsSerializable.swift in Sources */, 371 | 9FA427F1D84703CE8EC6CD38 /* Defaults.swift in Sources */, 372 | ); 373 | runOnlyForDeploymentPostprocessing = 0; 374 | }; 375 | /* End PBXSourcesBuildPhase section */ 376 | 377 | /* Begin PBXTargetDependency section */ 378 | 4686E7C751F7B20D8E067DAB /* PBXTargetDependency */ = { 379 | isa = PBXTargetDependency; 380 | name = SwiftyUserDefaults; 381 | target = 61A78F3463F2610F6773A31E /* SwiftyUserDefaults */; 382 | targetProxy = 37EED1FB2EAE89DAEE25751E /* PBXContainerItemProxy */; 383 | }; 384 | /* End PBXTargetDependency section */ 385 | 386 | /* Begin XCBuildConfiguration section */ 387 | 0AEDEA65E982193E22A052C0 /* Release */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ALWAYS_SEARCH_USER_PATHS = NO; 391 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 392 | CLANG_ANALYZER_NONNULL = YES; 393 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 394 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 395 | CLANG_CXX_LIBRARY = "libc++"; 396 | CLANG_ENABLE_MODULES = YES; 397 | CLANG_ENABLE_OBJC_ARC = YES; 398 | CLANG_ENABLE_OBJC_WEAK = YES; 399 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 400 | CLANG_WARN_BOOL_CONVERSION = YES; 401 | CLANG_WARN_COMMA = YES; 402 | CLANG_WARN_CONSTANT_CONVERSION = YES; 403 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 404 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 405 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 406 | CLANG_WARN_EMPTY_BODY = YES; 407 | CLANG_WARN_ENUM_CONVERSION = YES; 408 | CLANG_WARN_INFINITE_RECURSION = YES; 409 | CLANG_WARN_INT_CONVERSION = YES; 410 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 411 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 412 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 413 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 414 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 415 | CLANG_WARN_STRICT_PROTOTYPES = YES; 416 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 417 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 418 | CLANG_WARN_UNREACHABLE_CODE = YES; 419 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 420 | CODE_SIGN_IDENTITY = ""; 421 | COMBINE_HIDPI_IMAGES = YES; 422 | COPY_PHASE_STRIP = NO; 423 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 424 | ENABLE_NS_ASSERTIONS = NO; 425 | ENABLE_STRICT_OBJC_MSGSEND = YES; 426 | GCC_C_LANGUAGE_STANDARD = gnu11; 427 | GCC_NO_COMMON_BLOCKS = YES; 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | MTL_ENABLE_DEBUG_INFO = NO; 435 | MTL_FAST_MATH = YES; 436 | PRODUCT_NAME = "$(TARGET_NAME)"; 437 | SDKROOT = macosx; 438 | SWIFT_COMPILATION_MODE = wholemodule; 439 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 440 | SWIFT_VERSION = 5.0; 441 | TARGETED_DEVICE_FAMILY = "1,2,3,4"; 442 | }; 443 | name = Release; 444 | }; 445 | 1B66BF1D9A81BB8C91CAD3BC /* Debug */ = { 446 | isa = XCBuildConfiguration; 447 | buildSettings = { 448 | ALWAYS_SEARCH_USER_PATHS = NO; 449 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 450 | CLANG_ANALYZER_NONNULL = YES; 451 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 452 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 453 | CLANG_CXX_LIBRARY = "libc++"; 454 | CLANG_ENABLE_MODULES = YES; 455 | CLANG_ENABLE_OBJC_ARC = YES; 456 | CLANG_ENABLE_OBJC_WEAK = YES; 457 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 458 | CLANG_WARN_BOOL_CONVERSION = YES; 459 | CLANG_WARN_COMMA = YES; 460 | CLANG_WARN_CONSTANT_CONVERSION = YES; 461 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 462 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 463 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 464 | CLANG_WARN_EMPTY_BODY = YES; 465 | CLANG_WARN_ENUM_CONVERSION = YES; 466 | CLANG_WARN_INFINITE_RECURSION = YES; 467 | CLANG_WARN_INT_CONVERSION = YES; 468 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 469 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 470 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 471 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 472 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 473 | CLANG_WARN_STRICT_PROTOTYPES = YES; 474 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 475 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 476 | CLANG_WARN_UNREACHABLE_CODE = YES; 477 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 478 | CODE_SIGN_IDENTITY = ""; 479 | COMBINE_HIDPI_IMAGES = YES; 480 | COPY_PHASE_STRIP = NO; 481 | DEBUG_INFORMATION_FORMAT = dwarf; 482 | ENABLE_STRICT_OBJC_MSGSEND = YES; 483 | ENABLE_TESTABILITY = YES; 484 | GCC_C_LANGUAGE_STANDARD = gnu11; 485 | GCC_DYNAMIC_NO_PIC = NO; 486 | GCC_NO_COMMON_BLOCKS = YES; 487 | GCC_OPTIMIZATION_LEVEL = 0; 488 | GCC_PREPROCESSOR_DEFINITIONS = ( 489 | "DEBUG=1", 490 | "$(inherited)", 491 | ); 492 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 493 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 494 | GCC_WARN_UNDECLARED_SELECTOR = YES; 495 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 496 | GCC_WARN_UNUSED_FUNCTION = YES; 497 | GCC_WARN_UNUSED_VARIABLE = YES; 498 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 499 | MTL_FAST_MATH = YES; 500 | ONLY_ACTIVE_ARCH = YES; 501 | PRODUCT_NAME = "$(TARGET_NAME)"; 502 | SDKROOT = macosx; 503 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 504 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 505 | SWIFT_VERSION = 5.0; 506 | TARGETED_DEVICE_FAMILY = "1,2,3,4"; 507 | }; 508 | name = Debug; 509 | }; 510 | 6025098483F60AD52B01FBF3 /* Release */ = { 511 | isa = XCBuildConfiguration; 512 | buildSettings = { 513 | APPLICATION_EXTENSION_API_ONLY = NO; 514 | "FRAMEWORK_SEARCH_PATHS[sdk=appletv*]" = ( 515 | "$(SRCROOT)/Carthage/Build/tvOS/", 516 | "$(inherited)", 517 | ); 518 | "FRAMEWORK_SEARCH_PATHS[sdk=iphone*]" = ( 519 | "$(SRCROOT)/Carthage/Build/iOS/", 520 | "$(inherited)", 521 | ); 522 | "FRAMEWORK_SEARCH_PATHS[sdk=macosx*]" = ( 523 | "$(SRCROOT)/Carthage/Build/Mac/", 524 | "$(inherited)", 525 | ); 526 | "FRAMEWORK_SEARCH_PATHS[sdk=watch*]" = ( 527 | "$(SRCROOT)/Carthage/Build/watchOS/", 528 | "$(inherited)", 529 | ); 530 | INFOPLIST_FILE = Tests/Info.plist; 531 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 532 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 533 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 534 | MACOSX_DEPLOYMENT_TARGET = 10.11; 535 | PRODUCT_BUNDLE_IDENTIFIER = io.radex.SwiftyUserDefaultsTests; 536 | SDKROOT = macosx; 537 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 538 | TVOS_DEPLOYMENT_TARGET = 9.0; 539 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 540 | }; 541 | name = Release; 542 | }; 543 | 72F2CF4A099C668400C88FFD /* Release */ = { 544 | isa = XCBuildConfiguration; 545 | buildSettings = { 546 | APPLICATION_EXTENSION_API_ONLY = YES; 547 | COMBINE_HIDPI_IMAGES = YES; 548 | CURRENT_PROJECT_VERSION = 1; 549 | DEFINES_MODULE = YES; 550 | DYLIB_COMPATIBILITY_VERSION = 1; 551 | DYLIB_CURRENT_VERSION = 1; 552 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 553 | FRAMEWORK_VERSION = A; 554 | INFOPLIST_FILE = Sources/Info.plist; 555 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 556 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 557 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 558 | MACOSX_DEPLOYMENT_TARGET = 10.11; 559 | PRODUCT_BUNDLE_IDENTIFIER = io.radex.SwiftyUserDefaults; 560 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 561 | SDKROOT = macosx; 562 | SKIP_INSTALL = YES; 563 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 564 | TVOS_DEPLOYMENT_TARGET = 9.0; 565 | VERSIONING_SYSTEM = "apple-generic"; 566 | VERSION_INFO_PREFIX = ""; 567 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 568 | }; 569 | name = Release; 570 | }; 571 | 81E09F09CF50B55F973D51D5 /* Debug */ = { 572 | isa = XCBuildConfiguration; 573 | buildSettings = { 574 | APPLICATION_EXTENSION_API_ONLY = YES; 575 | COMBINE_HIDPI_IMAGES = YES; 576 | CURRENT_PROJECT_VERSION = 1; 577 | DEFINES_MODULE = YES; 578 | DYLIB_COMPATIBILITY_VERSION = 1; 579 | DYLIB_CURRENT_VERSION = 1; 580 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 581 | FRAMEWORK_VERSION = A; 582 | INFOPLIST_FILE = Sources/Info.plist; 583 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 584 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 585 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 586 | MACOSX_DEPLOYMENT_TARGET = 10.11; 587 | PRODUCT_BUNDLE_IDENTIFIER = io.radex.SwiftyUserDefaults; 588 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 589 | SDKROOT = macosx; 590 | SKIP_INSTALL = YES; 591 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 592 | TVOS_DEPLOYMENT_TARGET = 9.0; 593 | VERSIONING_SYSTEM = "apple-generic"; 594 | VERSION_INFO_PREFIX = ""; 595 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 596 | }; 597 | name = Debug; 598 | }; 599 | BA853046EAB120F3D4679B3C /* Debug */ = { 600 | isa = XCBuildConfiguration; 601 | buildSettings = { 602 | APPLICATION_EXTENSION_API_ONLY = NO; 603 | "FRAMEWORK_SEARCH_PATHS[sdk=appletv*]" = ( 604 | "$(SRCROOT)/Carthage/Build/tvOS/", 605 | "$(inherited)", 606 | ); 607 | "FRAMEWORK_SEARCH_PATHS[sdk=iphone*]" = ( 608 | "$(SRCROOT)/Carthage/Build/iOS/", 609 | "$(inherited)", 610 | ); 611 | "FRAMEWORK_SEARCH_PATHS[sdk=macosx*]" = ( 612 | "$(SRCROOT)/Carthage/Build/Mac/", 613 | "$(inherited)", 614 | ); 615 | "FRAMEWORK_SEARCH_PATHS[sdk=watch*]" = ( 616 | "$(SRCROOT)/Carthage/Build/watchOS/", 617 | "$(inherited)", 618 | ); 619 | INFOPLIST_FILE = Tests/Info.plist; 620 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 621 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 622 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 623 | MACOSX_DEPLOYMENT_TARGET = 10.11; 624 | PRODUCT_BUNDLE_IDENTIFIER = io.radex.SwiftyUserDefaultsTests; 625 | SDKROOT = macosx; 626 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 627 | TVOS_DEPLOYMENT_TARGET = 9.0; 628 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 629 | }; 630 | name = Debug; 631 | }; 632 | /* End XCBuildConfiguration section */ 633 | 634 | /* Begin XCConfigurationList section */ 635 | A6B5E4AD6295A8E8C20F6BEE /* Build configuration list for PBXNativeTarget "SwiftyUserDefaultsTests" */ = { 636 | isa = XCConfigurationList; 637 | buildConfigurations = ( 638 | 6025098483F60AD52B01FBF3 /* Release */, 639 | BA853046EAB120F3D4679B3C /* Debug */, 640 | ); 641 | defaultConfigurationIsVisible = 0; 642 | defaultConfigurationName = Release; 643 | }; 644 | DD6606403CD62287E39DEABD /* Build configuration list for PBXNativeTarget "SwiftyUserDefaults" */ = { 645 | isa = XCConfigurationList; 646 | buildConfigurations = ( 647 | 72F2CF4A099C668400C88FFD /* Release */, 648 | 81E09F09CF50B55F973D51D5 /* Debug */, 649 | ); 650 | defaultConfigurationIsVisible = 0; 651 | defaultConfigurationName = Release; 652 | }; 653 | E3B831884380EEF15D197700 /* Build configuration list for PBXProject "SwiftyUserDefaults" */ = { 654 | isa = XCConfigurationList; 655 | buildConfigurations = ( 656 | 1B66BF1D9A81BB8C91CAD3BC /* Debug */, 657 | 0AEDEA65E982193E22A052C0 /* Release */, 658 | ); 659 | defaultConfigurationIsVisible = 0; 660 | defaultConfigurationName = Release; 661 | }; 662 | /* End XCConfigurationList section */ 663 | }; 664 | rootObject = 311AD9CED9DA95F1D1E5D269 /* Project object */; 665 | } 666 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.xcodeproj/xcshareddata/IDETemplateMacros.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FILEHEADER 6 | 7 | // SwiftyUserDefaults 8 | // 9 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in all 19 | // copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | 30 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.xcodeproj/xcshareddata/xcschemes/SwiftyUserDefaults-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.xcodeproj/xcshareddata/xcschemes/SwiftyUserDefaults.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /SwiftyUserDefaults.xcodeproj/xcshareddata/xcschemes/SwiftyUserDefaultsTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import XCTest 26 | import Quick 27 | 28 | @testable import SwiftyUserDefaultsTests 29 | 30 | QCKMain([ 31 | DefaultsBoolSpec.self, 32 | DefaultsDataSpec.self, 33 | DefaultsDateSpec.self, 34 | DefaultsDictionarySpec.self, 35 | DefaultsDoubleSpec.self, 36 | DefaultsIntSpec.self, 37 | DefaultsStringSpec.self, 38 | DefaultsUrlSpec.self, 39 | DefaultsBestFroggiesEnumSerializableSpec.self, 40 | DefaultsFrogCodableSpec.self, 41 | DefaultsFrogCustomSerializableSpec.self, 42 | DefaultsFrogSerializableSpec.self 43 | ]) 44 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - line_length 3 | - file_length 4 | - type_name 5 | - force_unwrapping 6 | - force_cast 7 | - force_try 8 | - function_body_length 9 | - cyclomatic_complexity 10 | - operator_whitespace 11 | - identifier_name 12 | 13 | opt_in_rules: 14 | - empty_count -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+Bool.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Quick 26 | 27 | final class DefaultsBoolSpec: QuickSpec, DefaultsSerializableSpec { 28 | 29 | typealias Serializable = Bool 30 | 31 | var customValue: Bool = true 32 | var defaultValue: Bool = false 33 | var keyStore = FrogKeyStore() 34 | 35 | override func spec() { 36 | given("Bool") { 37 | self.testValues() 38 | self.testOptionalValues() 39 | self.testOptionalValuesWithoutDefaultValue() 40 | self.testObserving() 41 | self.testPlistRegisteringValues(valueStrings: ["YES": true, 42 | "yes": true, 43 | "TRUE": true, 44 | "true": true, 45 | "Y": true, 46 | "y": true, 47 | "T": true, 48 | "t": true, 49 | "1": true, 50 | "NO": false, 51 | "no": false, 52 | "FALSE": false, 53 | "false": false, 54 | "N": false, 55 | "n": false, 56 | "f": false, 57 | "F": false, 58 | "0": false, 59 | "blabla": nil, 60 | "test": nil]) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+Data.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Quick 26 | import Foundation 27 | 28 | final class DefaultsDataSpec: QuickSpec, DefaultsSerializableSpec { 29 | 30 | typealias Serializable = Data 31 | 32 | var customValue: Data = "custom data".data(using: .utf8)! 33 | var defaultValue: Data = "default data".data(using: .utf8)! 34 | var keyStore = FrogKeyStore() 35 | 36 | override func spec() { 37 | given("Data") { 38 | self.testValues() 39 | self.testOptionalValues() 40 | self.testOptionalValuesWithoutDefaultValue() 41 | self.testObserving() 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+Date.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Quick 26 | import Foundation 27 | 28 | final class DefaultsDateSpec: QuickSpec, DefaultsSerializableSpec { 29 | 30 | typealias Serializable = Date 31 | 32 | var customValue: Date = Date(timeIntervalSince1970: 50) 33 | var defaultValue: Date = Date() 34 | var keyStore = FrogKeyStore() 35 | 36 | override func spec() { 37 | given("Date") { 38 | self.testValues() 39 | self.testOptionalValues() 40 | self.testOptionalValuesWithoutDefaultValue() 41 | self.testObserving() 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+Dictionary.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Quick 26 | import Foundation 27 | 28 | final class DefaultsDictionarySpec: QuickSpec, DefaultsSerializableSpec { 29 | 30 | typealias Serializable = [String: AnyHashable] 31 | 32 | var customValue: [String: AnyHashable] = ["a": "b"] 33 | var defaultValue: [String: AnyHashable] = ["c": "d", "e": 1] 34 | var keyStore = FrogKeyStore() 35 | 36 | override func spec() { 37 | given("Dictionary") { 38 | self.testValues() 39 | self.testOptionalValues() 40 | self.testOptionalValuesWithoutDefaultValue() 41 | self.testObserving() 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+Double.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Quick 26 | 27 | final class DefaultsDoubleSpec: QuickSpec, DefaultsSerializableSpec { 28 | 29 | typealias Serializable = Double 30 | 31 | var customValue: Double = 2.0 32 | var defaultValue: Double = 1.0 33 | var keyStore = FrogKeyStore() 34 | 35 | override func spec() { 36 | given("Double") { 37 | self.testValues() 38 | self.testOptionalValues() 39 | self.testOptionalValuesWithoutDefaultValue() 40 | self.testObserving() 41 | self.testPlistRegisteringValues(valueStrings: ["0": 0.0, 42 | "1": 1.0, 43 | "2": 2.0, 44 | "31.0": 31.0]) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+Int.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Quick 26 | 27 | final class DefaultsIntSpec: QuickSpec, DefaultsSerializableSpec { 28 | 29 | typealias Serializable = Int 30 | 31 | var customValue: Int = 2 32 | var defaultValue: Int = 1 33 | var keyStore = FrogKeyStore() 34 | 35 | override func spec() { 36 | given("Int") { 37 | self.testValues() 38 | self.testOptionalValues() 39 | self.testOptionalValuesWithoutDefaultValue() 40 | self.testObserving() 41 | self.testPlistRegisteringValues(valueStrings: ["0": 0, 42 | "1": 1, 43 | "2": 2, 44 | "111111": 111111]) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Quick 26 | 27 | final class DefaultsStringSpec: QuickSpec, DefaultsSerializableSpec { 28 | 29 | typealias Serializable = String 30 | 31 | var customValue: String = "custom value" 32 | var defaultValue: String = "default value" 33 | var keyStore = FrogKeyStore() 34 | 35 | override func spec() { 36 | given("String") { 37 | self.testValues() 38 | self.testOptionalValues() 39 | self.testOptionalValuesWithoutDefaultValue() 40 | self.testObserving() 41 | self.testPlistRegisteringValues(valueStrings: ["0.0": "0.0", 42 | "test": "test"]) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/Built-ins/Defaults+URL.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Quick 26 | import Foundation 27 | 28 | final class DefaultsUrlSpec: QuickSpec, DefaultsSerializableSpec { 29 | 30 | typealias Serializable = URL 31 | 32 | var customValue: URL = URL(string: "https://google.com")! 33 | var defaultValue: URL = URL(string: "https://github.com")! 34 | var keyStore = FrogKeyStore() 35 | 36 | override func spec() { 37 | given("URL") { 38 | self.testValues() 39 | self.testOptionalValues() 40 | self.testOptionalValuesWithoutDefaultValue() 41 | self.testObserving() 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/External types/Defaults+BestFroggiesEnum.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Quick 26 | 27 | final class DefaultsBestFroggiesEnumSerializableSpec: QuickSpec, DefaultsSerializableSpec { 28 | 29 | typealias Serializable = BestFroggiesEnum 30 | 31 | var customValue: BestFroggiesEnum = .Andy 32 | var defaultValue: BestFroggiesEnum = .Dandy 33 | var keyStore = FrogKeyStore() 34 | 35 | override func spec() { 36 | given("BestFroggiesEnum") { 37 | self.testValues() 38 | self.testOptionalValues() 39 | self.testOptionalValuesWithoutDefaultValue() 40 | self.testObserving() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/External types/Defaults+Color.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Quick 26 | import Nimble 27 | import SwiftyUserDefaults 28 | 29 | #if canImport(UIKit) || canImport(AppKit) 30 | #if canImport(UIKit) 31 | import UIKit.UIColor 32 | public typealias Color = UIColor 33 | #elseif canImport(AppKit) 34 | import AppKit.NSColor 35 | public typealias Color = NSColor 36 | #endif 37 | 38 | extension Color: DefaultsSerializable {} 39 | 40 | final class DefaultsUIColorSerializableSpec: QuickSpec, DefaultsSerializableSpec { 41 | 42 | typealias Serializable = Color 43 | 44 | var customValue: Color = .green 45 | var defaultValue: Color = .blue 46 | var keyStore = FrogKeyStore() 47 | 48 | override func spec() { 49 | given("NSColor") { 50 | self.testValues() 51 | self.testOptionalValues() 52 | self.testOptionalValuesWithoutDefaultValue() 53 | self.testObserving() 54 | } 55 | } 56 | } 57 | #endif 58 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/External types/Defaults+FrogCodable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Quick 26 | 27 | final class DefaultsFrogCodableSpec: QuickSpec, DefaultsSerializableSpec { 28 | 29 | typealias Serializable = FrogCodable 30 | 31 | var customValue: FrogCodable = FrogCodable(name: "custom") 32 | var defaultValue: FrogCodable = FrogCodable(name: "default") 33 | var keyStore = FrogKeyStore() 34 | 35 | override func spec() { 36 | given("FrogCodable") { 37 | self.testValues() 38 | self.testOptionalValues() 39 | self.testOptionalValuesWithoutDefaultValue() 40 | self.testObserving() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/External types/Defaults+FrogCustomSerializable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Quick 26 | 27 | final class DefaultsFrogCustomSerializableSpec: QuickSpec, DefaultsSerializableSpec { 28 | 29 | typealias Serializable = FrogCustomSerializable 30 | 31 | var customValue: FrogCustomSerializable = FrogCustomSerializable(name: "custom") 32 | var defaultValue: FrogCustomSerializable = FrogCustomSerializable(name: "default") 33 | var keyStore = FrogKeyStore() 34 | 35 | override func spec() { 36 | given("FrogCustomSerializable") { 37 | self.testValues() 38 | self.testOptionalValues() 39 | self.testOptionalValuesWithoutDefaultValue() 40 | self.testObserving() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/External types/Defaults+FrogSerializable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Quick 26 | 27 | final class DefaultsFrogSerializableSpec: QuickSpec, DefaultsSerializableSpec { 28 | 29 | typealias Serializable = FrogSerializable 30 | 31 | var customValue: FrogSerializable = FrogSerializable(name: "custom") 32 | var defaultValue: FrogSerializable = FrogSerializable(name: "default") 33 | var keyStore = FrogKeyStore() 34 | 35 | override func spec() { 36 | given("FrogSerializable") { 37 | self.testValues() 38 | self.testOptionalValues() 39 | self.testOptionalValuesWithoutDefaultValue() 40 | self.testObserving() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/TestHelpers/TestHelper.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Quick 3 | import SwiftyUserDefaults 4 | 5 | func given(_ description: String, closure: @escaping () -> Void) { 6 | describe(description, closure: closure) 7 | } 8 | 9 | func when(_ description: String, closure: @escaping () -> Void) { 10 | context(description, closure: closure) 11 | } 12 | 13 | func then(_ description: String, closure: @escaping () -> Void) { 14 | it(description, closure: closure) 15 | } 16 | 17 | extension UserDefaults { 18 | 19 | func cleanObjects() { 20 | for (key, _) in dictionaryRepresentation() { 21 | removeObject(forKey: key) 22 | } 23 | } 24 | } 25 | 26 | struct FrogCodable: Codable, Equatable, DefaultsSerializable { 27 | 28 | let name: String 29 | 30 | init(name: String = "Froggy") { 31 | self.name = name 32 | } 33 | } 34 | 35 | final class FrogSerializable: NSObject, DefaultsSerializable, NSCoding { 36 | 37 | typealias T = FrogSerializable 38 | 39 | let name: String 40 | 41 | init(name: String = "Froggy") { 42 | self.name = name 43 | } 44 | 45 | init?(coder aDecoder: NSCoder) { 46 | guard let name = aDecoder.decodeObject(forKey: "name") as? String else { return nil } 47 | 48 | self.name = name 49 | } 50 | 51 | func encode(with aCoder: NSCoder) { 52 | aCoder.encode(name, forKey: "name") 53 | } 54 | 55 | override func isEqual(_ object: Any?) -> Bool { 56 | guard let rhs = object as? FrogSerializable else { return false } 57 | 58 | return name == rhs.name 59 | } 60 | } 61 | 62 | enum BestFroggiesEnum: String, DefaultsSerializable { 63 | 64 | case Andy 65 | case Dandy 66 | } 67 | 68 | final class DefaultsFrogBridge: DefaultsBridge { 69 | func get(key: String, userDefaults: UserDefaults) -> FrogCustomSerializable? { 70 | let name = userDefaults.string(forKey: key) 71 | return name.map(FrogCustomSerializable.init) 72 | } 73 | 74 | func save(key: String, value: FrogCustomSerializable?, userDefaults: UserDefaults) { 75 | userDefaults.set(value?.name, forKey: key) 76 | } 77 | 78 | func deserialize(_ object: Any) -> FrogCustomSerializable? { 79 | guard let name = object as? String else { return nil } 80 | 81 | return FrogCustomSerializable(name: name) 82 | } 83 | } 84 | 85 | final class DefaultsFrogArrayBridge: DefaultsBridge { 86 | func get(key: String, userDefaults: UserDefaults) -> [FrogCustomSerializable]? { 87 | return userDefaults.array(forKey: key)? 88 | .compactMap { $0 as? String } 89 | .map(FrogCustomSerializable.init) 90 | } 91 | 92 | func save(key: String, value: [FrogCustomSerializable]?, userDefaults: UserDefaults) { 93 | let values = value?.map { $0.name } 94 | userDefaults.set(values, forKey: key) 95 | } 96 | 97 | func deserialize(_ object: Any) -> [FrogCustomSerializable]? { 98 | guard let names = object as? [String] else { return nil } 99 | 100 | return names.map(FrogCustomSerializable.init) 101 | } 102 | } 103 | 104 | struct FrogCustomSerializable: DefaultsSerializable, Equatable { 105 | 106 | static var _defaults: DefaultsFrogBridge { return DefaultsFrogBridge() } 107 | static var _defaultsArray: DefaultsFrogArrayBridge { return DefaultsFrogArrayBridge() } 108 | 109 | let name: String 110 | } 111 | 112 | final class FrogKeyStore: DefaultsKeyStore { 113 | 114 | lazy var testValue: DefaultsKey = { fatalError("not initialized yet") }() 115 | lazy var testArray: DefaultsKey<[Serializable]> = { fatalError("not initialized yet") }() 116 | lazy var testOptionalValue: DefaultsKey = { fatalError("not initialized yet") }() 117 | lazy var testOptionalArray: DefaultsKey<[Serializable]?> = { fatalError("not initialized yet") }() 118 | } 119 | -------------------------------------------------------------------------------- /Tests/SwiftyUserDefaultsTests/TestHelpers/UserDefaults+PropertyList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftyUserDefaults 3 | // 4 | // Copyright (c) 2015-present Radosław Pietruszewski, Łukasz Mróz 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | fileprivate let propertyListPrefixes: Set = [ "{", "[", "(", "<", "\"" ] 28 | 29 | // Kudos to stdlib-corelibs-foundation: 30 | // https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/UserDefaults.swift#L415-L458 31 | internal extension UserDefaults { 32 | static func _parseArguments(_ arguments: [String]) -> [String: Any] { 33 | var result: [String: Any] = [:] 34 | 35 | let count = arguments.count 36 | 37 | var index = 0 38 | while index < count - 1 { // We're looking for pairs, so stop at the second-to-last argument. 39 | let current = arguments[index] 40 | let next = arguments[index + 1] 41 | if current.hasPrefix("-") && !next.hasPrefix("-") { 42 | // Match what Darwin does, which is to check whether the first argument is one of the characters that make up a NeXTStep-style or XML property list: open brace, open parens, open bracket, open angle bracket, or double quote. If it is, attempt parsing it as a plist; otherwise, just use the argument value as a String. 43 | 44 | let keySubstring = current[current.index(after: current.startIndex)...] 45 | if !keySubstring.isEmpty { 46 | let key = String(keySubstring) 47 | let value = next.trimmingCharacters(in: .whitespacesAndNewlines) 48 | 49 | var parsed = false 50 | if let prefix = value.first, propertyListPrefixes.contains(prefix) { 51 | if let data = value.data(using: .utf8), 52 | let plist = try? PropertyListSerialization.propertyList(from: data, format: nil) { 53 | 54 | // If we can parse that argument as a plist, use the parsed value. 55 | parsed = true 56 | result[key] = plist 57 | 58 | } 59 | } 60 | 61 | if !parsed { 62 | result[key] = value 63 | } 64 | } 65 | 66 | index += 1 // Skip both the key and the value on this loop. 67 | } 68 | 69 | index += 1 70 | } 71 | 72 | return result 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /scripts/carthage_integration_step.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PROJECT_NAME="TestCarthage" 4 | FRAMEWORK_NAME="SwiftyUserDefaults" 5 | PLATFORM="iOS" 6 | DEPLOYMENT_TARGET="11.0" 7 | 8 | FRAMEWORK_DIR=$(pwd) 9 | BRANCH_NAME=$(git symbolic-ref -q HEAD) 10 | BRANCH_NAME=${BRANCH_NAME##refs/heads/} 11 | BRANCH_NAME=${BRANCH_NAME:-HEAD} 12 | 13 | mkdir $PROJECT_NAME 14 | cd $PROJECT_NAME 15 | echo "name: $PROJECT_NAME\ntargets:\n $PROJECT_NAME:\n type: application\n platform: $PLATFORM\n deploymentTarget: '$DEPLOYMENT_TARGET'" > project.yml 16 | xcodegen generate 17 | echo "git \"file://$FRAMEWORK_DIR\" \"$BRANCH_NAME\"" > Cartfile 18 | carthage bootstrap --platform $PLATFORM --verbose | xcpretty 19 | cd ../ 20 | rm -rf $PROJECT_NAME -------------------------------------------------------------------------------- /scripts/cocoapods_integration_step.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PROJECT_NAME="TestCocoaPods" 4 | FRAMEWORK_NAME="SwiftyUserDefaults" 5 | PLATFORM="iOS" 6 | DEPLOYMENT_TARGET="11.0" 7 | 8 | mkdir $PROJECT_NAME 9 | cd $PROJECT_NAME 10 | echo "name: $PROJECT_NAME\ntargets:\n $PROJECT_NAME:\n type: application\n platform: $PLATFORM\n deploymentTarget: '$DEPLOYMENT_TARGET'" > project.yml 11 | xcodegen generate 12 | echo "target '$PROJECT_NAME' do\n use_frameworks!\n pod '$FRAMEWORK_NAME', :path => '../'\nend" > Podfile 13 | pod install 14 | cd ../ 15 | rm -rf $PROJECT_NAME -------------------------------------------------------------------------------- /scripts/copy-carthage-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | case "$PLATFORM_NAME" in 4 | macosx) plat=Mac;; 5 | iphone*) plat=iOS;; 6 | watch*) plat=watchOS;; 7 | appletv*) plat=tvOS;; 8 | *) echo "error: Unknown PLATFORM_NAME: $PLATFORM_NAME"; exit 1;; 9 | esac 10 | 11 | for (( n = 0; n < SCRIPT_INPUT_FILE_COUNT; n++ )); do 12 | VAR=SCRIPT_INPUT_FILE_$n 13 | framework=$(basename "${!VAR}") 14 | export SCRIPT_INPUT_FILE_$n="$SRCROOT"/Carthage/Build/$plat/"$framework" 15 | done 16 | 17 | /usr/local/bin/carthage copy-frameworks || exit 18 | 19 | for (( n = 0; n < SCRIPT_INPUT_FILE_COUNT; n++ )); do 20 | VAR=SCRIPT_INPUT_FILE_$n 21 | source=${!VAR}.dSYM 22 | dest=${BUILT_PRODUCTS_DIR}/$(basename "$source") 23 | ditto "$source" "$dest" || exit 24 | done -------------------------------------------------------------------------------- /scripts/spm_integration_step.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | SWIFT_VERSION=$1 4 | PROJECT_NAME="TestSPM" 5 | FRAMEWORK_NAME="SwiftyUserDefaults" 6 | 7 | FRAMEWORK_DIR=$(pwd) 8 | BRANCH_NAME=$(git symbolic-ref -q HEAD) 9 | BRANCH_NAME=${BRANCH_NAME##refs/heads/} 10 | BRANCH_NAME=${BRANCH_NAME:-HEAD} 11 | 12 | mkdir $PROJECT_NAME 13 | cd $PROJECT_NAME 14 | 15 | swift package init 16 | echo "// swift-tools-version:$SWIFT_VERSION 17 | 18 | import PackageDescription 19 | 20 | let package = Package( 21 | name: \"$PROJECT_NAME\", 22 | dependencies: [ 23 | .package(url: \"$FRAMEWORK_DIR\", .branch(\"$BRANCH_NAME\")), 24 | ], 25 | targets: [ 26 | .target(name: \"$PROJECT_NAME\", dependencies: [\"$FRAMEWORK_NAME\"]), 27 | ] 28 | ) 29 | " > Package.swift 30 | swift build -Xswiftc "-target" -Xswiftc "x86_64-apple-macosx10.11" 31 | cd ../ 32 | rm -rf $PROJECT_NAME --------------------------------------------------------------------------------