1
0

State.swift 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. import Foundation
  2. // TODO: Document State properly, this is an important type.
  3. // - It supports value types
  4. // - It supports ObservableObject
  5. // - It supports Optional<ObservableObject>
  6. /// A property wrapper that acts as a source of truth for view state.
  7. @propertyWrapper
  8. public struct State<Value>: ObservableProperty {
  9. private final class Storage: StateStorageProtocol {
  10. var value: Value
  11. var didChange = Publisher()
  12. var downstreamObservation: Cancellable?
  13. init(_ value: Value) {
  14. self.value = value
  15. }
  16. }
  17. private let implementation: StateImpl<Storage>
  18. private var storage: Storage { implementation.storage }
  19. public var didChange: Publisher { storage.didChange }
  20. /// Accesses the underlying value of this `State`.
  21. public var wrappedValue: Value {
  22. get { implementation.wrappedValue }
  23. nonmutating set { implementation.wrappedValue = newValue }
  24. }
  25. /// Returns a ``Binding`` to this state.
  26. public var projectedValue: Binding<Value> { implementation.projectedValue }
  27. /// Creates a `State` given an initial value.
  28. ///
  29. /// - Parameter initialValue: The state's initial value.
  30. public init(wrappedValue initialValue: Value) {
  31. implementation = StateImpl(initialStorage: Storage(initialValue))
  32. }
  33. public func update(with environment: EnvironmentValues, previousValue: State<Value>?) {
  34. implementation.update(with: environment, previousValue: previousValue?.implementation)
  35. }
  36. }
  37. extension State {
  38. // NB: `ExpressibleByNilLiteral` is what SwiftUI checks for too.
  39. public init() where Value: ExpressibleByNilLiteral {
  40. self.init(wrappedValue: nil)
  41. }
  42. @available(
  43. *,
  44. deprecated,
  45. message: """
  46. 'State' does not work correctly with non-observable classes; conform \
  47. your class to 'ObservableObject' or use a struct instead
  48. """
  49. )
  50. public init(wrappedValue initialValue: Value) where Value: AnyObject {
  51. implementation = StateImpl(initialStorage: Storage(initialValue))
  52. }
  53. // NB: Needed to prevent deprecation warnings for `ObservableObject` types, which
  54. // *are* fully supported by `State`
  55. public init(wrappedValue initialValue: Value) where Value: ObservableObject {
  56. implementation = StateImpl(initialStorage: Storage(initialValue))
  57. }
  58. }
  59. extension State: SnapshottableProperty {
  60. public func tryRestoreFromSnapshot(_ snapshot: Data) {
  61. // Ignored for now
  62. }
  63. public func snapshot() throws -> Data? {
  64. return nil
  65. }
  66. }