Meta tweaks

This commit is contained in:
Sindre Sorhus 2020-01-21 00:07:30 +07:00
parent 6029ac796b
commit ca813bf449
7 changed files with 134 additions and 127 deletions

View File

@ -1,3 +1,3 @@
language: swift language: swift
osx_image: xcode11.2 osx_image: xcode11.3
script: xcodebuild test -project Defaults.xcodeproj -scheme Defaults-macOS script: xcodebuild test -project Defaults.xcodeproj -scheme Defaults-macOS

View File

@ -68,7 +68,7 @@
AD2FAA281CD0B6E100659CF4 /* DefaultsTests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = DefaultsTests.plist; sourceTree = "<group>"; }; AD2FAA281CD0B6E100659CF4 /* DefaultsTests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = DefaultsTests.plist; sourceTree = "<group>"; };
DD75027A1C68FCFC006590AF /* Defaults-macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Defaults-macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; DD75027A1C68FCFC006590AF /* Defaults-macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Defaults-macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
DD75028D1C690C7A006590AF /* Defaults-tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Defaults-tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; DD75028D1C690C7A006590AF /* Defaults-tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Defaults-tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
E286D0C623B8D51100570D1E /* Observation+Combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Observation+Combine.swift"; sourceTree = "<group>"; }; E286D0C623B8D51100570D1E /* Observation+Combine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "Observation+Combine.swift"; sourceTree = "<group>"; usesTabs = 1; };
E3EB3E32216505920033B089 /* util.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = util.swift; sourceTree = "<group>"; usesTabs = 1; }; E3EB3E32216505920033B089 /* util.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = util.swift; sourceTree = "<group>"; usesTabs = 1; };
E3EB3E34216507AE0033B089 /* Observation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Observation.swift; sourceTree = "<group>"; usesTabs = 1; }; E3EB3E34216507AE0033B089 /* Observation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Observation.swift; sourceTree = "<group>"; usesTabs = 1; };
/* End PBXFileReference section */ /* End PBXFileReference section */

View File

@ -16,13 +16,13 @@ public final class Defaults {
fileprivate init() {} fileprivate init() {}
} }
public final class Key<T: Codable>: Keys { public final class Key<Value: Codable>: Keys {
public let name: String public let name: String
public let defaultValue: T public let defaultValue: Value
public let suite: UserDefaults public let suite: UserDefaults
/// Create a defaults key. /// Create a defaults key.
public init(_ key: String, default defaultValue: T, suite: UserDefaults = .standard) { public init(_ key: String, default defaultValue: Value, suite: UserDefaults = .standard) {
self.name = key self.name = key
self.defaultValue = defaultValue self.defaultValue = defaultValue
self.suite = suite self.suite = suite
@ -30,7 +30,7 @@ public final class Defaults {
super.init() super.init()
// 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(T.self) { if UserDefaults.isNativelySupportedType(Value.self) {
suite.register(defaults: [key: defaultValue]) suite.register(defaults: [key: defaultValue])
} else if let value = suite._encode(defaultValue) { } else if let value = suite._encode(defaultValue) {
suite.register(defaults: [key: value]) suite.register(defaults: [key: value])
@ -39,13 +39,13 @@ 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 final class NSSecureCodingKey<T: NSSecureCoding>: Keys { public final class NSSecureCodingKey<Value: NSSecureCoding>: Keys {
public let name: String public let name: String
public let defaultValue: T public let defaultValue: Value
public let suite: UserDefaults public let suite: UserDefaults
/// Create a defaults key. /// Create a defaults key.
public init(_ key: String, default defaultValue: T, suite: UserDefaults = .standard) { public init(_ key: String, default defaultValue: Value, suite: UserDefaults = .standard) {
self.name = key self.name = key
self.defaultValue = defaultValue self.defaultValue = defaultValue
self.suite = suite self.suite = suite
@ -53,7 +53,7 @@ public final class Defaults {
super.init() super.init()
// 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(T.self) { if UserDefaults.isNativelySupportedType(Value.self) {
suite.register(defaults: [key: defaultValue]) suite.register(defaults: [key: defaultValue])
} else if let value = try? NSKeyedArchiver.archivedData(withRootObject: defaultValue, requiringSecureCoding: true) { } else if let value = try? NSKeyedArchiver.archivedData(withRootObject: defaultValue, requiringSecureCoding: true) {
suite.register(defaults: [key: value]) suite.register(defaults: [key: value])
@ -61,7 +61,7 @@ public final class Defaults {
} }
} }
public final class OptionalKey<T: Codable>: Keys { public final class OptionalKey<Value: Codable>: Keys {
public let name: String public let name: String
public let suite: UserDefaults public let suite: UserDefaults
@ -73,7 +73,7 @@ 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 final class NSSecureCodingOptionalKey<T: NSSecureCoding>: Keys { public final class NSSecureCodingOptionalKey<Value: NSSecureCoding>: Keys {
public let name: String public let name: String
public let suite: UserDefaults public let suite: UserDefaults
@ -87,7 +87,7 @@ public final class Defaults {
fileprivate init() {} fileprivate init() {}
/// Access a defaults value using a `Defaults.Key`. /// Access a defaults value using a `Defaults.Key`.
public static subscript<T: Codable>(key: Key<T>) -> T { public static subscript<Value: Codable>(key: Key<Value>) -> Value {
get { key.suite[key] } get { key.suite[key] }
set { set {
key.suite[key] = newValue key.suite[key] = newValue
@ -96,7 +96,7 @@ public final class Defaults {
/// Access a defaults value using a `Defaults.NSSecureCodingKey`. /// Access a defaults value using a `Defaults.NSSecureCodingKey`.
@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<T: NSSecureCoding>(key: NSSecureCodingKey<T>) -> T { public static subscript<Value: NSSecureCoding>(key: NSSecureCodingKey<Value>) -> Value {
get { key.suite[key] } get { key.suite[key] }
set { set {
key.suite[key] = newValue key.suite[key] = newValue
@ -104,7 +104,7 @@ public final class Defaults {
} }
/// Access a defaults value using a `Defaults.OptionalKey`. /// Access a defaults value using a `Defaults.OptionalKey`.
public static subscript<T: Codable>(key: OptionalKey<T>) -> T? { public static subscript<Value: Codable>(key: OptionalKey<Value>) -> Value? {
get { key.suite[key] } get { key.suite[key] }
set { set {
key.suite[key] = newValue key.suite[key] = newValue
@ -113,7 +113,7 @@ public final class Defaults {
/// Access a defaults value using a `Defaults.OptionalKey`. /// 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<T: NSSecureCoding>(key: NSSecureCodingOptionalKey<T>) -> T? { public static subscript<Value: NSSecureCoding>(key: NSSecureCodingOptionalKey<Value>) -> Value? {
get { key.suite[key] } get { key.suite[key] }
set { set {
key.suite[key] = newValue key.suite[key] = newValue
@ -140,7 +140,7 @@ public final class Defaults {
//=> false //=> false
``` ```
*/ */
public static func reset<T: Codable>(_ keys: Key<T>..., suite: UserDefaults = .standard) { public static func reset<Value: Codable>(_ keys: Key<Value>..., suite: UserDefaults = .standard) {
reset(keys, suite: suite) reset(keys, suite: suite)
} }
@ -151,7 +151,7 @@ public final class Defaults {
- Parameter suite: `UserDefaults` suite. - Parameter suite: `UserDefaults` 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 static func reset<T: NSSecureCoding>(_ keys: NSSecureCodingKey<T>..., suite: UserDefaults = .standard) { public static func reset<Value: NSSecureCoding>(_ keys: NSSecureCodingKey<Value>..., suite: UserDefaults = .standard) {
reset(keys, suite: suite) reset(keys, suite: suite)
} }
@ -175,7 +175,7 @@ public final class Defaults {
//=> false //=> false
``` ```
*/ */
public static func reset<T: Codable>(_ keys: [Key<T>], suite: UserDefaults = .standard) { public static func reset<Value: Codable>(_ keys: [Key<Value>], suite: UserDefaults = .standard) {
for key in keys { for key in keys {
key.suite[key] = key.defaultValue key.suite[key] = key.defaultValue
} }
@ -188,7 +188,7 @@ public final class Defaults {
- Parameter suite: `UserDefaults` suite. - Parameter suite: `UserDefaults` 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 static func reset<T: NSSecureCoding>(_ keys: [NSSecureCodingKey<T>], suite: UserDefaults = .standard) { public static func reset<Value: NSSecureCoding>(_ keys: [NSSecureCodingKey<Value>], suite: UserDefaults = .standard) {
for key in keys { for key in keys {
key.suite[key] = key.defaultValue key.suite[key] = key.defaultValue
} }
@ -213,7 +213,7 @@ public final class Defaults {
//=> nil //=> nil
``` ```
*/ */
public static func reset<T: Codable>(_ keys: OptionalKey<T>..., suite: UserDefaults = .standard) { public static func reset<Value: Codable>(_ keys: OptionalKey<Value>..., suite: UserDefaults = .standard) {
reset(keys, suite: suite) reset(keys, suite: suite)
} }
@ -225,7 +225,7 @@ 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 static func reset<T: NSSecureCoding>(_ keys: NSSecureCodingOptionalKey<T>..., suite: UserDefaults = .standard) { public static func reset<Value: NSSecureCoding>(_ keys: NSSecureCodingOptionalKey<Value>..., suite: UserDefaults = .standard) {
reset(keys, suite: suite) reset(keys, suite: suite)
} }
@ -248,7 +248,7 @@ public final class Defaults {
//=> nil //=> nil
``` ```
*/ */
public static func reset<T: Codable>(_ keys: [OptionalKey<T>], suite: UserDefaults = .standard) { public static func reset<Value: Codable>(_ keys: [OptionalKey<Value>], suite: UserDefaults = .standard) {
for key in keys { for key in keys {
key.suite[key] = nil key.suite[key] = nil
} }
@ -261,7 +261,7 @@ public final class Defaults {
- Parameter suite: `UserDefaults` suite. - Parameter suite: `UserDefaults` 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 static func reset<T: NSSecureCoding>(_ keys: [NSSecureCodingOptionalKey<T>], suite: UserDefaults = .standard) { public static func reset<Value: NSSecureCoding>(_ keys: [NSSecureCodingOptionalKey<Value>], suite: UserDefaults = .standard) {
for key in keys { for key in keys {
key.suite[key] = nil key.suite[key] = nil
} }
@ -278,9 +278,9 @@ public final class Defaults {
} }
extension UserDefaults { extension UserDefaults {
private func _get<T: Codable>(_ key: String) -> T? { private func _get<Value: Codable>(_ key: String) -> Value? {
if UserDefaults.isNativelySupportedType(T.self) { if UserDefaults.isNativelySupportedType(Value.self) {
return object(forKey: key) as? T return object(forKey: key) as? Value
} }
guard guard
@ -291,7 +291,7 @@ extension UserDefaults {
} }
do { do {
return (try JSONDecoder().decode([T].self, from: data)).first return (try JSONDecoder().decode([Value].self, from: data)).first
} catch { } catch {
print(error) print(error)
} }
@ -300,9 +300,9 @@ 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 _get<T: NSSecureCoding>(_ key: String) -> T? { private func _get<Value: NSSecureCoding>(_ key: String) -> Value? {
if UserDefaults.isNativelySupportedType(T.self) { if UserDefaults.isNativelySupportedType(Value.self) {
return object(forKey: key) as? T return object(forKey: key) as? Value
} }
guard guard
@ -312,7 +312,7 @@ extension UserDefaults {
} }
do { do {
return try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? T return try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Value
} catch { } catch {
print(error) print(error)
} }
@ -320,7 +320,7 @@ extension UserDefaults {
return nil return nil
} }
fileprivate func _encode<T: Codable>(_ value: T) -> String? { fileprivate func _encode<Value: Codable>(_ value: Value) -> String? {
do { do {
// Some codable values like URL and enum are encoded as a top-level // Some codable values like URL and enum are encoded as a top-level
// string which JSON can't handle, so we need to wrap it in an array // string which JSON can't handle, so we need to wrap it in an array
@ -333,8 +333,8 @@ extension UserDefaults {
} }
} }
private func _set<T: Codable>(_ key: String, to value: T) { private func _set<Value: Codable>(_ key: String, to value: Value) {
if UserDefaults.isNativelySupportedType(T.self) { if UserDefaults.isNativelySupportedType(Value.self) {
set(value, forKey: key) set(value, forKey: key)
return return
} }
@ -343,8 +343,8 @@ 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<T: NSSecureCoding>(_ key: String, to value: T) { private func _set<Value: NSSecureCoding>(_ key: String, to value: Value) {
if UserDefaults.isNativelySupportedType(T.self) { if UserDefaults.isNativelySupportedType(Value.self) {
set(value, forKey: key) set(value, forKey: key)
return return
} }
@ -352,7 +352,7 @@ extension UserDefaults {
set(try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true), forKey: key) set(try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true), forKey: key)
} }
public subscript<T: Codable>(key: Defaults.Key<T>) -> T { public subscript<Value: Codable>(key: Defaults.Key<Value>) -> Value {
get { _get(key.name) ?? key.defaultValue } get { _get(key.name) ?? key.defaultValue }
set { set {
_set(key.name, to: newValue) _set(key.name, to: newValue)
@ -360,14 +360,14 @@ 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, *)
public subscript<T: NSSecureCoding>(key: Defaults.NSSecureCodingKey<T>) -> T { public subscript<Value: NSSecureCoding>(key: Defaults.NSSecureCodingKey<Value>) -> Value {
get { _get(key.name) ?? key.defaultValue } get { _get(key.name) ?? key.defaultValue }
set { set {
_set(key.name, to: newValue) _set(key.name, to: newValue)
} }
} }
public subscript<T: Codable>(key: Defaults.OptionalKey<T>) -> T? { public subscript<Value: Codable>(key: Defaults.OptionalKey<Value>) -> Value? {
get { _get(key.name) } get { _get(key.name) }
set { set {
guard let value = newValue else { guard let value = newValue else {
@ -380,7 +380,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, *)
public subscript<T: NSSecureCoding>(key: Defaults.NSSecureCodingOptionalKey<T>) -> T? { public subscript<Value: NSSecureCoding>(key: Defaults.NSSecureCodingOptionalKey<Value>) -> Value? {
get { _get(key.name) } get { _get(key.name) }
set { set {
guard let value = newValue else { guard let value = newValue else {
@ -392,9 +392,10 @@ extension UserDefaults {
} }
} }
fileprivate static func isNativelySupportedType<T>(_ type: T.Type) -> Bool { fileprivate static func isNativelySupportedType<Value>(_ type: Value.Type) -> Bool {
switch type { switch type {
case is Bool.Type, case
is Bool.Type,
is String.Type, is String.Type,
is Int.Type, is Int.Type,
is Double.Type, is Double.Type,

View File

@ -1,3 +1,5 @@
#if canImport(Combine)
import Foundation import Foundation
import Combine import Combine
@ -82,12 +84,12 @@ extension Defaults {
``` ```
*/ */
@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, *)
public static func publisher<T: Codable>( public static func publisher<Value: Codable>(
_ key: Defaults.Key<T>, _ key: Defaults.Key<Value>,
options: NSKeyValueObservingOptions = [.initial, .old, .new] options: NSKeyValueObservingOptions = [.initial, .old, .new]
) -> AnyPublisher<KeyChange<T>, Never> { ) -> AnyPublisher<KeyChange<Value>, Never> {
let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options) let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options)
.map { KeyChange<T>(change: $0, defaultValue: key.defaultValue) } .map { KeyChange<Value>(change: $0, defaultValue: key.defaultValue) }
return AnyPublisher(publisher) return AnyPublisher(publisher)
} }
@ -96,12 +98,12 @@ extension Defaults {
Returns a type-erased `Publisher` that publishes changes related to the given key. Returns a type-erased `Publisher` that publishes changes related to the given 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, *) @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<T: NSSecureCoding>( public static func publisher<Value: NSSecureCoding>(
_ key: Defaults.NSSecureCodingKey<T>, _ key: Defaults.NSSecureCodingKey<Value>,
options: NSKeyValueObservingOptions = [.initial, .old, .new] options: NSKeyValueObservingOptions = [.initial, .old, .new]
) -> AnyPublisher<NSSecureCodingKeyChange<T>, Never> { ) -> AnyPublisher<NSSecureCodingKeyChange<Value>, Never> {
let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options) let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options)
.map { NSSecureCodingKeyChange<T>(change: $0, defaultValue: key.defaultValue) } .map { NSSecureCodingKeyChange<Value>(change: $0, defaultValue: key.defaultValue) }
return AnyPublisher(publisher) return AnyPublisher(publisher)
} }
@ -110,12 +112,12 @@ extension Defaults {
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.
*/ */
@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, *)
public static func publisher<T: Codable>( public static func publisher<Value: Codable>(
_ key: Defaults.OptionalKey<T>, _ key: Defaults.OptionalKey<Value>,
options: NSKeyValueObservingOptions = [.initial, .old, .new] options: NSKeyValueObservingOptions = [.initial, .old, .new]
) -> AnyPublisher<OptionalKeyChange<T>, Never> { ) -> AnyPublisher<OptionalKeyChange<Value>, Never> {
let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options) let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options)
.map { OptionalKeyChange<T>(change: $0) } .map { OptionalKeyChange<Value>(change: $0) }
return AnyPublisher(publisher) return AnyPublisher(publisher)
} }
@ -124,12 +126,12 @@ extension Defaults {
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.
*/ */
@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, *)
public static func publisher<T: NSSecureCoding>( public static func publisher<Value: NSSecureCoding>(
_ key: Defaults.NSSecureCodingOptionalKey<T>, _ key: Defaults.NSSecureCodingOptionalKey<Value>,
options: NSKeyValueObservingOptions = [.initial, .old, .new] options: NSKeyValueObservingOptions = [.initial, .old, .new]
) -> AnyPublisher<NSSecureCodingOptionalKeyChange<T>, Never> { ) -> AnyPublisher<NSSecureCodingOptionalKeyChange<Value>, Never> {
let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options) let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options)
.map { NSSecureCodingOptionalKeyChange<T>(change: $0) } .map { NSSecureCodingOptionalKeyChange<Value>(change: $0) }
return AnyPublisher(publisher) return AnyPublisher(publisher)
} }
@ -138,8 +140,8 @@ extension Defaults {
Publisher for multiple `Key<T>` observation, but without specific information about changes. Publisher for multiple `Key<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, *) @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<T: Codable>( public static func publisher<Value: Codable>(
keys: Defaults.Key<T>..., keys: Defaults.Key<Value>...,
options: NSKeyValueObservingOptions = [.initial, .old, .new] options: NSKeyValueObservingOptions = [.initial, .old, .new]
) -> AnyPublisher<Void, Never> { ) -> AnyPublisher<Void, Never> {
let initial = Empty<Void, Never>(completeImmediately: false).eraseToAnyPublisher() let initial = Empty<Void, Never>(completeImmediately: false).eraseToAnyPublisher()
@ -160,8 +162,8 @@ extension Defaults {
Publisher for multiple `OptionalKey<T>` observation, but without specific information about changes. 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, *) @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<T: Codable>( public static func publisher<Value: Codable>(
keys: Defaults.OptionalKey<T>..., keys: Defaults.OptionalKey<Value>...,
options: NSKeyValueObservingOptions = [.initial, .old, .new] options: NSKeyValueObservingOptions = [.initial, .old, .new]
) -> AnyPublisher<Void, Never> { ) -> AnyPublisher<Void, Never> {
let initial = Empty<Void, Never>(completeImmediately: false).eraseToAnyPublisher() let initial = Empty<Void, Never>(completeImmediately: false).eraseToAnyPublisher()
@ -182,8 +184,8 @@ extension Defaults {
Publisher for multiple `NSSecureCodingKey<T>` observation, but without specific information about changes. Publisher for multiple `NSSecureCodingKey<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, *) @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<T: NSSecureCoding>( public static func publisher<Value: NSSecureCoding>(
keys: Defaults.NSSecureCodingKey<T>..., keys: Defaults.NSSecureCodingKey<Value>...,
options: NSKeyValueObservingOptions = [.initial, .old, .new] options: NSKeyValueObservingOptions = [.initial, .old, .new]
) -> AnyPublisher<Void, Never> { ) -> AnyPublisher<Void, Never> {
let initial = Empty<Void, Never>(completeImmediately: false).eraseToAnyPublisher() let initial = Empty<Void, Never>(completeImmediately: false).eraseToAnyPublisher()
@ -204,8 +206,8 @@ extension Defaults {
Publisher for multiple `NSSecureCodingOptionalKey<T>` observation, but without specific information about changes. Publisher for multiple `NSSecureCodingOptionalKey<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, *) @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<T: NSSecureCoding>( public static func publisher<Value: NSSecureCoding>(
keys: Defaults.NSSecureCodingOptionalKey<T>..., keys: Defaults.NSSecureCodingOptionalKey<Value>...,
options: NSKeyValueObservingOptions = [.initial, .old, .new] options: NSKeyValueObservingOptions = [.initial, .old, .new]
) -> AnyPublisher<Void, Never> { ) -> AnyPublisher<Void, Never> {
let initial = Empty<Void, Never>(completeImmediately: false).eraseToAnyPublisher() let initial = Empty<Void, Never>(completeImmediately: false).eraseToAnyPublisher()
@ -243,3 +245,5 @@ extension Defaults {
} }
} }
} }
#endif

View File

@ -26,7 +26,7 @@ public protocol DefaultsObservation: AnyObject {
} }
extension Defaults { extension Defaults {
private static func deserialize<T: Decodable>(_ value: Any?, to type: T.Type) -> T? { private static func deserialize<Value: Decodable>(_ value: Any?, to type: Value.Type) -> Value? {
guard guard
let value = value, let value = value,
!(value is NSNull) !(value is NSNull)
@ -35,16 +35,16 @@ extension Defaults {
} }
// This handles the case where the value was a plist value using `isNativelySupportedType` // This handles the case where the value was a plist value using `isNativelySupportedType`
if let value = value as? T { if let value = value as? Value {
return value return value
} }
// Using the array trick as done below in `UserDefaults#_set()` // Using the array trick as done below in `UserDefaults#_set()`
return [T].init(jsonString: "\([value])")?.first return [Value].init(jsonString: "\([value])")?.first
} }
@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 static func deserialize<T: NSSecureCoding>(_ value: Any?, to type: T.Type) -> T? { private static func deserialize<Value: NSSecureCoding>(_ value: Any?, to type: Value.Type) -> Value? {
guard guard
let value = value, let value = value,
!(value is NSNull) !(value is NSNull)
@ -53,7 +53,7 @@ extension Defaults {
} }
// This handles the case where the value was a plist value using `isNativelySupportedType` // This handles the case where the value was a plist value using `isNativelySupportedType`
if let value = value as? T { if let value = value as? Value {
return value return value
} }
@ -61,7 +61,7 @@ extension Defaults {
return nil return nil
} }
return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(dataValue) as? T return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(dataValue) as? Value
} }
final class BaseChange { final class BaseChange {
@ -80,69 +80,69 @@ extension Defaults {
} }
} }
public struct KeyChange<T: Codable> { public struct KeyChange<Value: Codable> {
public let kind: NSKeyValueChange public let kind: NSKeyValueChange
public let indexes: IndexSet? public let indexes: IndexSet?
public let isPrior: Bool public let isPrior: Bool
public let newValue: T public let newValue: Value
public let oldValue: T public let oldValue: Value
init(change: BaseChange, defaultValue: T) { init(change: BaseChange, defaultValue: Value) {
self.kind = change.kind self.kind = change.kind
self.indexes = change.indexes self.indexes = change.indexes
self.isPrior = change.isPrior self.isPrior = change.isPrior
self.oldValue = deserialize(change.oldValue, to: T.self) ?? defaultValue self.oldValue = deserialize(change.oldValue, to: Value.self) ?? defaultValue
self.newValue = deserialize(change.newValue, to: T.self) ?? defaultValue self.newValue = deserialize(change.newValue, to: Value.self) ?? defaultValue
} }
} }
@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 NSSecureCodingKeyChange<T: NSSecureCoding> { public struct NSSecureCodingKeyChange<Value: NSSecureCoding> {
public let kind: NSKeyValueChange public let kind: NSKeyValueChange
public let indexes: IndexSet? public let indexes: IndexSet?
public let isPrior: Bool public let isPrior: Bool
public let newValue: T public let newValue: Value
public let oldValue: T public let oldValue: Value
init(change: BaseChange, defaultValue: T) { init(change: BaseChange, defaultValue: Value) {
self.kind = change.kind self.kind = change.kind
self.indexes = change.indexes self.indexes = change.indexes
self.isPrior = change.isPrior self.isPrior = change.isPrior
self.oldValue = deserialize(change.oldValue, to: T.self) ?? defaultValue self.oldValue = deserialize(change.oldValue, to: Value.self) ?? defaultValue
self.newValue = deserialize(change.newValue, to: T.self) ?? defaultValue self.newValue = deserialize(change.newValue, to: Value.self) ?? defaultValue
} }
} }
public struct OptionalKeyChange<T: Codable> { public struct OptionalKeyChange<Value: Codable> {
public let kind: NSKeyValueChange public let kind: NSKeyValueChange
public let indexes: IndexSet? public let indexes: IndexSet?
public let isPrior: Bool public let isPrior: Bool
public let newValue: T? public let newValue: Value?
public let oldValue: T? public let oldValue: Value?
init(change: BaseChange) { init(change: BaseChange) {
self.kind = change.kind self.kind = change.kind
self.indexes = change.indexes self.indexes = change.indexes
self.isPrior = change.isPrior self.isPrior = change.isPrior
self.oldValue = deserialize(change.oldValue, to: T.self) self.oldValue = deserialize(change.oldValue, to: Value.self)
self.newValue = deserialize(change.newValue, to: T.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<T: NSSecureCoding> { public struct NSSecureCodingOptionalKeyChange<Value: NSSecureCoding> {
public let kind: NSKeyValueChange public let kind: NSKeyValueChange
public let indexes: IndexSet? public let indexes: IndexSet?
public let isPrior: Bool public let isPrior: Bool
public let newValue: T? public let newValue: Value?
public let oldValue: T? public let oldValue: Value?
init(change: BaseChange) { init(change: BaseChange) {
self.kind = change.kind self.kind = change.kind
self.indexes = change.indexes self.indexes = change.indexes
self.isPrior = change.isPrior self.isPrior = change.isPrior
self.oldValue = deserialize(change.oldValue, to: T.self) self.oldValue = deserialize(change.oldValue, to: Value.self)
self.newValue = deserialize(change.newValue, to: T.self) self.newValue = deserialize(change.newValue, to: Value.self)
} }
} }
@ -223,14 +223,14 @@ extension Defaults {
} }
``` ```
*/ */
public static func observe<T: Codable>( public static func observe<Value: Codable>(
_ key: Defaults.Key<T>, _ key: Defaults.Key<Value>,
options: NSKeyValueObservingOptions = [.initial, .old, .new], options: NSKeyValueObservingOptions = [.initial, .old, .new],
handler: @escaping (KeyChange<T>) -> Void handler: @escaping (KeyChange<Value>) -> Void
) -> DefaultsObservation { ) -> DefaultsObservation {
let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in
handler( handler(
KeyChange<T>(change: change, defaultValue: key.defaultValue) KeyChange<Value>(change: change, defaultValue: key.defaultValue)
) )
} }
observation.start(options: options) observation.start(options: options)
@ -241,14 +241,14 @@ extension Defaults {
Observe a defaults key. Observe a defaults key.
*/ */
@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 func observe<T: NSSecureCoding>( public static func observe<Value: NSSecureCoding>(
_ key: Defaults.NSSecureCodingKey<T>, _ key: Defaults.NSSecureCodingKey<Value>,
options: NSKeyValueObservingOptions = [.initial, .old, .new], options: NSKeyValueObservingOptions = [.initial, .old, .new],
handler: @escaping (NSSecureCodingKeyChange<T>) -> Void handler: @escaping (NSSecureCodingKeyChange<Value>) -> Void
) -> DefaultsObservation { ) -> DefaultsObservation {
let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in
handler( handler(
NSSecureCodingKeyChange<T>(change: change, defaultValue: key.defaultValue) NSSecureCodingKeyChange<Value>(change: change, defaultValue: key.defaultValue)
) )
} }
observation.start(options: options) observation.start(options: options)
@ -269,14 +269,14 @@ extension Defaults {
} }
``` ```
*/ */
public static func observe<T: Codable>( public static func observe<Value: Codable>(
_ key: Defaults.OptionalKey<T>, _ key: Defaults.OptionalKey<Value>,
options: NSKeyValueObservingOptions = [.initial, .old, .new], options: NSKeyValueObservingOptions = [.initial, .old, .new],
handler: @escaping (OptionalKeyChange<T>) -> Void handler: @escaping (OptionalKeyChange<Value>) -> Void
) -> DefaultsObservation { ) -> DefaultsObservation {
let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in
handler( handler(
OptionalKeyChange<T>(change: change) OptionalKeyChange<Value>(change: change)
) )
} }
observation.start(options: options) observation.start(options: options)
@ -287,14 +287,14 @@ extension Defaults {
Observe an optional defaults key. Observe an optional defaults key.
*/ */
@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 func observe<T: NSSecureCoding>( public static func observe<Value: NSSecureCoding>(
_ key: Defaults.NSSecureCodingOptionalKey<T>, _ key: Defaults.NSSecureCodingOptionalKey<Value>,
options: NSKeyValueObservingOptions = [.initial, .old, .new], options: NSKeyValueObservingOptions = [.initial, .old, .new],
handler: @escaping (NSSecureCodingOptionalKeyChange<T>) -> Void handler: @escaping (NSSecureCodingOptionalKeyChange<Value>) -> Void
) -> DefaultsObservation { ) -> DefaultsObservation {
let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in let observation = UserDefaultsKeyObservation(object: key.suite, key: key.name) { change in
handler( handler(
NSSecureCodingOptionalKeyChange<T>(change: change) NSSecureCodingOptionalKeyChange<Value>(change: change)
) )
} }
observation.start(options: options) observation.start(options: options)

View File

@ -2,6 +2,8 @@
> 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).
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.
It's used in production by apps like [Gifski](https://github.com/sindresorhus/Gifski), [Dato](https://sindresorhus.com/dato), [Lungo](https://sindresorhus.com/lungo), [Battery Indicator](https://sindresorhus.com/battery-indicator), and [HEIC Converter](https://sindresorhus.com/heic-converter). It's used in production by apps like [Gifski](https://github.com/sindresorhus/Gifski), [Dato](https://sindresorhus.com/dato), [Lungo](https://sindresorhus.com/lungo), [Battery Indicator](https://sindresorhus.com/battery-indicator), and [HEIC Converter](https://sindresorhus.com/heic-converter).