Various improvements
This commit is contained in:
parent
ab8127604c
commit
110f2d2b9e
|
@ -33,14 +33,14 @@
|
||||||
E38C9F28244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; };
|
E38C9F28244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; };
|
||||||
E38C9F29244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; };
|
E38C9F29244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; };
|
||||||
E38C9F2A244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; };
|
E38C9F2A244ADA2F00A6737A /* SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = E38C9F26244ADA2F00A6737A /* SwiftUI.swift */; };
|
||||||
E3EB3E33216505920033B089 /* util.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* util.swift */; };
|
E3EB3E33216505920033B089 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* Utilities.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 */; };
|
||||||
E3EB3E37216507B50033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; };
|
E3EB3E37216507B50033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; };
|
||||||
E3EB3E38216507B60033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; };
|
E3EB3E38216507B60033B089 /* Observation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E34216507AE0033B089 /* Observation.swift */; };
|
||||||
E3EB3E39216507C30033B089 /* util.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* util.swift */; };
|
E3EB3E39216507C30033B089 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* Utilities.swift */; };
|
||||||
E3EB3E3A216507C40033B089 /* util.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* util.swift */; };
|
E3EB3E3A216507C40033B089 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* Utilities.swift */; };
|
||||||
E3EB3E3B216507C40033B089 /* util.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* util.swift */; };
|
E3EB3E3B216507C40033B089 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3EB3E32216505920033B089 /* Utilities.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -84,7 +84,7 @@
|
||||||
E339B3B22449ED2000E7A40A /* Reset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Reset.swift; sourceTree = "<group>"; usesTabs = 1; };
|
E339B3B22449ED2000E7A40A /* Reset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Reset.swift; sourceTree = "<group>"; usesTabs = 1; };
|
||||||
E339B3B72449F10D00E7A40A /* UserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UserDefaults.swift; sourceTree = "<group>"; usesTabs = 1; };
|
E339B3B72449F10D00E7A40A /* UserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = UserDefaults.swift; sourceTree = "<group>"; usesTabs = 1; };
|
||||||
E38C9F26244ADA2F00A6737A /* SwiftUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SwiftUI.swift; sourceTree = "<group>"; usesTabs = 1; };
|
E38C9F26244ADA2F00A6737A /* SwiftUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = SwiftUI.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 /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = Utilities.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 */
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@
|
||||||
E3EB3E34216507AE0033B089 /* Observation.swift */,
|
E3EB3E34216507AE0033B089 /* Observation.swift */,
|
||||||
E286D0C623B8D51100570D1E /* Observation+Combine.swift */,
|
E286D0C623B8D51100570D1E /* Observation+Combine.swift */,
|
||||||
E38C9F26244ADA2F00A6737A /* SwiftUI.swift */,
|
E38C9F26244ADA2F00A6737A /* SwiftUI.swift */,
|
||||||
E3EB3E32216505920033B089 /* util.swift */,
|
E3EB3E32216505920033B089 /* Utilities.swift */,
|
||||||
);
|
);
|
||||||
path = Defaults;
|
path = Defaults;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -512,7 +512,7 @@
|
||||||
8933C7851EB5B820000D00A4 /* Defaults.swift in Sources */,
|
8933C7851EB5B820000D00A4 /* Defaults.swift in Sources */,
|
||||||
E339B3B92449F10D00E7A40A /* UserDefaults.swift in Sources */,
|
E339B3B92449F10D00E7A40A /* UserDefaults.swift in Sources */,
|
||||||
E3EB3E35216507AE0033B089 /* Observation.swift in Sources */,
|
E3EB3E35216507AE0033B089 /* Observation.swift in Sources */,
|
||||||
E3EB3E33216505920033B089 /* util.swift in Sources */,
|
E3EB3E33216505920033B089 /* Utilities.swift in Sources */,
|
||||||
E339B3B42449ED2000E7A40A /* Reset.swift in Sources */,
|
E339B3B42449ED2000E7A40A /* Reset.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -531,7 +531,7 @@
|
||||||
files = (
|
files = (
|
||||||
E286D0CA23B8D54E00570D1E /* Observation+Combine.swift in Sources */,
|
E286D0CA23B8D54E00570D1E /* Observation+Combine.swift in Sources */,
|
||||||
E38C9F2A244ADA2F00A6737A /* SwiftUI.swift in Sources */,
|
E38C9F2A244ADA2F00A6737A /* SwiftUI.swift in Sources */,
|
||||||
E3EB3E3A216507C40033B089 /* util.swift in Sources */,
|
E3EB3E3A216507C40033B089 /* Utilities.swift in Sources */,
|
||||||
E339B3BB2449F10D00E7A40A /* UserDefaults.swift in Sources */,
|
E339B3BB2449F10D00E7A40A /* UserDefaults.swift in Sources */,
|
||||||
E3EB3E37216507B50033B089 /* Observation.swift in Sources */,
|
E3EB3E37216507B50033B089 /* Observation.swift in Sources */,
|
||||||
8933C7871EB5B820000D00A4 /* Defaults.swift in Sources */,
|
8933C7871EB5B820000D00A4 /* Defaults.swift in Sources */,
|
||||||
|
@ -545,7 +545,7 @@
|
||||||
files = (
|
files = (
|
||||||
E286D0C923B8D54D00570D1E /* Observation+Combine.swift in Sources */,
|
E286D0C923B8D54D00570D1E /* Observation+Combine.swift in Sources */,
|
||||||
E38C9F29244ADA2F00A6737A /* SwiftUI.swift in Sources */,
|
E38C9F29244ADA2F00A6737A /* SwiftUI.swift in Sources */,
|
||||||
E3EB3E3B216507C40033B089 /* util.swift in Sources */,
|
E3EB3E3B216507C40033B089 /* Utilities.swift in Sources */,
|
||||||
E339B3BA2449F10D00E7A40A /* UserDefaults.swift in Sources */,
|
E339B3BA2449F10D00E7A40A /* UserDefaults.swift in Sources */,
|
||||||
E3EB3E38216507B60033B089 /* Observation.swift in Sources */,
|
E3EB3E38216507B60033B089 /* Observation.swift in Sources */,
|
||||||
8933C7881EB5B820000D00A4 /* Defaults.swift in Sources */,
|
8933C7881EB5B820000D00A4 /* Defaults.swift in Sources */,
|
||||||
|
@ -559,7 +559,7 @@
|
||||||
files = (
|
files = (
|
||||||
E286D0C723B8D51100570D1E /* Observation+Combine.swift in Sources */,
|
E286D0C723B8D51100570D1E /* Observation+Combine.swift in Sources */,
|
||||||
E38C9F27244ADA2F00A6737A /* SwiftUI.swift in Sources */,
|
E38C9F27244ADA2F00A6737A /* SwiftUI.swift in Sources */,
|
||||||
E3EB3E39216507C30033B089 /* util.swift in Sources */,
|
E3EB3E39216507C30033B089 /* Utilities.swift in Sources */,
|
||||||
E339B3B82449F10D00E7A40A /* UserDefaults.swift in Sources */,
|
E339B3B82449F10D00E7A40A /* UserDefaults.swift in Sources */,
|
||||||
E3EB3E36216507B50033B089 /* Observation.swift in Sources */,
|
E3EB3E36216507B50033B089 /* Observation.swift in Sources */,
|
||||||
8933C7861EB5B820000D00A4 /* Defaults.swift in Sources */,
|
8933C7861EB5B820000D00A4 /* Defaults.swift in Sources */,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// MIT License © Sindre Sorhus
|
// MIT License © Sindre Sorhus
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public protocol DefaultsBaseKey: Defaults.Keys {
|
public protocol DefaultsBaseKey: Defaults.AnyKey {
|
||||||
var name: String { get }
|
var name: String { get }
|
||||||
var suite: UserDefaults { get }
|
var suite: UserDefaults { get }
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ extension DefaultsBaseKey {
|
||||||
|
|
||||||
public enum Defaults {
|
public enum Defaults {
|
||||||
public typealias BaseKey = DefaultsBaseKey
|
public typealias BaseKey = DefaultsBaseKey
|
||||||
|
public typealias AnyKey = Keys
|
||||||
|
|
||||||
public class Keys: BaseKey {
|
public class Keys: BaseKey {
|
||||||
public typealias Key = Defaults.Key
|
public typealias Key = Defaults.Key
|
||||||
|
@ -34,7 +35,7 @@ public enum Defaults {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class Key<Value: Codable>: Keys {
|
public final class Key<Value: Codable>: AnyKey {
|
||||||
public let defaultValue: Value
|
public let defaultValue: Value
|
||||||
|
|
||||||
/// Create a defaults key.
|
/// Create a defaults key.
|
||||||
|
@ -58,7 +59,7 @@ public enum 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<Value: NSSecureCoding>: Keys {
|
public final class NSSecureCodingKey<Value: NSSecureCoding>: AnyKey {
|
||||||
public let defaultValue: Value
|
public let defaultValue: Value
|
||||||
|
|
||||||
/// Create a defaults key.
|
/// Create a defaults key.
|
||||||
|
@ -82,7 +83,7 @@ public enum 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<Value: NSSecureCoding>: Keys {
|
public final class NSSecureCodingOptionalKey<Value: NSSecureCoding>: AnyKey {
|
||||||
/// Create an optional defaults key.
|
/// Create an optional defaults key.
|
||||||
public init(_ key: String, suite: UserDefaults = .standard) {
|
public init(_ key: String, suite: UserDefaults = .standard) {
|
||||||
super.init(name: key, suite: suite)
|
super.init(name: key, suite: suite)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#if canImport(Combine)
|
#if canImport(Combine)
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
|
@ -133,7 +132,7 @@ 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(
|
public static func publisher(
|
||||||
keys: Keys...,
|
keys: AnyKey...,
|
||||||
options: ObservationOptions = [.initial]
|
options: ObservationOptions = [.initial]
|
||||||
) -> AnyPublisher<Void, Never> {
|
) -> AnyPublisher<Void, Never> {
|
||||||
let initial = Empty<Void, Never>(completeImmediately: false).eraseToAnyPublisher()
|
let initial = Empty<Void, Never>(completeImmediately: false).eraseToAnyPublisher()
|
||||||
|
@ -150,5 +149,4 @@ extension Defaults {
|
||||||
return combinedPublisher
|
return combinedPublisher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -141,38 +141,38 @@ extension Defaults {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static var preventPropagationThreadDictKey: String {
|
private static var preventPropagationThreadDictionaryKey: String {
|
||||||
"\(type(of: Observation.self))_threadUpdatingValuesFlag"
|
"\(type(of: Observation.self))_threadUpdatingValuesFlag"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Execute block without triggering events of changes made at defaults keys.
|
Execute the closure without triggering change events.
|
||||||
|
|
||||||
|
Any `Defaults` key changes made within the closure will not propagate to `Defaults` event listeners (`Defaults.observe()` and `Defaults.publisher()`). This can be useful to prevent infinite recursion when you want to change a key in the callback listening to changes for the same key.
|
||||||
|
|
||||||
|
- Note: This only works with `Defaults.observe()` and `Defaults.publisher()`. User-made KVO will not be affected.
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
```
|
||||||
let observer = Defaults.observe(keys: .key1, .key2) {
|
let observer = Defaults.observe(keys: .key1, .key2) {
|
||||||
// …
|
// …
|
||||||
|
|
||||||
Defaults.withoutPropagation {
|
Defaults.withoutPropagation {
|
||||||
// update some value at .key1
|
// Update `.key1` without propagating the change to listeners.
|
||||||
// this will not be propagated
|
|
||||||
Defaults[.key1] = 11
|
Defaults[.key1] = 11
|
||||||
}
|
}
|
||||||
// this will be propagated
|
|
||||||
|
// This will be propagated.
|
||||||
Defaults[.someKey] = true
|
Defaults[.someKey] = true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This only works with defaults `observe` or `publisher`. User made KVO will not be affected.
|
|
||||||
*/
|
*/
|
||||||
public static func withoutPropagation(block: () -> Void) {
|
public static func withoutPropagation(_ closure: () -> Void) {
|
||||||
// How does it work?
|
// How does it work?
|
||||||
// KVO observation callbacks are executed right after change is made,
|
// KVO observation callbacks are executed right after a change is made, and run on the same thread as the caller. So it works by storing a flag in the current thread's dictionary, which is then evaluated in the callback.
|
||||||
// and run on the same thread as the caller. So it works by storing a flag in current
|
|
||||||
// thread's dictionary, which is then evaluated in `observeValue` callback
|
|
||||||
|
|
||||||
let key = preventPropagationThreadDictKey
|
let key = preventPropagationThreadDictionaryKey
|
||||||
Thread.current.threadDictionary[key] = true
|
Thread.current.threadDictionary[key] = true
|
||||||
block()
|
closure()
|
||||||
Thread.current.threadDictionary[key] = false
|
Thread.current.threadDictionary[key] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@ extension Defaults {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let key = preventPropagationThreadDictKey
|
let key = preventPropagationThreadDictionaryKey
|
||||||
let updatingValuesFlag = (Thread.current.threadDictionary[key] as? Bool) ?? false
|
let updatingValuesFlag = (Thread.current.threadDictionary[key] as? Bool) ?? false
|
||||||
guard !updatingValuesFlag else {
|
guard !updatingValuesFlag else {
|
||||||
return
|
return
|
||||||
|
@ -260,7 +260,7 @@ extension Defaults {
|
||||||
}
|
}
|
||||||
|
|
||||||
private var observables: [SuiteKeyPair]
|
private var observables: [SuiteKeyPair]
|
||||||
private var lifetimeAssociation: LifetimeAssociation? = nil
|
private var lifetimeAssociation: LifetimeAssociation?
|
||||||
private let callback: UserDefaultsKeyObservation.Callback
|
private let callback: UserDefaultsKeyObservation.Callback
|
||||||
|
|
||||||
init(observables: [(suite: UserDefaults, key: String)], callback: @escaping UserDefaultsKeyObservation.Callback) {
|
init(observables: [(suite: UserDefaults, key: String)], callback: @escaping UserDefaultsKeyObservation.Callback) {
|
||||||
|
@ -279,14 +279,14 @@ extension Defaults {
|
||||||
self,
|
self,
|
||||||
forKeyPath: observable.key,
|
forKeyPath: observable.key,
|
||||||
options: options.toNSKeyValueObservingOptions,
|
options: options.toNSKeyValueObservingOptions,
|
||||||
context: &type(of: self).observationContext
|
context: &Self.observationContext
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func invalidate() {
|
public func invalidate() {
|
||||||
for observable in observables {
|
for observable in observables {
|
||||||
observable.suite?.removeObserver(self, forKeyPath: observable.key, context: &type(of: self).observationContext)
|
observable.suite?.removeObserver(self, forKeyPath: observable.key, context: &Self.observationContext)
|
||||||
observable.suite = nil
|
observable.suite = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,7 +313,7 @@ extension Defaults {
|
||||||
context: UnsafeMutableRawPointer?
|
context: UnsafeMutableRawPointer?
|
||||||
) {
|
) {
|
||||||
guard
|
guard
|
||||||
context == &type(of: self).observationContext
|
context == &Self.observationContext
|
||||||
else {
|
else {
|
||||||
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
|
||||||
return
|
return
|
||||||
|
@ -326,7 +326,7 @@ extension Defaults {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let key = preventPropagationThreadDictKey
|
let key = preventPropagationThreadDictionaryKey
|
||||||
let updatingValuesFlag = (Thread.current.threadDictionary[key] as? Bool) ?? false
|
let updatingValuesFlag = (Thread.current.threadDictionary[key] as? Bool) ?? false
|
||||||
if updatingValuesFlag {
|
if updatingValuesFlag {
|
||||||
return
|
return
|
||||||
|
@ -401,7 +401,7 @@ extension Defaults {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Observe multiple keys of any type, but without specific information about changes.
|
Observe multiple keys of any type, but without any information about the changes.
|
||||||
|
|
||||||
```
|
```
|
||||||
extension Defaults.Keys {
|
extension Defaults.Keys {
|
||||||
|
@ -410,12 +410,12 @@ extension Defaults {
|
||||||
}
|
}
|
||||||
|
|
||||||
let observer = Defaults.observe(keys: .setting1, .setting2) {
|
let observer = Defaults.observe(keys: .setting1, .setting2) {
|
||||||
//...
|
// …
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
*/
|
*/
|
||||||
public static func observe(
|
public static func observe(
|
||||||
keys: Keys...,
|
keys: AnyKey...,
|
||||||
options: ObservationOptions = [.initial],
|
options: ObservationOptions = [.initial],
|
||||||
handler: @escaping () -> Void
|
handler: @escaping () -> Void
|
||||||
) -> Observation {
|
) -> Observation {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,4 @@
|
||||||
#if canImport(Combine)
|
#if canImport(Combine)
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
|
@ -111,5 +110,4 @@ public struct Default<Value: Codable>: DynamicProperty {
|
||||||
key.reset()
|
key.reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -369,7 +369,7 @@ final class DefaultsTests: XCTestCase {
|
||||||
|
|
||||||
let publisher = Defaults
|
let publisher = Defaults
|
||||||
.publisher(key)
|
.publisher(key)
|
||||||
.compactMap { $0.newValue }
|
.map(\.newValue)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
.collect(2)
|
.collect(2)
|
||||||
|
|
||||||
|
@ -579,10 +579,7 @@ final class DefaultsTests: XCTestCase {
|
||||||
waitForExpectations(timeout: 10)
|
waitForExpectations(timeout: 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// This checks if the callback is still being called if the value is changed on a second thread while the initial thread is doing some long running task.
|
||||||
This checks if callback is still being called, if value is changed on second thread,
|
|
||||||
while initial thread is doing some long lasting task.
|
|
||||||
*/
|
|
||||||
func testObservePreventPropagationMultipleThreads() {
|
func testObservePreventPropagationMultipleThreads() {
|
||||||
let key1 = Defaults.Key<Int?>("preventPropagation3", default: nil)
|
let key1 = Defaults.Key<Int?>("preventPropagation3", default: nil)
|
||||||
let expect = expectation(description: "No infinite recursion")
|
let expect = expectation(description: "No infinite recursion")
|
||||||
|
@ -610,9 +607,7 @@ final class DefaultsTests: XCTestCase {
|
||||||
waitForExpectations(timeout: 10)
|
waitForExpectations(timeout: 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Check if propagation prevention works across multiple observations.
|
||||||
Check if propagation prevention works across multiple observations
|
|
||||||
*/
|
|
||||||
func testObservePreventPropagationMultipleObservations() {
|
func testObservePreventPropagationMultipleObservations() {
|
||||||
let key1 = Defaults.Key<Bool?>("preventPropagation4", default: nil)
|
let key1 = Defaults.Key<Bool?>("preventPropagation4", default: nil)
|
||||||
let key2 = Defaults.Key<Bool?>("preventPropagation5", default: nil)
|
let key2 = Defaults.Key<Bool?>("preventPropagation5", default: nil)
|
||||||
|
|
45
readme.md
45
readme.md
|
@ -239,23 +239,6 @@ Defaults[.isUnicornMode] = true
|
||||||
|
|
||||||
The observation will be valid until `self` is deinitialized.
|
The observation will be valid until `self` is deinitialized.
|
||||||
|
|
||||||
### Control propagation of change events
|
|
||||||
|
|
||||||
```swift
|
|
||||||
let observer = Defaults.observe(keys: .key1, .key2) {
|
|
||||||
// …
|
|
||||||
Defaults.withoutPropagation {
|
|
||||||
// update some value at .key1
|
|
||||||
// this will not be propagated
|
|
||||||
Defaults[.key1] = 11
|
|
||||||
}
|
|
||||||
// this will be propagated
|
|
||||||
Defaults[.someKey] = true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Changes made within `Defaults.withoutPropagation` block, will not be propagated to observation callbacks, and therefore will prevent infinite recursion.
|
|
||||||
|
|
||||||
### Reset keys to their default values
|
### Reset keys to their default values
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
|
@ -274,6 +257,24 @@ Defaults[.isUnicornMode]
|
||||||
|
|
||||||
This works for a `Key` with an optional too, which will be reset back to `nil`.
|
This works for a `Key` with an optional too, which will be reset back to `nil`.
|
||||||
|
|
||||||
|
### Control propagation of change events
|
||||||
|
|
||||||
|
Changes made within the `Defaults.withoutPropagation` closure will not be propagated to observation callbacks (`Defaults.observe()` or `Defaults.publisher()`), and therefore could prevent infinite recursion.
|
||||||
|
|
||||||
|
```swift
|
||||||
|
let observer = Defaults.observe(keys: .key1, .key2) {
|
||||||
|
// …
|
||||||
|
|
||||||
|
Defaults.withoutPropagation {
|
||||||
|
// Update `.key1` without propagating the change to listeners.
|
||||||
|
Defaults[.key1] = 11
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will be propagated.
|
||||||
|
Defaults[.someKey] = true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### It's just `UserDefaults` with sugar
|
### It's just `UserDefaults` with sugar
|
||||||
|
|
||||||
This works too:
|
This works too:
|
||||||
|
@ -408,9 +409,9 @@ By default, it will also trigger an initial event on creation. This can be usefu
|
||||||
|
|
||||||
Type: `func`
|
Type: `func`
|
||||||
|
|
||||||
Observe changes to multiple keys of any type, but without specific information about changes.
|
Observe multiple keys of any type, but without any information about the changes.
|
||||||
|
|
||||||
Options same as in `observe` for a single key.
|
Options are the same as in `.observe(…)` for a single key.
|
||||||
|
|
||||||
#### `Defaults.publisher(_ key:, options:)`
|
#### `Defaults.publisher(_ key:, options:)`
|
||||||
|
|
||||||
|
@ -500,11 +501,11 @@ Break the lifetime tie created by `tieToLifetime(of:)`, if one exists.
|
||||||
|
|
||||||
The effects of any call to `tieToLifetime(of:)` are reversed. Note however that if the tied-to object has already died, then the observation is already invalid and this method has no logical effect.
|
The effects of any call to `tieToLifetime(of:)` are reversed. Note however that if the tied-to object has already died, then the observation is already invalid and this method has no logical effect.
|
||||||
|
|
||||||
#### `Defaults.withoutPropagation(_ block:)`
|
#### `Defaults.withoutPropagation(_ closure:)`
|
||||||
|
|
||||||
Execute block without emitting events of changes made at defaults keys.
|
Execute the closure without triggering change events.
|
||||||
|
|
||||||
Changes made within the block will not be propagated to observation callbacks. This only works with defaults `observe` or `publisher`. User made KVO will not be affected.
|
Any `Defaults` key changes made within the closure will not propagate to `Defaults` event listeners (`Defaults.observe()` and `Defaults.publisher()`). This can be useful to prevent infinite recursion when you want to change a key in the callback listening to changes for the same key.
|
||||||
|
|
||||||
### `@Default(_ key:)`
|
### `@Default(_ key:)`
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue