Get rid of `OptionalKey`
This commit is contained in:
parent
3275717838
commit
b2fdee2055
|
@ -1,3 +1,3 @@
|
||||||
language: swift
|
language: swift
|
||||||
osx_image: xcode11.3
|
osx_image: xcode11.4
|
||||||
script: xcodebuild test -project Defaults.xcodeproj -scheme Defaults-macOS
|
script: xcodebuild test -project Defaults.xcodeproj -scheme Defaults-macOS
|
||||||
|
|
|
@ -8,7 +8,7 @@ Pod::Spec.new do |s|
|
||||||
s.authors = { 'Sindre Sorhus' => 'sindresorhus@gmail.com' }
|
s.authors = { 'Sindre Sorhus' => 'sindresorhus@gmail.com' }
|
||||||
s.source = { :git => 'https://github.com/sindresorhus/Defaults.git', :tag => "v#{s.version}" }
|
s.source = { :git => 'https://github.com/sindresorhus/Defaults.git', :tag => "v#{s.version}" }
|
||||||
s.source_files = 'Sources/**/*.swift'
|
s.source_files = 'Sources/**/*.swift'
|
||||||
s.swift_version = '5.1'
|
s.swift_version = '5.2'
|
||||||
s.macos.deployment_target = '10.12'
|
s.macos.deployment_target = '10.12'
|
||||||
s.ios.deployment_target = '10.0'
|
s.ios.deployment_target = '10.0'
|
||||||
s.tvos.deployment_target = '10.0'
|
s.tvos.deployment_target = '10.0'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// swift-tools-version:5.1
|
// swift-tools-version:5.2
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
|
|
|
@ -8,8 +8,6 @@ public final class Defaults {
|
||||||
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
||||||
public typealias NSSecureCodingKey = Defaults.NSSecureCodingKey
|
public typealias NSSecureCodingKey = Defaults.NSSecureCodingKey
|
||||||
|
|
||||||
public typealias OptionalKey = Defaults.OptionalKey
|
|
||||||
|
|
||||||
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
||||||
public typealias NSSecureCodingOptionalKey = Defaults.NSSecureCodingOptionalKey
|
public typealias NSSecureCodingOptionalKey = Defaults.NSSecureCodingOptionalKey
|
||||||
|
|
||||||
|
@ -29,6 +27,10 @@ public final class Defaults {
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
if (defaultValue as? _DefaultsOptionalType)?.isNil == true {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the default value in the actual UserDefaults, so it can be used in other contexts, like binding.
|
// Sets the default value in the actual UserDefaults, so it can be used in other contexts, like binding.
|
||||||
if UserDefaults.isNativelySupportedType(Value.self) {
|
if UserDefaults.isNativelySupportedType(Value.self) {
|
||||||
suite.register(defaults: [key: defaultValue])
|
suite.register(defaults: [key: defaultValue])
|
||||||
|
@ -52,6 +54,10 @@ public final class Defaults {
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
if (defaultValue as? _DefaultsOptionalType)?.isNil == true {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the default value in the actual UserDefaults, so it can be used in other contexts, like binding.
|
// Sets the default value in the actual UserDefaults, so it can be used in other contexts, like binding.
|
||||||
if UserDefaults.isNativelySupportedType(Value.self) {
|
if UserDefaults.isNativelySupportedType(Value.self) {
|
||||||
suite.register(defaults: [key: defaultValue])
|
suite.register(defaults: [key: defaultValue])
|
||||||
|
@ -61,17 +67,6 @@ public final class Defaults {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class OptionalKey<Value: Codable>: Keys {
|
|
||||||
public let name: String
|
|
||||||
public let suite: UserDefaults
|
|
||||||
|
|
||||||
/// Create an optional defaults key.
|
|
||||||
public init(_ key: String, suite: UserDefaults = .standard) {
|
|
||||||
self.name = key
|
|
||||||
self.suite = suite
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
||||||
public final class NSSecureCodingOptionalKey<Value: NSSecureCoding>: Keys {
|
public final class NSSecureCodingOptionalKey<Value: NSSecureCoding>: Keys {
|
||||||
public let name: String
|
public let name: String
|
||||||
|
@ -103,15 +98,7 @@ public final class Defaults {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access a defaults value using a `Defaults.OptionalKey`.
|
/// Access a defaults value using a `Defaults.NSSecureCodingOptionalKey`.
|
||||||
public static subscript<Value: Codable>(key: OptionalKey<Value>) -> Value? {
|
|
||||||
get { key.suite[key] }
|
|
||||||
set {
|
|
||||||
key.suite[key] = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Access a defaults value using a `Defaults.OptionalKey`.
|
|
||||||
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
||||||
public static subscript<Value: NSSecureCoding>(key: NSSecureCodingOptionalKey<Value>) -> Value? {
|
public static subscript<Value: NSSecureCoding>(key: NSSecureCodingOptionalKey<Value>) -> Value? {
|
||||||
get { key.suite[key] }
|
get { key.suite[key] }
|
||||||
|
@ -193,29 +180,6 @@ public final class Defaults {
|
||||||
key.suite[key] = key.defaultValue
|
key.suite[key] = key.defaultValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
Reset the given optional keys back to `nil`.
|
|
||||||
|
|
||||||
- Parameter keys: Keys to reset.
|
|
||||||
- Parameter suite: `UserDefaults` suite.
|
|
||||||
|
|
||||||
```
|
|
||||||
extension Defaults.Keys {
|
|
||||||
static let unicorn = OptionalKey<String>("unicorn")
|
|
||||||
}
|
|
||||||
|
|
||||||
Defaults[.unicorn] = "🦄"
|
|
||||||
|
|
||||||
Defaults.reset(.unicorn)
|
|
||||||
|
|
||||||
Defaults[.unicorn]
|
|
||||||
//=> nil
|
|
||||||
```
|
|
||||||
*/
|
|
||||||
public static func reset<Value: Codable>(_ keys: OptionalKey<Value>..., suite: UserDefaults = .standard) {
|
|
||||||
reset(keys, suite: suite)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Reset the given optional keys back to `nil`.
|
Reset the given optional keys back to `nil`.
|
||||||
|
@ -228,31 +192,6 @@ public final class Defaults {
|
||||||
public static func reset<Value: NSSecureCoding>(_ keys: NSSecureCodingOptionalKey<Value>..., suite: UserDefaults = .standard) {
|
public static func reset<Value: NSSecureCoding>(_ keys: NSSecureCodingOptionalKey<Value>..., suite: UserDefaults = .standard) {
|
||||||
reset(keys, suite: suite)
|
reset(keys, suite: suite)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
Reset the given array of optional keys back to `nil`.
|
|
||||||
|
|
||||||
- Parameter keys: Keys to reset.
|
|
||||||
- Parameter suite: `UserDefaults` suite.
|
|
||||||
|
|
||||||
```
|
|
||||||
extension Defaults.Keys {
|
|
||||||
static let unicorn = OptionalKey<String>("unicorn")
|
|
||||||
}
|
|
||||||
|
|
||||||
Defaults[.unicorn] = "🦄"
|
|
||||||
|
|
||||||
Defaults.reset(.unicorn)
|
|
||||||
|
|
||||||
Defaults[.unicorn]
|
|
||||||
//=> nil
|
|
||||||
```
|
|
||||||
*/
|
|
||||||
public static func reset<Value: Codable>(_ keys: [OptionalKey<Value>], suite: UserDefaults = .standard) {
|
|
||||||
for key in keys {
|
|
||||||
key.suite[key] = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Reset the given array of optional keys back to `nil`.
|
Reset the given array of optional keys back to `nil`.
|
||||||
|
@ -277,6 +216,19 @@ public final class Defaults {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Defaults.Key where Value: _DefaultsOptionalType {
|
||||||
|
public convenience init(_ key: String, suite: UserDefaults = .standard) {
|
||||||
|
self.init(key, default: nil, suite: suite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
||||||
|
extension Defaults.NSSecureCodingKey where Value: _DefaultsOptionalType {
|
||||||
|
public convenience init(_ key: String, suite: UserDefaults = .standard) {
|
||||||
|
self.init(key, default: nil, suite: suite)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension UserDefaults {
|
extension UserDefaults {
|
||||||
private func _get<Value: Codable>(_ key: String) -> Value? {
|
private func _get<Value: Codable>(_ key: String) -> Value? {
|
||||||
if UserDefaults.isNativelySupportedType(Value.self) {
|
if UserDefaults.isNativelySupportedType(Value.self) {
|
||||||
|
@ -334,6 +286,11 @@ extension UserDefaults {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func _set<Value: Codable>(_ key: String, to value: Value) {
|
private func _set<Value: Codable>(_ key: String, to value: Value) {
|
||||||
|
if (value as? _DefaultsOptionalType)?.isNil == true {
|
||||||
|
removeObject(forKey: key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if UserDefaults.isNativelySupportedType(Value.self) {
|
if UserDefaults.isNativelySupportedType(Value.self) {
|
||||||
set(value, forKey: key)
|
set(value, forKey: key)
|
||||||
return
|
return
|
||||||
|
@ -344,6 +301,7 @@ extension UserDefaults {
|
||||||
|
|
||||||
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
||||||
private func _set<Value: NSSecureCoding>(_ key: String, to value: Value) {
|
private func _set<Value: NSSecureCoding>(_ key: String, to value: Value) {
|
||||||
|
// TODO: Handle nil here too.
|
||||||
if UserDefaults.isNativelySupportedType(Value.self) {
|
if UserDefaults.isNativelySupportedType(Value.self) {
|
||||||
set(value, forKey: key)
|
set(value, forKey: key)
|
||||||
return
|
return
|
||||||
|
@ -367,18 +325,6 @@ extension UserDefaults {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscript<Value: Codable>(key: Defaults.OptionalKey<Value>) -> Value? {
|
|
||||||
get { _get(key.name) }
|
|
||||||
set {
|
|
||||||
guard let value = newValue else {
|
|
||||||
set(nil, forKey: key.name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_set(key.name, to: value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
||||||
public subscript<Value: NSSecureCoding>(key: Defaults.NSSecureCodingOptionalKey<Value>) -> Value? {
|
public subscript<Value: NSSecureCoding>(key: Defaults.NSSecureCodingOptionalKey<Value>) -> Value? {
|
||||||
get { _get(key.name) }
|
get { _get(key.name) }
|
||||||
|
@ -392,16 +338,23 @@ extension UserDefaults {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate static func isNativelySupportedType<Value>(_ type: Value.Type) -> Bool {
|
fileprivate static func isNativelySupportedType<T>(_ type: T.Type) -> Bool {
|
||||||
switch type {
|
switch type {
|
||||||
case
|
case
|
||||||
is Bool.Type,
|
is Bool.Type,
|
||||||
|
is Bool?.Type, // swiftlint:disable:this discouraged_optional_boolean
|
||||||
is String.Type,
|
is String.Type,
|
||||||
|
is String?.Type,
|
||||||
is Int.Type,
|
is Int.Type,
|
||||||
|
is Int?.Type,
|
||||||
is Double.Type,
|
is Double.Type,
|
||||||
|
is Double?.Type,
|
||||||
is Float.Type,
|
is Float.Type,
|
||||||
|
is Float?.Type,
|
||||||
is Date.Type,
|
is Date.Type,
|
||||||
is Data.Type:
|
is Date?.Type,
|
||||||
|
is Data.Type,
|
||||||
|
is Data?.Type:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -60,7 +60,7 @@ extension Defaults {
|
||||||
self.options = options
|
self.options = options
|
||||||
}
|
}
|
||||||
|
|
||||||
func receive<S>(subscriber: S) where S : Subscriber, DefaultsPublisher.Failure == S.Failure, DefaultsPublisher.Output == S.Input {
|
func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input {
|
||||||
let subscription = DefaultsSubscription(
|
let subscription = DefaultsSubscription(
|
||||||
subscriber: subscriber,
|
subscriber: subscriber,
|
||||||
suite: suite,
|
suite: suite,
|
||||||
|
@ -81,7 +81,7 @@ extension Defaults {
|
||||||
static let isUnicornMode = Key<Bool>("isUnicornMode", default: false)
|
static let isUnicornMode = Key<Bool>("isUnicornMode", default: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
let publisher = Defaults.publisher(.isUnicornMode).map { $0.newValue }
|
let publisher = Defaults.publisher(.isUnicornMode).map(\.newValue)
|
||||||
|
|
||||||
let cancellable = publisher.sink { value in
|
let cancellable = publisher.sink { value in
|
||||||
print(value)
|
print(value)
|
||||||
|
@ -114,20 +114,6 @@ extension Defaults {
|
||||||
return AnyPublisher(publisher)
|
return AnyPublisher(publisher)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
Returns a type-erased `Publisher` that publishes changes related to the given optional key.
|
|
||||||
*/
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
|
|
||||||
public static func publisher<Value: Codable>(
|
|
||||||
_ key: Defaults.OptionalKey<Value>,
|
|
||||||
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
|
||||||
) -> AnyPublisher<OptionalKeyChange<Value>, Never> {
|
|
||||||
let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options)
|
|
||||||
.map { OptionalKeyChange<Value>(change: $0) }
|
|
||||||
|
|
||||||
return AnyPublisher(publisher)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns a type-erased `Publisher` that publishes changes related to the given optional key.
|
Returns a type-erased `Publisher` that publishes changes related to the given optional key.
|
||||||
*/
|
*/
|
||||||
|
@ -154,29 +140,7 @@ extension Defaults {
|
||||||
|
|
||||||
let combinedPublisher =
|
let combinedPublisher =
|
||||||
keys.map { key in
|
keys.map { key in
|
||||||
return Defaults.publisher(key, options: options)
|
Defaults.publisher(key, options: options)
|
||||||
.map { _ in () }
|
|
||||||
.eraseToAnyPublisher()
|
|
||||||
}.reduce(initial) { (combined, keyPublisher) in
|
|
||||||
combined.merge(with: keyPublisher).eraseToAnyPublisher()
|
|
||||||
}
|
|
||||||
|
|
||||||
return combinedPublisher
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Publisher for multiple `OptionalKey<T>` observation, but without specific information about changes.
|
|
||||||
*/
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
|
|
||||||
public static func publisher<Value: Codable>(
|
|
||||||
keys: Defaults.OptionalKey<Value>...,
|
|
||||||
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
|
||||||
) -> AnyPublisher<Void, Never> {
|
|
||||||
let initial = Empty<Void, Never>(completeImmediately: false).eraseToAnyPublisher()
|
|
||||||
|
|
||||||
let combinedPublisher =
|
|
||||||
keys.map { key in
|
|
||||||
return Defaults.publisher(key, options: options)
|
|
||||||
.map { _ in () }
|
.map { _ in () }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}.reduce(initial) { (combined, keyPublisher) in
|
}.reduce(initial) { (combined, keyPublisher) in
|
||||||
|
@ -198,7 +162,7 @@ extension Defaults {
|
||||||
|
|
||||||
let combinedPublisher =
|
let combinedPublisher =
|
||||||
keys.map { key in
|
keys.map { key in
|
||||||
return Defaults.publisher(key, options: options)
|
Defaults.publisher(key, options: options)
|
||||||
.map { _ in () }
|
.map { _ in () }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}.reduce(initial) { (combined, keyPublisher) in
|
}.reduce(initial) { (combined, keyPublisher) in
|
||||||
|
@ -220,7 +184,7 @@ extension Defaults {
|
||||||
|
|
||||||
let combinedPublisher =
|
let combinedPublisher =
|
||||||
keys.map { key in
|
keys.map { key in
|
||||||
return Defaults.publisher(key, options: options)
|
Defaults.publisher(key, options: options)
|
||||||
.map { _ in () }
|
.map { _ in () }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}.reduce(initial) { (combined, keyPublisher) in
|
}.reduce(initial) { (combined, keyPublisher) in
|
||||||
|
|
|
@ -113,22 +113,6 @@ extension Defaults {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct OptionalKeyChange<Value: Codable> {
|
|
||||||
public let kind: NSKeyValueChange
|
|
||||||
public let indexes: IndexSet?
|
|
||||||
public let isPrior: Bool
|
|
||||||
public let newValue: Value?
|
|
||||||
public let oldValue: Value?
|
|
||||||
|
|
||||||
init(change: BaseChange) {
|
|
||||||
self.kind = change.kind
|
|
||||||
self.indexes = change.indexes
|
|
||||||
self.isPrior = change.isPrior
|
|
||||||
self.oldValue = deserialize(change.oldValue, to: Value.self)
|
|
||||||
self.newValue = deserialize(change.newValue, to: Value.self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
||||||
public struct NSSecureCodingOptionalKeyChange<Value: NSSecureCoding> {
|
public struct NSSecureCodingOptionalKeyChange<Value: NSSecureCoding> {
|
||||||
public let kind: NSKeyValueChange
|
public let kind: NSKeyValueChange
|
||||||
|
@ -179,6 +163,7 @@ extension Defaults {
|
||||||
lifetimeAssociation = LifetimeAssociation(of: self, with: weaklyHeldObject, deinitHandler: { [weak self] in
|
lifetimeAssociation = LifetimeAssociation(of: self, with: weaklyHeldObject, deinitHandler: { [weak self] in
|
||||||
self?.invalidate()
|
self?.invalidate()
|
||||||
})
|
})
|
||||||
|
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,34 +240,6 @@ extension Defaults {
|
||||||
return observation
|
return observation
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
Observe an optional defaults key.
|
|
||||||
|
|
||||||
```
|
|
||||||
extension Defaults.Keys {
|
|
||||||
static let isUnicornMode = OptionalKey<Bool>("isUnicornMode")
|
|
||||||
}
|
|
||||||
|
|
||||||
let observer = Defaults.observe(.isUnicornMode) { change in
|
|
||||||
print(change.newValue)
|
|
||||||
//=> Optional(nil)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
*/
|
|
||||||
public static func observe<Value: Codable>(
|
|
||||||
_ key: Defaults.OptionalKey<Value>,
|
|
||||||
options: NSKeyValueObservingOptions = [.initial, .old, .new],
|
|
||||||
handler: @escaping (OptionalKeyChange<Value>) -> Void
|
|
||||||
) -> DefaultsObservation {
|
|
||||||
let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in
|
|
||||||
handler(
|
|
||||||
OptionalKeyChange<Value>(change: change)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
observation.start(options: options)
|
|
||||||
return observation
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Observe an optional defaults key.
|
Observe an optional defaults key.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -18,16 +18,19 @@ extension Decodable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class AssociatedObject<T: Any> {
|
|
||||||
subscript(index: Any) -> T? {
|
final class ObjectAssociation<T: Any> {
|
||||||
|
subscript(index: AnyObject) -> T? {
|
||||||
get {
|
get {
|
||||||
return objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as! T?
|
objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as! T?
|
||||||
} set {
|
}
|
||||||
|
set {
|
||||||
objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Causes a given target object to live at least as long as a given owner object.
|
Causes a given target object to live at least as long as a given owner object.
|
||||||
*/
|
*/
|
||||||
|
@ -46,7 +49,7 @@ final class LifetimeAssociation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static let associatedObjects = AssociatedObject<[ObjectLifetimeTracker]>()
|
private static let associatedObjects = ObjectAssociation<[ObjectLifetimeTracker]>()
|
||||||
private weak var wrappedObject: ObjectLifetimeTracker?
|
private weak var wrappedObject: ObjectLifetimeTracker?
|
||||||
private weak var owner: AnyObject?
|
private weak var owner: AnyObject?
|
||||||
|
|
||||||
|
@ -113,3 +116,19 @@ final class LifetimeAssociation {
|
||||||
self.owner = nil
|
self.owner = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// A protocol for making generic type constraints of optionals.
|
||||||
|
/// - Note: It's intentionally not including `associatedtype Wrapped` as that limits a lot of the use-cases.
|
||||||
|
public protocol _DefaultsOptionalType: ExpressibleByNilLiteral {
|
||||||
|
/// This is useful as you can't compare `_OptionalType` to `nil`.
|
||||||
|
var isNil: Bool { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Optional: _DefaultsOptionalType {
|
||||||
|
public var isNil: Bool { self == nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
func isOptionalType<T>(_ type: T.Type) -> Bool {
|
||||||
|
type is _DefaultsOptionalType.Type
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import XCTest
|
|
||||||
import Defaults
|
|
||||||
import CoreData
|
import CoreData
|
||||||
import Combine
|
import Combine
|
||||||
|
import XCTest
|
||||||
|
import Defaults
|
||||||
|
|
||||||
let fixtureURL = URL(string: "https://sindresorhus.com")!
|
let fixtureURL = URL(string: "https://sindresorhus.com")!
|
||||||
let fixtureURL2 = URL(string: "https://example.com")!
|
let fixtureURL2 = URL(string: "https://example.com")!
|
||||||
|
@ -17,7 +17,6 @@ let fixtureDate = Date()
|
||||||
|
|
||||||
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
@available(iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0, iOSApplicationExtension 11.0, macOSApplicationExtension 10.13, tvOSApplicationExtension 11.0, watchOSApplicationExtension 4.0, *)
|
||||||
final class ExamplePersistentHistory: NSPersistentHistoryToken {
|
final class ExamplePersistentHistory: NSPersistentHistoryToken {
|
||||||
|
|
||||||
let value: String
|
let value: String
|
||||||
|
|
||||||
init(value: String) {
|
init(value: String) {
|
||||||
|
@ -34,9 +33,7 @@ final class ExamplePersistentHistory: NSPersistentHistoryToken {
|
||||||
coder.encode(value, forKey: "value")
|
coder.encode(value, forKey: "value")
|
||||||
}
|
}
|
||||||
|
|
||||||
override class var supportsSecureCoding: Bool {
|
override class var supportsSecureCoding: Bool { true }
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Defaults.Keys {
|
extension Defaults.Keys {
|
||||||
|
@ -72,7 +69,7 @@ final class DefaultsTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testOptionalKey() {
|
func testOptionalKey() {
|
||||||
let key = Defaults.OptionalKey<Bool>("independentOptionalKey")
|
let key = Defaults.Key<Bool?>("independentOptionalKey")
|
||||||
XCTAssertNil(Defaults[key])
|
XCTAssertNil(Defaults[key])
|
||||||
Defaults[key] = true
|
Defaults[key] = true
|
||||||
XCTAssertTrue(Defaults[key]!)
|
XCTAssertTrue(Defaults[key]!)
|
||||||
|
@ -227,7 +224,7 @@ final class DefaultsTests: XCTestCase {
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
|
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
|
||||||
func testObserveOptionalKeyCombine() {
|
func testObserveOptionalKeyCombine() {
|
||||||
let key = Defaults.OptionalKey<Bool>("observeOptionalKey")
|
let key = Defaults.Key<Bool?>("observeOptionalKey")
|
||||||
let expect = expectation(description: "Observation closure being called")
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
|
||||||
let publisher = Defaults
|
let publisher = Defaults
|
||||||
|
@ -348,8 +345,8 @@ final class DefaultsTests: XCTestCase {
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
|
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, iOSApplicationExtension 13.0, macOSApplicationExtension 10.15, tvOSApplicationExtension 13.0, watchOSApplicationExtension 6.0, *)
|
||||||
func testObserveMultipleOptionalKeysCombine() {
|
func testObserveMultipleOptionalKeysCombine() {
|
||||||
let key1 = Defaults.OptionalKey<Bool>("observeOptionalKey1")
|
let key1 = Defaults.Key<Bool?>("observeOptionalKey1")
|
||||||
let key2 = Defaults.OptionalKey<Bool>("observeOptionalKey2")
|
let key2 = Defaults.Key<Bool?>("observeOptionalKey2")
|
||||||
let expect = expectation(description: "Observation closure being called")
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
|
||||||
let publisher = Defaults.publisher(keys: key1, key2, options: [.old, .new]).collect(2)
|
let publisher = Defaults.publisher(keys: key1, key2, options: [.old, .new]).collect(2)
|
||||||
|
@ -441,7 +438,7 @@ final class DefaultsTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testObserveOptionalKey() {
|
func testObserveOptionalKey() {
|
||||||
let key = Defaults.OptionalKey<Bool>("observeOptionalKey")
|
let key = Defaults.Key<Bool?>("observeOptionalKey")
|
||||||
let expect = expectation(description: "Observation closure being called")
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
|
||||||
var observation: DefaultsObservation!
|
var observation: DefaultsObservation!
|
||||||
|
@ -558,8 +555,8 @@ final class DefaultsTests: XCTestCase {
|
||||||
let newString1 = "bar1"
|
let newString1 = "bar1"
|
||||||
let newString2 = "bar2"
|
let newString2 = "bar2"
|
||||||
let newString3 = "bar3"
|
let newString3 = "bar3"
|
||||||
let key1 = Defaults.OptionalKey<String>("optionalKey1")
|
let key1 = Defaults.Key<String?>("optionalKey1")
|
||||||
let key2 = Defaults.OptionalKey<String>("optionalKey2")
|
let key2 = Defaults.Key<String?>("optionalKey2")
|
||||||
Defaults[key1] = newString1
|
Defaults[key1] = newString1
|
||||||
Defaults[key2] = newString2
|
Defaults[key2] = newString2
|
||||||
Defaults.reset(key1)
|
Defaults.reset(key1)
|
||||||
|
@ -578,9 +575,9 @@ final class DefaultsTests: XCTestCase {
|
||||||
let newString1 = "bar1"
|
let newString1 = "bar1"
|
||||||
let newString2 = "bar2"
|
let newString2 = "bar2"
|
||||||
let newString3 = "bar3"
|
let newString3 = "bar3"
|
||||||
let key1 = Defaults.OptionalKey<String>("aoptionalKey1")
|
let key1 = Defaults.Key<String?>("aoptionalKey1")
|
||||||
let key2 = Defaults.OptionalKey<String>("aoptionalKey2")
|
let key2 = Defaults.Key<String?>("aoptionalKey2")
|
||||||
let key3 = Defaults.OptionalKey<String>("aoptionalKey3")
|
let key3 = Defaults.Key<String?>("aoptionalKey3")
|
||||||
Defaults[key1] = newString1
|
Defaults[key1] = newString1
|
||||||
Defaults[key2] = newString2
|
Defaults[key2] = newString2
|
||||||
Defaults[key3] = newString3
|
Defaults[key3] = newString3
|
||||||
|
|
40
readme.md
40
readme.md
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
> Swifty and modern [UserDefaults](https://developer.apple.com/documentation/foundation/userdefaults)
|
> Swifty and modern [UserDefaults](https://developer.apple.com/documentation/foundation/userdefaults)
|
||||||
|
|
||||||
**Note:** The readme reflects the master branch. [Click here](https://github.com/sindresorhus/Defaults/tree/55ffea9487fb9b559406d909ee31dcd955fe77aa#readme) for docs for the latest version. The code in the master branch cannot be released until Apple fixes [this bug](https://github.com/feedback-assistant/reports/issues/44).
|
#### Note: The readme reflects the master branch. [Click here](https://github.com/sindresorhus/Defaults/tree/55ffea9487fb9b559406d909ee31dcd955fe77aa#readme) for docs for the latest version. The code in the master branch cannot be released until Apple fixes [this bug](https://github.com/feedback-assistant/reports/issues/44).
|
||||||
|
|
||||||
It uses `NSUserDefaults` underneath but exposes a type-safe facade with lots of nice conveniences.
|
It uses `NSUserDefaults` underneath but exposes a type-safe facade with lots of nice conveniences.
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ You can also declare optional keys for when you don't want to declare a default
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
extension Defaults.Keys {
|
extension Defaults.Keys {
|
||||||
static let name = OptionalKey<Double>("name")
|
static let name = Key<Double?>("name")
|
||||||
}
|
}
|
||||||
|
|
||||||
if let name = Defaults[.name] {
|
if let name = Defaults[.name] {
|
||||||
|
@ -228,7 +228,7 @@ Defaults[.isUnicornMode]
|
||||||
//=> false
|
//=> false
|
||||||
```
|
```
|
||||||
|
|
||||||
This works for `OptionalKey` too, which will be reset back to `nil`.
|
This works for a `Key` with an optional too, which will be reset back to `nil`.
|
||||||
|
|
||||||
### It's just `UserDefaults` with sugar
|
### It's just `UserDefaults` with sugar
|
||||||
|
|
||||||
|
@ -308,16 +308,6 @@ Create a NSSecureCoding key with a default value.
|
||||||
|
|
||||||
The default value is written to the actual `UserDefaults` and can be used elsewhere. For example, with a Interface Builder binding.
|
The default value is written to the actual `UserDefaults` and can be used elsewhere. For example, with a Interface Builder binding.
|
||||||
|
|
||||||
#### `Defaults.OptionalKey` *(alias `Defaults.Keys.OptionalKey`)*
|
|
||||||
|
|
||||||
```swift
|
|
||||||
Defaults.OptionalKey<T>(_ key: String, suite: UserDefaults = .standard)
|
|
||||||
```
|
|
||||||
|
|
||||||
Type: `class`
|
|
||||||
|
|
||||||
Create a key with an optional value.
|
|
||||||
|
|
||||||
#### `Defaults.NSSecureCodingOptionalKey` *(alias `Defaults.Keys.NSSecureCodingOptionalKey`)*
|
#### `Defaults.NSSecureCodingOptionalKey` *(alias `Defaults.Keys.NSSecureCodingOptionalKey`)*
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
|
@ -333,8 +323,6 @@ Create a NSSecureCoding key with an optional value.
|
||||||
```swift
|
```swift
|
||||||
Defaults.reset<T: Codable>(_ keys: Defaults.Key<T>..., suite: UserDefaults = .standard)
|
Defaults.reset<T: Codable>(_ keys: Defaults.Key<T>..., suite: UserDefaults = .standard)
|
||||||
Defaults.reset<T: Codable>(_ keys: [Defaults.Key<T>], suite: UserDefaults = .standard)
|
Defaults.reset<T: Codable>(_ keys: [Defaults.Key<T>], suite: UserDefaults = .standard)
|
||||||
Defaults.reset<T: Codable>(_ keys: Defaults.OptionalKey<T>..., suite: UserDefaults = .standard)
|
|
||||||
Defaults.reset<T: Codable>(_ keys: [Defaults.OptionalKey<T>], suite: UserDefaults = .standard)
|
|
||||||
|
|
||||||
Defaults.reset<T: Codable>(_ keys: Defaults.NSSecureCodingKey<T>..., suite: UserDefaults = .standard)
|
Defaults.reset<T: Codable>(_ keys: Defaults.NSSecureCodingKey<T>..., suite: UserDefaults = .standard)
|
||||||
Defaults.reset<T: Codable>(_ keys: [Defaults.NSSecureCodingKey<T>], suite: UserDefaults = .standard)
|
Defaults.reset<T: Codable>(_ keys: [Defaults.NSSecureCodingKey<T>], suite: UserDefaults = .standard)
|
||||||
|
@ -364,14 +352,6 @@ Defaults.observe<T: NSSecureCoding>(
|
||||||
) -> DefaultsObservation
|
) -> DefaultsObservation
|
||||||
```
|
```
|
||||||
|
|
||||||
```swift
|
|
||||||
Defaults.observe<T: Codable>(
|
|
||||||
_ key: Defaults.OptionalKey<T>,
|
|
||||||
options: NSKeyValueObservingOptions = [.initial, .old, .new],
|
|
||||||
handler: @escaping (OptionalKeyChange<T>) -> Void
|
|
||||||
) -> DefaultsObservation
|
|
||||||
```
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
Defaults.observe<T: NSSecureCoding>(
|
Defaults.observe<T: NSSecureCoding>(
|
||||||
_ key: Defaults.NSSecureCodingOptionalKey<T>,
|
_ key: Defaults.NSSecureCodingOptionalKey<T>,
|
||||||
|
@ -402,13 +382,6 @@ Defaults.publisher<T: NSSecureCoding>(
|
||||||
) -> AnyPublisher<NSSecureCodingKeyChange<T>, Never>
|
) -> AnyPublisher<NSSecureCodingKeyChange<T>, Never>
|
||||||
```
|
```
|
||||||
|
|
||||||
```swift
|
|
||||||
Defaults.publisher<T: Codable>(
|
|
||||||
_ key: Defaults.OptionalKey<T>,
|
|
||||||
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
|
||||||
) -> AnyPublisher<OptionalKeyChange<T>, Never>
|
|
||||||
```
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
Defaults.publisher<T: NSSecureCoding>(
|
Defaults.publisher<T: NSSecureCoding>(
|
||||||
_ key: Defaults.NSSecureCodingOptionalKey<T>,
|
_ key: Defaults.NSSecureCodingOptionalKey<T>,
|
||||||
|
@ -438,13 +411,6 @@ Defaults.publisher<T: NSSecureCoding>(
|
||||||
) -> AnyPublisher<Void, Never> {
|
) -> AnyPublisher<Void, Never> {
|
||||||
```
|
```
|
||||||
|
|
||||||
```swift
|
|
||||||
Defaults.publisher<T: Codable>(
|
|
||||||
keys: Defaults.OptionalKey<T>...,
|
|
||||||
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
|
||||||
) -> AnyPublisher<Void, Never> {
|
|
||||||
```
|
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
Defaults.publisher<T: NSSecureCoding>(
|
Defaults.publisher<T: NSSecureCoding>(
|
||||||
keys: Defaults.NSSecureCodingOptionalKey<T>...,
|
keys: Defaults.NSSecureCodingOptionalKey<T>...,
|
||||||
|
|
Loading…
Reference in New Issue