Combine support (#31)
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
This commit is contained in:
parent
a2e2be2d5d
commit
6029ac796b
|
@ -17,6 +17,10 @@
|
||||||
8933C7901EB5B82D000D00A4 /* DefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7891EB5B82A000D00A4 /* DefaultsTests.swift */; };
|
8933C7901EB5B82D000D00A4 /* DefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8933C7891EB5B82A000D00A4 /* DefaultsTests.swift */; };
|
||||||
DD7502881C68FEDE006590AF /* Defaults.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6DA0F1BF000BD002C0205 /* Defaults.framework */; };
|
DD7502881C68FEDE006590AF /* Defaults.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6DA0F1BF000BD002C0205 /* Defaults.framework */; };
|
||||||
DD7502921C690C7A006590AF /* Defaults.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D9F01BEFFFBE002C0205 /* Defaults.framework */; };
|
DD7502921C690C7A006590AF /* Defaults.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D9F01BEFFFBE002C0205 /* Defaults.framework */; };
|
||||||
|
E286D0C723B8D51100570D1E /* Observation+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E286D0C623B8D51100570D1E /* Observation+Combine.swift */; };
|
||||||
|
E286D0C823B8D54C00570D1E /* Observation+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E286D0C623B8D51100570D1E /* Observation+Combine.swift */; };
|
||||||
|
E286D0C923B8D54D00570D1E /* Observation+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E286D0C623B8D51100570D1E /* Observation+Combine.swift */; };
|
||||||
|
E286D0CA23B8D54E00570D1E /* Observation+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = E286D0C623B8D51100570D1E /* Observation+Combine.swift */; };
|
||||||
E3EB3E33216505920033B089 /* util.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* util.swift */; };
|
E3EB3E33216505920033B089 /* util.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* util.swift */; };
|
||||||
E3EB3E35216507AE0033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; };
|
E3EB3E35216507AE0033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; };
|
||||||
E3EB3E36216507B50033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; };
|
E3EB3E36216507B50033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; };
|
||||||
|
@ -64,6 +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>"; };
|
||||||
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 */
|
||||||
|
@ -197,6 +202,7 @@
|
||||||
children = (
|
children = (
|
||||||
8933C7841EB5B820000D00A4 /* Defaults.swift */,
|
8933C7841EB5B820000D00A4 /* Defaults.swift */,
|
||||||
E3EB3E34216507AE0033B089 /* Observation.swift */,
|
E3EB3E34216507AE0033B089 /* Observation.swift */,
|
||||||
|
E286D0C623B8D51100570D1E /* Observation+Combine.swift */,
|
||||||
E3EB3E32216505920033B089 /* util.swift */,
|
E3EB3E32216505920033B089 /* util.swift */,
|
||||||
);
|
);
|
||||||
path = Defaults;
|
path = Defaults;
|
||||||
|
@ -483,6 +489,7 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
E286D0C823B8D54C00570D1E /* Observation+Combine.swift in Sources */,
|
||||||
8933C7851EB5B820000D00A4 /* Defaults.swift in Sources */,
|
8933C7851EB5B820000D00A4 /* Defaults.swift in Sources */,
|
||||||
E3EB3E35216507AE0033B089 /* Observation.swift in Sources */,
|
E3EB3E35216507AE0033B089 /* Observation.swift in Sources */,
|
||||||
E3EB3E33216505920033B089 /* util.swift in Sources */,
|
E3EB3E33216505920033B089 /* util.swift in Sources */,
|
||||||
|
@ -501,6 +508,7 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
E286D0CA23B8D54E00570D1E /* Observation+Combine.swift in Sources */,
|
||||||
E3EB3E3A216507C40033B089 /* util.swift in Sources */,
|
E3EB3E3A216507C40033B089 /* util.swift in Sources */,
|
||||||
E3EB3E37216507B50033B089 /* Observation.swift in Sources */,
|
E3EB3E37216507B50033B089 /* Observation.swift in Sources */,
|
||||||
8933C7871EB5B820000D00A4 /* Defaults.swift in Sources */,
|
8933C7871EB5B820000D00A4 /* Defaults.swift in Sources */,
|
||||||
|
@ -511,6 +519,7 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
E286D0C923B8D54D00570D1E /* Observation+Combine.swift in Sources */,
|
||||||
E3EB3E3B216507C40033B089 /* util.swift in Sources */,
|
E3EB3E3B216507C40033B089 /* util.swift in Sources */,
|
||||||
E3EB3E38216507B60033B089 /* Observation.swift in Sources */,
|
E3EB3E38216507B60033B089 /* Observation.swift in Sources */,
|
||||||
8933C7881EB5B820000D00A4 /* Defaults.swift in Sources */,
|
8933C7881EB5B820000D00A4 /* Defaults.swift in Sources */,
|
||||||
|
@ -521,6 +530,7 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
E286D0C723B8D51100570D1E /* Observation+Combine.swift in Sources */,
|
||||||
E3EB3E39216507C30033B089 /* util.swift in Sources */,
|
E3EB3E39216507C30033B089 /* util.swift in Sources */,
|
||||||
E3EB3E36216507B50033B089 /* Observation.swift in Sources */,
|
E3EB3E36216507B50033B089 /* Observation.swift in Sources */,
|
||||||
8933C7861EB5B820000D00A4 /* Defaults.swift in Sources */,
|
8933C7861EB5B820000D00A4 /* Defaults.swift in Sources */,
|
||||||
|
|
|
@ -0,0 +1,245 @@
|
||||||
|
import Foundation
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
extension Defaults {
|
||||||
|
/**
|
||||||
|
Custom `Subscription` for `UserDefaults` key observation.
|
||||||
|
*/
|
||||||
|
@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, *)
|
||||||
|
final class DefaultsSubscription<SubscriberType: Subscriber>: Subscription where SubscriberType.Input == BaseChange {
|
||||||
|
private var subscriber: SubscriberType?
|
||||||
|
private var observation: UserDefaultsKeyObservation?
|
||||||
|
|
||||||
|
init(subscriber: SubscriberType, suite: UserDefaults, key: String, options: NSKeyValueObservingOptions) {
|
||||||
|
self.subscriber = subscriber
|
||||||
|
self.observation = UserDefaultsKeyObservation(
|
||||||
|
object: suite,
|
||||||
|
key: key,
|
||||||
|
callback: observationCallback(_:)
|
||||||
|
)
|
||||||
|
self.observation?.start(options: options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func request(_ demand: Subscribers.Demand) {
|
||||||
|
// Nothing as we send events only when they occur.
|
||||||
|
}
|
||||||
|
|
||||||
|
func cancel() {
|
||||||
|
observation?.invalidate()
|
||||||
|
observation = nil
|
||||||
|
subscriber = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private func observationCallback(_ change: BaseChange) {
|
||||||
|
_ = subscriber?.receive(change)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Custom Publisher, which is using DefaultsSubscription.
|
||||||
|
*/
|
||||||
|
@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, *)
|
||||||
|
struct DefaultsPublisher: Publisher {
|
||||||
|
typealias Output = BaseChange
|
||||||
|
typealias Failure = Never
|
||||||
|
|
||||||
|
private let suite: UserDefaults
|
||||||
|
private let key: String
|
||||||
|
private let options: NSKeyValueObservingOptions
|
||||||
|
|
||||||
|
init(suite: UserDefaults, key: String, options: NSKeyValueObservingOptions) {
|
||||||
|
self.suite = suite
|
||||||
|
self.key = key
|
||||||
|
self.options = options
|
||||||
|
}
|
||||||
|
|
||||||
|
func receive<S>(subscriber: S) where S : Subscriber, DefaultsPublisher.Failure == S.Failure, DefaultsPublisher.Output == S.Input {
|
||||||
|
let subscription = DefaultsSubscription(
|
||||||
|
subscriber: subscriber,
|
||||||
|
suite: suite,
|
||||||
|
key: key,
|
||||||
|
options: options
|
||||||
|
)
|
||||||
|
|
||||||
|
subscriber.receive(subscription: subscription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a type-erased `Publisher` that publishes changes related to the given key.
|
||||||
|
|
||||||
|
```
|
||||||
|
extension Defaults.Keys {
|
||||||
|
static let isUnicornMode = Key<Bool>("isUnicornMode", default: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
let publisher = Defaults.publisher(.isUnicornMode).map { $0.newValue }
|
||||||
|
|
||||||
|
let cancellable = publisher.sink { value in
|
||||||
|
print(value)
|
||||||
|
//=> false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
@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>(
|
||||||
|
_ key: Defaults.Key<T>,
|
||||||
|
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
||||||
|
) -> AnyPublisher<KeyChange<T>, Never> {
|
||||||
|
let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options)
|
||||||
|
.map { KeyChange<T>(change: $0, defaultValue: key.defaultValue) }
|
||||||
|
|
||||||
|
return AnyPublisher(publisher)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
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, *)
|
||||||
|
public static func publisher<T: NSSecureCoding>(
|
||||||
|
_ key: Defaults.NSSecureCodingKey<T>,
|
||||||
|
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
||||||
|
) -> AnyPublisher<NSSecureCodingKeyChange<T>, Never> {
|
||||||
|
let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options)
|
||||||
|
.map { NSSecureCodingKeyChange<T>(change: $0, defaultValue: key.defaultValue) }
|
||||||
|
|
||||||
|
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<T: Codable>(
|
||||||
|
_ key: Defaults.OptionalKey<T>,
|
||||||
|
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
||||||
|
) -> AnyPublisher<OptionalKeyChange<T>, Never> {
|
||||||
|
let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options)
|
||||||
|
.map { OptionalKeyChange<T>(change: $0) }
|
||||||
|
|
||||||
|
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<T: NSSecureCoding>(
|
||||||
|
_ key: Defaults.NSSecureCodingOptionalKey<T>,
|
||||||
|
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
||||||
|
) -> AnyPublisher<NSSecureCodingOptionalKeyChange<T>, Never> {
|
||||||
|
let publisher = DefaultsPublisher(suite: key.suite, key: key.name, options: options)
|
||||||
|
.map { NSSecureCodingOptionalKeyChange<T>(change: $0) }
|
||||||
|
|
||||||
|
return AnyPublisher(publisher)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
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, *)
|
||||||
|
public static func publisher<T: Codable>(
|
||||||
|
keys: Defaults.Key<T>...,
|
||||||
|
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 () }
|
||||||
|
.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<T: Codable>(
|
||||||
|
keys: Defaults.OptionalKey<T>...,
|
||||||
|
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 () }
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}.reduce(initial) { (combined, keyPublisher) in
|
||||||
|
combined.merge(with: keyPublisher).eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
return combinedPublisher
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
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, *)
|
||||||
|
public static func publisher<T: NSSecureCoding>(
|
||||||
|
keys: Defaults.NSSecureCodingKey<T>...,
|
||||||
|
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 () }
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}.reduce(initial) { (combined, keyPublisher) in
|
||||||
|
combined.merge(with: keyPublisher).eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
return combinedPublisher
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
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, *)
|
||||||
|
public static func publisher<T: NSSecureCoding>(
|
||||||
|
keys: Defaults.NSSecureCodingOptionalKey<T>...,
|
||||||
|
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 () }
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}.reduce(initial) { (combined, keyPublisher) in
|
||||||
|
combined.merge(with: keyPublisher).eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
return combinedPublisher
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Convenience `Publisher` for all `UserDefaults` key change events. A wrapper around the `UserDefaults.didChangeNotification`.
|
||||||
|
|
||||||
|
- Parameter initialEvent: Trigger an initial event immediately.
|
||||||
|
*/
|
||||||
|
@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 publisherAll(initialEvent: Bool = true) -> AnyPublisher<Void, Never> {
|
||||||
|
let publisher =
|
||||||
|
NotificationCenter.default.publisher(for: UserDefaults.didChangeNotification)
|
||||||
|
.map { _ in () }
|
||||||
|
|
||||||
|
if initialEvent {
|
||||||
|
return publisher
|
||||||
|
.prepend(())
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
} else {
|
||||||
|
return publisher
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,14 +64,14 @@ extension Defaults {
|
||||||
return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(dataValue) as? T
|
return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(dataValue) as? T
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate final class BaseChange {
|
final class BaseChange {
|
||||||
fileprivate let kind: NSKeyValueChange
|
let kind: NSKeyValueChange
|
||||||
fileprivate let indexes: IndexSet?
|
let indexes: IndexSet?
|
||||||
fileprivate let isPrior: Bool
|
let isPrior: Bool
|
||||||
fileprivate let newValue: Any?
|
let newValue: Any?
|
||||||
fileprivate let oldValue: Any?
|
let oldValue: Any?
|
||||||
|
|
||||||
fileprivate init(change: [NSKeyValueChangeKey: Any]) {
|
init(change: [NSKeyValueChangeKey: Any]) {
|
||||||
kind = NSKeyValueChange(rawValue: change[.kindKey] as! UInt)!
|
kind = NSKeyValueChange(rawValue: change[.kindKey] as! UInt)!
|
||||||
indexes = change[.indexesKey] as? IndexSet
|
indexes = change[.indexesKey] as? IndexSet
|
||||||
isPrior = change[.notificationIsPriorKey] as? Bool ?? false
|
isPrior = change[.notificationIsPriorKey] as? Bool ?? false
|
||||||
|
@ -87,7 +87,7 @@ extension Defaults {
|
||||||
public let newValue: T
|
public let newValue: T
|
||||||
public let oldValue: T
|
public let oldValue: T
|
||||||
|
|
||||||
fileprivate init(change: BaseChange, defaultValue: T) {
|
init(change: BaseChange, defaultValue: T) {
|
||||||
self.kind = change.kind
|
self.kind = change.kind
|
||||||
self.indexes = change.indexes
|
self.indexes = change.indexes
|
||||||
self.isPrior = change.isPrior
|
self.isPrior = change.isPrior
|
||||||
|
@ -104,7 +104,7 @@ extension Defaults {
|
||||||
public let newValue: T
|
public let newValue: T
|
||||||
public let oldValue: T
|
public let oldValue: T
|
||||||
|
|
||||||
fileprivate init(change: BaseChange, defaultValue: T) {
|
init(change: BaseChange, defaultValue: T) {
|
||||||
self.kind = change.kind
|
self.kind = change.kind
|
||||||
self.indexes = change.indexes
|
self.indexes = change.indexes
|
||||||
self.isPrior = change.isPrior
|
self.isPrior = change.isPrior
|
||||||
|
@ -120,7 +120,7 @@ extension Defaults {
|
||||||
public let newValue: T?
|
public let newValue: T?
|
||||||
public let oldValue: T?
|
public let oldValue: T?
|
||||||
|
|
||||||
fileprivate 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
|
||||||
|
@ -137,7 +137,7 @@ extension Defaults {
|
||||||
public let newValue: T?
|
public let newValue: T?
|
||||||
public let oldValue: T?
|
public let oldValue: T?
|
||||||
|
|
||||||
fileprivate 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
|
||||||
|
@ -146,14 +146,14 @@ extension Defaults {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class UserDefaultsKeyObservation: NSObject, DefaultsObservation {
|
final class UserDefaultsKeyObservation: NSObject, DefaultsObservation {
|
||||||
fileprivate typealias Callback = (BaseChange) -> Void
|
typealias Callback = (BaseChange) -> Void
|
||||||
|
|
||||||
private weak var object: UserDefaults?
|
private weak var object: UserDefaults?
|
||||||
private let key: String
|
private let key: String
|
||||||
private let callback: Callback
|
private let callback: Callback
|
||||||
|
|
||||||
fileprivate init(object: UserDefaults, key: String, callback: @escaping Callback) {
|
init(object: UserDefaults, key: String, callback: @escaping Callback) {
|
||||||
self.object = object
|
self.object = object
|
||||||
self.key = key
|
self.key = key
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
|
@ -163,7 +163,7 @@ extension Defaults {
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func start(options: NSKeyValueObservingOptions) {
|
func start(options: NSKeyValueObservingOptions) {
|
||||||
object?.addObserver(self, forKeyPath: key, options: options, context: nil)
|
object?.addObserver(self, forKeyPath: key, options: options, context: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Foundation
|
||||||
import XCTest
|
import XCTest
|
||||||
import Defaults
|
import Defaults
|
||||||
import CoreData
|
import CoreData
|
||||||
|
import Combine
|
||||||
|
|
||||||
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")!
|
||||||
|
@ -165,6 +166,224 @@ final class DefaultsTests: XCTestCase {
|
||||||
Defaults.removeAll(suite: customSuite)
|
Defaults.removeAll(suite: customSuite)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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 testObserveKeyCombine() {
|
||||||
|
let key = Defaults.Key<Bool>("observeKey", default: false)
|
||||||
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
|
||||||
|
let publisher = Defaults
|
||||||
|
.publisher(key, options: [.old, .new])
|
||||||
|
.map { ($0.oldValue, $0.newValue) }
|
||||||
|
.collect(2)
|
||||||
|
|
||||||
|
let cancellable = publisher.sink { tuples in
|
||||||
|
for (i, expected) in [(false, true), (true, false)].enumerated() {
|
||||||
|
XCTAssertEqual(expected.0, tuples[i].0)
|
||||||
|
XCTAssertEqual(expected.1, tuples[i].1)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
Defaults[key] = true
|
||||||
|
Defaults.reset(key)
|
||||||
|
cancellable.cancel()
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 testObserveNSSecureCodingKeyCombine() {
|
||||||
|
let key = Defaults.NSSecureCodingKey<ExamplePersistentHistory>("observeNSSecureCodingKey", default: ExamplePersistentHistory(value: "TestValue"))
|
||||||
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
|
||||||
|
let publisher = Defaults
|
||||||
|
.publisher(key, options: [.old, .new])
|
||||||
|
.map { ($0.oldValue.value, $0.newValue.value) }
|
||||||
|
.collect(3)
|
||||||
|
|
||||||
|
let expectedValues = [
|
||||||
|
("TestValue", "NewTestValue"),
|
||||||
|
("NewTestValue", "NewTestValue2"),
|
||||||
|
("NewTestValue2", "TestValue")
|
||||||
|
]
|
||||||
|
|
||||||
|
let cancellable = publisher.sink { actualValues in
|
||||||
|
for (expected, actual) in zip(expectedValues, actualValues) {
|
||||||
|
XCTAssertEqual(expected.0, actual.0)
|
||||||
|
XCTAssertEqual(expected.1, actual.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
Defaults[key] = ExamplePersistentHistory(value: "NewTestValue")
|
||||||
|
Defaults[key] = ExamplePersistentHistory(value: "NewTestValue2")
|
||||||
|
Defaults.reset(key)
|
||||||
|
cancellable.cancel()
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
@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() {
|
||||||
|
let key = Defaults.OptionalKey<Bool>("observeOptionalKey")
|
||||||
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
|
||||||
|
let publisher = Defaults
|
||||||
|
.publisher(key, options: [.old, .new])
|
||||||
|
.map { ($0.oldValue, $0.newValue) }
|
||||||
|
.collect(3)
|
||||||
|
|
||||||
|
let expectedValues: [(Bool?, Bool?)] = [(nil, true), (true, false), (false, nil)]
|
||||||
|
|
||||||
|
let cancellable = publisher.sink { actualValues in
|
||||||
|
for (expected, actual) in zip(expectedValues, actualValues) {
|
||||||
|
XCTAssertEqual(expected.0, actual.0)
|
||||||
|
XCTAssertEqual(expected.1, actual.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
Defaults[key] = true
|
||||||
|
Defaults[key] = false
|
||||||
|
Defaults.reset(key)
|
||||||
|
cancellable.cancel()
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 testObserveNSSecureCodingOptionalKeyCombine() {
|
||||||
|
let key = Defaults.NSSecureCodingOptionalKey<ExamplePersistentHistory>("observeNSSecureCodingOptionalKey")
|
||||||
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
|
||||||
|
let publisher = Defaults
|
||||||
|
.publisher(key, options: [.old, .new])
|
||||||
|
.map { ($0.oldValue?.value, $0.newValue?.value) }
|
||||||
|
.collect(3)
|
||||||
|
|
||||||
|
let expectedValues: [(String?, String?)] = [
|
||||||
|
(nil, "NewTestValue"),
|
||||||
|
("NewTestValue", "NewTestValue2"),
|
||||||
|
("NewTestValue2", nil)
|
||||||
|
]
|
||||||
|
|
||||||
|
let cancellable = publisher.sink { actualValues in
|
||||||
|
for (expected, actual) in zip(expectedValues, actualValues) {
|
||||||
|
XCTAssertEqual(expected.0, actual.0)
|
||||||
|
XCTAssertEqual(expected.1, actual.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertNil(Defaults[key])
|
||||||
|
Defaults[key] = ExamplePersistentHistory(value: "NewTestValue")
|
||||||
|
Defaults[key] = ExamplePersistentHistory(value: "NewTestValue2")
|
||||||
|
Defaults.reset(key)
|
||||||
|
cancellable.cancel()
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 testObserveAllCombine() {
|
||||||
|
let key = Defaults.Key<Bool>("observeAllKey", default: false)
|
||||||
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
let publisher = Defaults.publisherAll().collect(3)
|
||||||
|
|
||||||
|
let cancellable = publisher.sink { actualValues in
|
||||||
|
XCTAssertEqual(3, actualValues.count)
|
||||||
|
expect.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
Defaults[key] = true
|
||||||
|
Defaults[key] = false
|
||||||
|
cancellable.cancel()
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 testObserveMultipleKeysCombine() {
|
||||||
|
let key1 = Defaults.Key<Bool>("observeKey1", default: false)
|
||||||
|
let key2 = Defaults.Key<Bool>("observeKey2", default: true)
|
||||||
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
|
||||||
|
let publisher = Defaults.publisher(keys: key1, key2, options: [.old, .new]).collect(2)
|
||||||
|
|
||||||
|
let cancellable = publisher.sink { _ in
|
||||||
|
expect.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
Defaults[key1] = true
|
||||||
|
Defaults[key2] = false
|
||||||
|
cancellable.cancel()
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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 testObserveMultipleNSSecureKeysCombine() {
|
||||||
|
let key1 = Defaults.NSSecureCodingKey<ExamplePersistentHistory>("observeNSSecureCodingKey1", default: ExamplePersistentHistory(value: "TestValue"))
|
||||||
|
let key2 = Defaults.NSSecureCodingKey<ExamplePersistentHistory>("observeNSSecureCodingKey2", default: ExamplePersistentHistory(value: "TestValue"))
|
||||||
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
|
||||||
|
let publisher = Defaults.publisher(keys: key1, key2, options: [.old, .new]).collect(2)
|
||||||
|
|
||||||
|
let cancellable = publisher.sink { _ in
|
||||||
|
expect.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
Defaults[key1] = ExamplePersistentHistory(value: "NewTestValue1")
|
||||||
|
Defaults[key2] = ExamplePersistentHistory(value: "NewTestValue2")
|
||||||
|
cancellable.cancel()
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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() {
|
||||||
|
let key1 = Defaults.OptionalKey<Bool>("observeOptionalKey1")
|
||||||
|
let key2 = Defaults.OptionalKey<Bool>("observeOptionalKey2")
|
||||||
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
|
||||||
|
let publisher = Defaults.publisher(keys: key1, key2, options: [.old, .new]).collect(2)
|
||||||
|
|
||||||
|
let cancellable = publisher.sink { _ in
|
||||||
|
expect.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
Defaults[key1] = true
|
||||||
|
Defaults[key2] = false
|
||||||
|
cancellable.cancel()
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 testObserveMultipleNSSecureOptionalKeysCombine() {
|
||||||
|
let key1 = Defaults.NSSecureCodingOptionalKey<ExamplePersistentHistory>("observeNSSecureCodingKey1")
|
||||||
|
let key2 = Defaults.NSSecureCodingOptionalKey<ExamplePersistentHistory>("observeNSSecureCodingKey2")
|
||||||
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
|
||||||
|
let publisher = Defaults.publisher(keys: key1, key2, options: [.old, .new]).collect(2)
|
||||||
|
|
||||||
|
let cancellable = publisher.sink { _ in
|
||||||
|
expect.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
Defaults[key1] = ExamplePersistentHistory(value: "NewTestValue1")
|
||||||
|
Defaults[key2] = ExamplePersistentHistory(value: "NewTestValue2")
|
||||||
|
cancellable.cancel()
|
||||||
|
|
||||||
|
waitForExpectations(timeout: 10)
|
||||||
|
}
|
||||||
|
|
||||||
func testObserveKey() {
|
func testObserveKey() {
|
||||||
let key = Defaults.Key<Bool>("observeKey", default: false)
|
let key = Defaults.Key<Bool>("observeKey", default: false)
|
||||||
let expect = expectation(description: "Observation closure being called")
|
let expect = expectation(description: "Observation closure being called")
|
||||||
|
|
114
readme.md
114
readme.md
|
@ -13,6 +13,7 @@ It's used in production by apps like [Gifski](https://github.com/sindresorhus/Gi
|
||||||
- **NSSecureCoding support:** You can store any [NSSecureCoding](https://developer.apple.com/documentation/foundation/nssecurecoding) value.
|
- **NSSecureCoding support:** You can store any [NSSecureCoding](https://developer.apple.com/documentation/foundation/nssecurecoding) value.
|
||||||
- **Debuggable:** The data is stored as JSON-serialized values.
|
- **Debuggable:** The data is stored as JSON-serialized values.
|
||||||
- **Observation:** Observe changes to keys.
|
- **Observation:** Observe changes to keys.
|
||||||
|
- **Publishers:** Combine publishers built-in.
|
||||||
- **Lightweight:** It's only some hundred lines of code.
|
- **Lightweight:** It's only some hundred lines of code.
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
@ -163,6 +164,31 @@ Defaults[.isUnicornMode] = true
|
||||||
|
|
||||||
In contrast to the native `UserDefaults` key observation, here you receive a strongly-typed change object.
|
In contrast to the native `UserDefaults` key observation, here you receive a strongly-typed change object.
|
||||||
|
|
||||||
|
There is also an observation API using the [Combine](https://developer.apple.com/documentation/combine) framework, exposing a [Publisher](https://developer.apple.com/documentation/combine/publisher) for key changes:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let publisher = Defaults.publisher(.isUnicornMode)
|
||||||
|
|
||||||
|
let cancellable = publisher.sink { change in
|
||||||
|
// Initial event
|
||||||
|
print(change.oldValue)
|
||||||
|
//=> false
|
||||||
|
print(change.newValue)
|
||||||
|
//=> false
|
||||||
|
|
||||||
|
// First actual event
|
||||||
|
print(change.oldValue)
|
||||||
|
//=> false
|
||||||
|
print(change.newValue)
|
||||||
|
//=> true
|
||||||
|
}
|
||||||
|
|
||||||
|
Defaults[.isUnicornMode] = true
|
||||||
|
|
||||||
|
// To invalidate the observation.
|
||||||
|
cancellable.cancel()
|
||||||
|
```
|
||||||
|
|
||||||
### Invalidate observations automatically
|
### Invalidate observations automatically
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
|
@ -329,7 +355,7 @@ Defaults.observe<T: Codable>(
|
||||||
```
|
```
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
Defaults.observe<T: Codable>(
|
Defaults.observe<T: NSSecureCoding>(
|
||||||
_ key: Defaults.NSSecureCodingKey<T>,
|
_ key: Defaults.NSSecureCodingKey<T>,
|
||||||
options: NSKeyValueObservingOptions = [.initial, .old, .new],
|
options: NSKeyValueObservingOptions = [.initial, .old, .new],
|
||||||
handler: @escaping (NSSecureCodingKeyChange<T>) -> Void
|
handler: @escaping (NSSecureCodingKeyChange<T>) -> Void
|
||||||
|
@ -345,7 +371,7 @@ Defaults.observe<T: Codable>(
|
||||||
```
|
```
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
Defaults.observe<T: Codable>(
|
Defaults.observe<T: NSSecureCoding>(
|
||||||
_ key: Defaults.NSSecureCodingOptionalKey<T>,
|
_ key: Defaults.NSSecureCodingOptionalKey<T>,
|
||||||
options: NSKeyValueObservingOptions = [.initial, .old, .new],
|
options: NSKeyValueObservingOptions = [.initial, .old, .new],
|
||||||
handler: @escaping (NSSecureCodingOptionalKeyChange<T>) -> Void
|
handler: @escaping (NSSecureCodingOptionalKeyChange<T>) -> Void
|
||||||
|
@ -358,6 +384,90 @@ Observe changes to a key or an optional key.
|
||||||
|
|
||||||
By default, it will also trigger an initial event on creation. This can be useful for setting default values on controls. You can override this behavior with the `options` argument.
|
By default, it will also trigger an initial event on creation. This can be useful for setting default values on controls. You can override this behavior with the `options` argument.
|
||||||
|
|
||||||
|
#### `Defaults.publisher`
|
||||||
|
|
||||||
|
```swift
|
||||||
|
Defaults.publisher<T: Codable>(
|
||||||
|
_ key: Defaults.Key<T>,
|
||||||
|
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
||||||
|
) -> AnyPublisher<KeyChange<T>, Never>
|
||||||
|
```
|
||||||
|
|
||||||
|
```swift
|
||||||
|
Defaults.publisher<T: NSSecureCoding>(
|
||||||
|
_ key: Defaults.NSSecureCodingKey<T>,
|
||||||
|
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
||||||
|
) -> AnyPublisher<NSSecureCodingKeyChange<T>, Never>
|
||||||
|
```
|
||||||
|
|
||||||
|
```swift
|
||||||
|
Defaults.publisher<T: Codable>(
|
||||||
|
_ key: Defaults.OptionalKey<T>,
|
||||||
|
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
||||||
|
) -> AnyPublisher<OptionalKeyChange<T>, Never>
|
||||||
|
```
|
||||||
|
|
||||||
|
```swift
|
||||||
|
Defaults.publisher<T: NSSecureCoding>(
|
||||||
|
_ key: Defaults.NSSecureCodingOptionalKey<T>,
|
||||||
|
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
||||||
|
) -> AnyPublisher<NSSecureCodingOptionalKeyChange<T>, Never>
|
||||||
|
```
|
||||||
|
|
||||||
|
Type: `func`
|
||||||
|
|
||||||
|
Observation API using [Publisher](https://developer.apple.com/documentation/combine/publisher) from the [Combine](https://developer.apple.com/documentation/combine) framework.
|
||||||
|
|
||||||
|
Available on macOS 10.15+, iOS 13.0+, tvOS 13.0+, and watchOS 6.0+.
|
||||||
|
|
||||||
|
#### `Defaults.publisher(keys:)`
|
||||||
|
|
||||||
|
```swift
|
||||||
|
Defaults.publisher<T: Codable>(
|
||||||
|
keys: Defaults.Key<T>...,
|
||||||
|
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
||||||
|
) -> AnyPublisher<Void, Never> {
|
||||||
|
```
|
||||||
|
|
||||||
|
```swift
|
||||||
|
Defaults.publisher<T: NSSecureCoding>(
|
||||||
|
keys: Defaults.NSSecureCodingKey<T>...,
|
||||||
|
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
||||||
|
) -> AnyPublisher<Void, Never> {
|
||||||
|
```
|
||||||
|
|
||||||
|
```swift
|
||||||
|
Defaults.publisher<T: Codable>(
|
||||||
|
keys: Defaults.OptionalKey<T>...,
|
||||||
|
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
||||||
|
) -> AnyPublisher<Void, Never> {
|
||||||
|
```
|
||||||
|
|
||||||
|
```swift
|
||||||
|
Defaults.publisher<T: NSSecureCoding>(
|
||||||
|
keys: Defaults.NSSecureCodingOptionalKey<T>...,
|
||||||
|
options: NSKeyValueObservingOptions = [.initial, .old, .new]
|
||||||
|
) -> AnyPublisher<Void, Never> {
|
||||||
|
```
|
||||||
|
|
||||||
|
Type: `func`
|
||||||
|
|
||||||
|
[Combine](https://developer.apple.com/documentation/combine) observation API for multiple key observation, but without specific information about changes.
|
||||||
|
|
||||||
|
Available on macOS 10.15+, iOS 13.0+, tvOS 13.0+, and watchOS 6.0+.
|
||||||
|
|
||||||
|
#### `Defaults.publisherAll`
|
||||||
|
|
||||||
|
```swift
|
||||||
|
Defaults.publisherAll(initialEvent: Bool = true) -> AnyPublisher<UserDefaults, Never>
|
||||||
|
```
|
||||||
|
|
||||||
|
Convenience [Publisher](https://developer.apple.com/documentation/combine/publisher) for all `UserDefaults` key change events. A wrapper around the [`UserDefaults.didChangeNotification` notification](https://developer.apple.com/documentation/foundation/userdefaults/1408206-didchangenotification).
|
||||||
|
|
||||||
|
- Parameter `initialEvent`: Trigger an initial event immediately.
|
||||||
|
|
||||||
|
Available on macOS 10.15+, iOS 13.0+, tvOS 13.0+, and watchOS 6.0+.
|
||||||
|
|
||||||
#### `Defaults.removeAll`
|
#### `Defaults.removeAll`
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
|
|
Loading…
Reference in New Issue