Get rid of `OptionalKey`

This commit is contained in:
Sindre Sorhus 2020-01-21 14:29:57 +07:00
parent 3275717838
commit b2fdee2055
9 changed files with 86 additions and 230 deletions

View File

@ -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

View File

@ -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'

View File

@ -1,4 +1,4 @@
// swift-tools-version:5.1 // swift-tools-version:5.2
import PackageDescription import PackageDescription
let package = Package( let package = Package(

View File

@ -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

View File

@ -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

View File

@ -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.
*/ */

View File

@ -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
}

View File

@ -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

View File

@ -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>...,