Skip to content

Commit c62f124

Browse files
authored
Merge pull request #2 from amine2233/feature/usererdefaults-combine
feat: add propertywrapper to userdefaults
2 parents 82df1df + 5c1d69f commit c62f124

File tree

5 files changed

+101
-17
lines changed

5 files changed

+101
-17
lines changed

Sources/CombineExtensionUserDefaults/UserDefaults/CombineUserDefaultOnChange.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ public extension Publishers {
1515
public typealias Failure = Never
1616

1717
private let userDefaults: UserDefaults
18-
private let key: Key
18+
private let key: UserDefaultsKey
1919

20-
public init(userDefaults: UserDefaults, key: Key) {
20+
public init(userDefaults: UserDefaults, key: UserDefaultsKey) {
2121
self.userDefaults = userDefaults
2222
self.key = key
2323
}
@@ -36,11 +36,11 @@ public extension Publishers {
3636
extension Publishers.DefaultsObservation {
3737
private final class SubscriptionDefaultsObservation<S: Subscriber, T: PropertyListValue>: NSObject, Subscription where S.Input == T? {
3838
private var subscriber: S?
39-
private var key: Key
39+
private var key: UserDefaultsKey
4040
private var isDisposed: Bool = false
4141
private var userDefaults: UserDefaults
4242

43-
init(subscriber: S, userDefaults: UserDefaults, key: Key) {
43+
init(subscriber: S, userDefaults: UserDefaults, key: UserDefaultsKey) {
4444
self.subscriber = subscriber
4545
self.userDefaults = userDefaults
4646
self.key = key

Sources/CombineExtensionUserDefaults/UserDefaults/DefaultsObservation.swift

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,17 @@ import Combine
1010
import CombineExtension
1111

1212
final class DefaultsObservation<T: PropertyListValue>: NSObject, Cancellable {
13-
let key: Key
14-
private var onChange: (T?) -> Void
13+
let key: UserDefaultsKey
1514
var isDisposed: Bool = false
1615
var userDefaults: UserDefaults
16+
private var onChange: (T?) -> Void
1717

18-
init(key: Key, userDefaults: UserDefaults = .standard, onChange: @escaping (T?) -> Void) {
18+
init(key: UserDefaultsKey, userDefaults: UserDefaults = .standard, onChange: @escaping (T?) -> Void) {
1919
self.key = key
2020
self.onChange = onChange
2121
self.userDefaults = userDefaults
22-
}
23-
24-
func configure() -> DefaultsObservation<T> {
25-
userDefaults.addObserver(self, forKeyPath: key.rawValue, options: [.new], context: nil)
26-
return self
22+
super.init()
23+
self.userDefaults.addObserver(self, forKeyPath: key.rawValue, options: [.new], context: nil)
2724
}
2825

2926
// swiftlint:disable block_based_kvo
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by amine on 27/12/2022.
6+
//
7+
8+
import Combine
9+
#if canImport(SwiftUI)
10+
import SwiftUI
11+
12+
/// A property wrapper type that reflects a value from `UserDefaults` and
13+
/// invalidates a view on a change in value in that user default.
14+
@frozen @propertyWrapper
15+
public struct DefaultsStorage<Value: PropertyListValue>: DynamicProperty {
16+
17+
@ObservedObject private var _value: DefaultsObservationStorage<Value>
18+
19+
private init(
20+
value: Value,
21+
userDefaults: UserDefaults = .standard,
22+
key: UserDefaultsKey
23+
) {
24+
_value = DefaultsObservationStorage(
25+
defaultValue: value,
26+
key: key,
27+
userDefaults: userDefaults
28+
)
29+
}
30+
31+
public var wrappedValue: Value {
32+
get {
33+
_value.value
34+
}
35+
nonmutating set {
36+
_value.value = newValue
37+
}
38+
}
39+
40+
public var projectedValue: Binding<Value> {
41+
Binding(
42+
get: { self.wrappedValue },
43+
set: { self.wrappedValue = $0 }
44+
)
45+
}
46+
47+
public var publisher: AnyPublisher<Value, Never> {
48+
_value.publisher
49+
}
50+
}
51+
52+
@usableFromInline
53+
final class DefaultsObservationStorage<T: PropertyListValue>: NSObject, ObservableObject {
54+
var defaultValue: T
55+
let key: UserDefaultsKey
56+
var isDisposed: Bool = false
57+
var userDefaults: UserDefaults
58+
var value: T {
59+
get { userDefaults.value(forKey: key.rawValue) as? T ?? defaultValue }
60+
set { userDefaults.setValue(newValue, forKey: key.rawValue) }
61+
}
62+
var publisher: AnyPublisher<T, Never> { subject.eraseToAnyPublisher() }
63+
64+
private var subject: CurrentValueSubject<T, Never>
65+
66+
init(
67+
defaultValue: T,
68+
key: UserDefaultsKey,
69+
userDefaults: UserDefaults = .standard
70+
) {
71+
self.defaultValue = defaultValue
72+
self.key = key
73+
self.userDefaults = userDefaults
74+
self.subject = CurrentValueSubject(defaultValue)
75+
super.init()
76+
self.userDefaults.addObserver(self, forKeyPath: key.rawValue, options: [.new], context: nil)
77+
}
78+
79+
// swiftlint:disable block_based_kvo
80+
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) {
81+
guard let change = change, object != nil, keyPath == key.rawValue else { return }
82+
let newValue = change[.newKey] as? T
83+
subject.send(newValue ?? defaultValue)
84+
}
85+
// swiftlint:enable block_based_kvo
86+
}
87+
88+
#endif

Sources/CombineExtensionUserDefaults/UserDefaults/PropertyListValue.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ import CombineExtension
1010

1111
public protocol PropertyListValue {}
1212

13-
public struct Key: RawRepresentable {
13+
public struct UserDefaultsKey: RawRepresentable {
1414
public let rawValue: String
1515

1616
public init(rawValue: String) {
1717
self.rawValue = rawValue
1818
}
1919
}
2020

21-
extension Key: ExpressibleByStringLiteral {
21+
extension UserDefaultsKey: ExpressibleByStringLiteral {
2222
public init(stringLiteral value: String) {
2323
rawValue = value
2424
}

Sources/CombineExtensionUserDefaults/UserDefaults/UserDefaults+Extension.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,13 @@ import Combine
1010
import CombineExtension
1111

1212
extension CombineExtension where Base: UserDefaults {
13-
public func change<T: PropertyListValue>(key: Key) -> AnyPublisher<T?, Never> {
13+
public func change<T: PropertyListValue>(key: UserDefaultsKey) -> AnyPublisher<T?, Never> {
1414
Publishers.DefaultsObservation(userDefaults: base, key: key)
1515
.eraseToAnyPublisher()
1616
}
1717

18-
public func onChange<T:PropertyListValue>(key: Key, callback: @escaping (T?) -> Void) -> AnyCancellable {
18+
public func onChange<T:PropertyListValue>(key: UserDefaultsKey, callback: @escaping (T?) -> Void) -> AnyCancellable {
1919
DefaultsObservation(key: key, userDefaults: base, onChange: callback)
20-
.configure()
2120
.eraseToAnyCancellable()
2221
}
2322
}

0 commit comments

Comments
 (0)