Skip to content

Commit

Permalink
Get rid of OptionalKey
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Apr 13, 2020
1 parent 3275717 commit b2fdee2
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 230 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
language: swift
osx_image: xcode11.3
osx_image: xcode11.4
script: xcodebuild test -project Defaults.xcodeproj -scheme Defaults-macOS
2 changes: 1 addition & 1 deletion Defaults.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Pod::Spec.new do |s|
s.authors = { 'Sindre Sorhus' => 'sindresorhus@gmail.com' }
s.source = { :git => 'https://github.com/sindresorhus/Defaults.git', :tag => "v#{s.version}" }
s.source_files = 'Sources/**/*.swift'
s.swift_version = '5.1'
s.swift_version = '5.2'
s.macos.deployment_target = '10.12'
s.ios.deployment_target = '10.0'
s.tvos.deployment_target = '10.0'
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.1
// swift-tools-version:5.2
import PackageDescription

let package = Package(
Expand Down
121 changes: 37 additions & 84 deletions Sources/Defaults/Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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, *)
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, *)
public typealias NSSecureCodingOptionalKey = Defaults.NSSecureCodingOptionalKey

Expand All @@ -29,6 +27,10 @@ public final class Defaults {

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.
if UserDefaults.isNativelySupportedType(Value.self) {
suite.register(defaults: [key: defaultValue])
Expand All @@ -52,6 +54,10 @@ public final class Defaults {

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.
if UserDefaults.isNativelySupportedType(Value.self) {
suite.register(defaults: [key: defaultValue])
Expand All @@ -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, *)
public final class NSSecureCodingOptionalKey<Value: NSSecureCoding>: Keys {
public let name: String
Expand Down Expand Up @@ -103,15 +98,7 @@ public final class Defaults {
}
}

/// Access a defaults value using a `Defaults.OptionalKey`.
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`.
/// Access a defaults value using a `Defaults.NSSecureCodingOptionalKey`.
@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? {
get { key.suite[key] }
Expand Down Expand Up @@ -193,29 +180,6 @@ public final class Defaults {
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`.
Expand All @@ -228,31 +192,6 @@ public final class Defaults {
public static func reset<Value: NSSecureCoding>(_ keys: NSSecureCodingOptionalKey<Value>..., suite: UserDefaults = .standard) {
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`.
Expand All @@ -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 {
private func _get<Value: Codable>(_ key: String) -> Value? {
if UserDefaults.isNativelySupportedType(Value.self) {
Expand Down Expand Up @@ -334,6 +286,11 @@ extension UserDefaults {
}

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) {
set(value, forKey: key)
return
Expand All @@ -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, *)
private func _set<Value: NSSecureCoding>(_ key: String, to value: Value) {
// TODO: Handle nil here too.
if UserDefaults.isNativelySupportedType(Value.self) {
set(value, forKey: key)
return
Expand All @@ -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, *)
public subscript<Value: NSSecureCoding>(key: Defaults.NSSecureCodingOptionalKey<Value>) -> Value? {
get { _get(key.name) }
Expand All @@ -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 {
case
is Bool.Type,
is Bool?.Type, // swiftlint:disable:this discouraged_optional_boolean
is String.Type,
is String?.Type,
is Int.Type,
is Int?.Type,
is Double.Type,
is Double?.Type,
is Float.Type,
is Float?.Type,
is Date.Type,
is Data.Type:
is Date?.Type,
is Data.Type,
is Data?.Type:
return true
default:
return false
Expand Down
46 changes: 5 additions & 41 deletions Sources/Defaults/Observation+Combine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ extension Defaults {
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(
subscriber: subscriber,
suite: suite,
Expand All @@ -81,7 +81,7 @@ extension Defaults {
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
print(value)
Expand Down Expand Up @@ -114,20 +114,6 @@ extension Defaults {
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.
*/
Expand All @@ -154,29 +140,7 @@ extension Defaults {

let combinedPublisher =
keys.map { key in
return 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)
Defaults.publisher(key, options: options)
.map { _ in () }
.eraseToAnyPublisher()
}.reduce(initial) { (combined, keyPublisher) in
Expand All @@ -198,7 +162,7 @@ extension Defaults {

let combinedPublisher =
keys.map { key in
return Defaults.publisher(key, options: options)
Defaults.publisher(key, options: options)
.map { _ in () }
.eraseToAnyPublisher()
}.reduce(initial) { (combined, keyPublisher) in
Expand All @@ -220,7 +184,7 @@ extension Defaults {

let combinedPublisher =
keys.map { key in
return Defaults.publisher(key, options: options)
Defaults.publisher(key, options: options)
.map { _ in () }
.eraseToAnyPublisher()
}.reduce(initial) { (combined, keyPublisher) in
Expand Down
45 changes: 1 addition & 44 deletions Sources/Defaults/Observation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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, *)
public struct NSSecureCodingOptionalKeyChange<Value: NSSecureCoding> {
public let kind: NSKeyValueChange
Expand Down Expand Up @@ -179,6 +163,7 @@ extension Defaults {
lifetimeAssociation = LifetimeAssociation(of: self, with: weaklyHeldObject, deinitHandler: { [weak self] in
self?.invalidate()
})

return self
}

Expand Down Expand Up @@ -255,34 +240,6 @@ extension Defaults {
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.
*/
Expand Down
Loading

0 comments on commit b2fdee2

Please sign in to comment.