Github.com/agelessman/…

We all know that the design concept of SwiftUI is Data flow, that is, View is driven by Data. We call the Data that View depends on state. Therefore, Data management in SwiftUI is state management.

Common state management consists of the following:

  • AppStorage
  • Binding
  • Environment
  • EnvironmentObject
  • FetchRequest
  • ObservedObject
  • State
  • StateObject

In development, their usage can be summarized in the following diagram:

If the View depends on the data, it will refresh when the data changes. We’ll focus on object object and StateObject.

ObservedObject

class MyViewModel: ObservableObject {
    @Published var name: String = "Zhang"
}

struct ContentView: View {
    @ObservedObject var dataModel: MyViewModel
    
    var body: some View {
        Text(dataModel.name)
    }
}
Copy the code

The above code is the most common use, dataModel provides data for the ContentView, so what about @observedobject? Look at the definition:

@propertyWrapper @frozen public struct ObservedObject<ObjectType> : DynamicProperty where ObjectType : ObservableObject {
    @dynamicMemberLookup @frozen public struct Wrapper {
        public subscript<Subject> (dynamicMember keyPath: ReferenceWritableKeyPath<ObjectType.Subject>) -> Binding<Subject> { get}}public init(initialValue: ObjectType)
    public init(wrappedValue: ObjectType)

    public var wrappedValue: ObjectType

    public var projectedValue: ObservedObject<ObjectType>.Wrapper { get}}Copy the code

By analyzing the above code, we found the following important information:

  • ObjectType : ObservableObject The type that represents it must be implementedObservableObject The protocol, which we’ll talk about later
  • projectedValue: ObservedObject<ObjectType>.WrapperThat means we can use it$dataModelTo access thisprojectedValue, its return value isWrapperType, look up herestruct WrapperThe definition of it is a@dynamicMemberLookup.@dynamicMemberLookupAnd we’ll talk more about that later, but you just need to know that when we want aBindType of data is, you can do thisTextField(" Enter text ", text: $datamodel.name)

ObservableObject is the ObservableObject protocol.

public protocol ObservableObject : AnyObject {

    /// The type of publisher that emits before the object has changed.
    associatedtype ObjectWillChangePublisher : Publisher = ObservableObjectPublisher where Self.ObjectWillChangePublisher.Failure = = Never

    /// A publisher that emits before the object has changed.
    var objectWillChange: Self.ObjectWillChangePublisher { get}}extension ObservableObject where Self.ObjectWillChangePublisher= =ObservableObjectPublisher {

    /// A publisher that emits before the object has changed.
    public var objectWillChange: ObservableObjectPublisher { get}}Copy the code

ObservableObject is derived from AnyObject, which means that the implementation of the protocol must be of class type, not struct type.

This protocol requires that an objectWillChange property be returned, which must implement the Publisher protocol, which the ObservableObject extension in the code above already implements, It returns the type of ObservableObjectPublisher, we’ll look at the definition of it:

final public class ObservableObjectPublisher : Publisher {

    /// The kind of values published by this publisher.
    public typealias Output = Void

    /// The kind of errors this publisher might publish.
    ///
    /// Use `Never` if this `Publisher` does not publish errors.
    public typealias Failure = Never

    /// Creates an observable object publisher instance.
    public init(a)

    final public func receive<S> (subscriber: S) where S : Subscriber.S.Failure = = ObservableObjectPublisher.Failure.S.Input = = ObservableObjectPublisher.Output

    final public func send(a)
}
Copy the code

Can see ObservableObjectPublisher Publisher is a very common, it is a custom Publisher, foreign only exposes a the send method, is used to signal the data is changed, the Publisher will not be any output data.

So far, we’ve seen that as long as it’s implementedObservableObjectAgreement, you get oneobjectWillChangeIt’s a Publisher, just call itObjectwillchange.send () triggers a View refresh.

Let’s implement this protocol first, the code is as follows:

class MyViewModel: ObservableObject {
    @Published var name: String = "Zhang"
    var age: Int = 20
    
    func click(a) {
        age = 30
        objectWillChange.send()
    }
}
Copy the code

If we wrap an attribute with @published, objectwillchange.send () is automatically called when the value of the attribute changes, otherwise we need to call it manually.

Let’s look again at the definition of @published:

@propertyWrapper public struct Published<Value> {

    public init(wrappedValue: Value)

    public init(initialValue: Value)

    /// A publisher for properties marked with the `@Published` attribute.
    public struct Publisher : Publisher {

        /// The kind of values published by this publisher.
        public typealias Output = Value

        /// The kind of errors this publisher might publish.
        ///
        /// Use `Never` if this `Publisher` does not publish errors.
        public typealias Failure = Never


        public func receive<S> (subscriber: S) where Value = = S.Input.S : Subscriber.S.Failure = = Published<Value>.Publisher.Failure
    }

    public var projectedValue: Published<Value>.Publisher { mutating get set}}Copy the code

Just remember that its project Value is a Publisher, and to get the project value, use the $sign, because it is a Publisher, so we can use Combine as we please:

 $name
      .map {
          "The name is:\ [$0)"
      }
      .sink(receiveValue: {
          print($0)})Copy the code

StateObject

Both @stateObject and @ObServedobject are used to wrap properties that implement the ObservableObject protocol, the only difference being the management of the property’s lifecycle.

  • @StateObjectIs managed by the View and is initialized once and destroyed when the View destroys it
  • @ObservedObjectThe life cycle is managed manually by us, usually from father to child

conclusion

This paper does not explain all state management in SwiftUI in detail, but only states related to Combine. Among them, the most core is ObservableObject protocol, which is absolutely the most commonly used technology in real development. In our customized View Model, By combining a series of Piplines to manipulate data, when the Source for Truth data changes, the View automatically refreshes.

Kean. Blog/post/swiftu…

[stackoverflow.com/questions/5…