Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ObservationView不修改body的改进尝试 #1

Open
winddpan opened this issue Oct 18, 2023 · 9 comments
Open

ObservationView不修改body的改进尝试 #1

winddpan opened this issue Oct 18, 2023 · 9 comments

Comments

@winddpan
Copy link

通过_makeView可以不修改body的实现达成
inspired

struct ContentView: View {
  private var person = Person(name: "Tom", age: 12)

  var body: some View {
    VStack {
      Text(person.name)
      Text("\(person.age)")
      HStack {
        Button("+") { person.age += 1 }
        Button("-") { person.age -= 1 }
      }
      .padding()
    }
  }

  @ViewBuilder
  private var observationView: ObservationView<Body> {
    ObservationView {
      body
    }
  }

  static func _makeView(view: SwiftUI._GraphValue<Self>, inputs: SwiftUI._ViewInputs)
    -> SwiftUI._ViewOutputs
  {
    return ObservationView<Body>._makeView(view: view[\.observationView], inputs: inputs)
  }

  static func _makeViewList(view: SwiftUI._GraphValue<Self>, inputs: SwiftUI._ViewListInputs)
    -> SwiftUI._ViewListOutputs
  {
    return ObservationView<Body>._makeViewList(view: view[\.observationView], inputs: inputs)
  }

  static func _viewListCount(inputs: SwiftUI._ViewListCountInputs) -> Int? {
    return ObservationView<Body>._viewListCount(inputs: inputs)
  }
}

包装一个协议

protocol ObservationBPView: View {}

extension ObservationBPView {
  @ViewBuilder
  private var observationView: ObservationView<Body> {
    ObservationView {
      body
    }
  }

 static func _makeView(view: SwiftUI._GraphValue<Self>, inputs: SwiftUI._ViewInputs)
    -> SwiftUI._ViewOutputs
  {
    return ObservationView<Body>._makeView(view: view[\.observationView], inputs: inputs)
  }

 static func _makeViewList(view: SwiftUI._GraphValue<Self>, inputs: SwiftUI._ViewListInputs)
    -> SwiftUI._ViewListOutputs
  {
    return ObservationView<Body>._makeViewList(view: view[\.observationView], inputs: inputs)
  }

 static func _viewListCount(inputs: SwiftUI._ViewListCountInputs) -> Int? {
    return ObservationView<Body>._viewListCount(inputs: inputs)
  }
}


struct ContentView: ObservationBPView {
  ...
}

包装一个Macro

@ObservationBPView
struct ContentView: View {
  ...

  // Macro expends below
  @ViewBuilder
  private var observationView: ObservationView<Body> {
    ObservationView {
      body
    }
  }

  static func _makeView(view: SwiftUI._GraphValue<Self>, inputs: SwiftUI._ViewInputs)
    -> SwiftUI._ViewOutputs
  {
    return ObservationView<Body>._makeView(view: view[\.observationView], inputs: inputs)
  }

  static func _makeViewList(view: SwiftUI._GraphValue<Self>, inputs: SwiftUI._ViewListInputs)
    -> SwiftUI._ViewListOutputs
  {
    return ObservationView<Body>._makeViewList(view: view[\.observationView], inputs: inputs)
  }

  static func _viewListCount(inputs: SwiftUI._ViewListCountInputs) -> Int? {
    return ObservationView<Body>._viewListCount(inputs: inputs)
  }
}
@winddpan winddpan changed the title ObservationView修改Body的改进尝试 ObservationView不修改body的改进尝试 Oct 18, 2023
@winddpan
Copy link
Author

Day2

发现只要View中有StateObject就报错:
Accessing StateObject's object without being installed on a View. This will create a new instance each time.

The only dynamic property a VersionedView / VersionedViewModifier can only contain is a Binding

一种取巧的尝试

@Observable final class Person {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

class Ref: ObservableObject {
    let randomColor = Color(
        red: .random(in: 0 ... 1),
        green: .random(in: 0 ... 1),
        blue: .random(in: 0 ... 1)
    )
}

struct ContentView: View {
    private var person = Person(name: "Tom", age: 12)
    @StateObject private var ref = Ref()
    @State private var randomColor = Color(
        red: .random(in: 0 ... 1),
        green: .random(in: 0 ... 1),
        blue: .random(in: 0 ... 1)
    )

    var body: some View {
        VStack {
            Text(person.name)
            Text("\(person.age)")

            HStack {
                Button("+") { person.age += 1 }
                Button("-") { person.age -= 1 }
                Button("name") { person.name += "@" }
            }
        }
        .padding()
        .background(randomColor)
        .foregroundColor(ref.randomColor)
    }
}

extension ContentView {
    private struct Impl: View {
        private var person = Person(name: "Tom", age: 12)
        @StateObject private var ref = Ref()
        @State private var randomColor = Color(
            red: .random(in: 0 ... 1),
            green: .random(in: 0 ... 1),
            blue: .random(in: 0 ... 1)
        )

        var body: some View {
            ObservationView {
                VStack {
                    Text(person.name)
                    Text("\(person.age)")

                    HStack {
                        Button("+") { person.age += 1 }
                        Button("-") { person.age -= 1 }
                        Button("name") { person.name += "@" }
                    }
                }
                .padding()
                .background(randomColor)
                .foregroundColor(ref.randomColor)
            }
        }
    }

    @ViewBuilder
    @MainActor
    private var observationBody: Impl {
        var mutatingSelf = self
        let ptr = withUnsafePointer(to: &mutatingSelf) { UnsafeRawPointer($0) }
        ptr.load(as: Impl.self)
    }

    static func _makeView(view: SwiftUI._GraphValue<Self>, inputs: SwiftUI._ViewInputs)
        -> SwiftUI._ViewOutputs {
        Impl._makeView(view: view[\.observationBody], inputs: inputs)
    }

    static func _makeViewList(view: SwiftUI._GraphValue<Self>, inputs: SwiftUI._ViewListInputs)
        -> SwiftUI._ViewListOutputs {
        Impl._makeViewList(view: view[\.observationBody], inputs: inputs)
    }

    static func _viewListCount(inputs: SwiftUI._ViewListCountInputs) -> Int? {
        Impl._viewListCount(inputs: inputs)
    }
}

但失去了原body断点的能力(实际调用了Impl的body),增加了一点包体积。😂😂😂
我决定在一个小项目上实验一下。

@Rex-xingjl
Copy link

进展咋样了 老哥

@winddpan
Copy link
Author

winddpan commented Nov 3, 2023

进展咋样了 老哥

  • _makeView方式无法做到局部刷新,已放弃。
  • ObservationView方式对延时加载的闭包无法处理,需要在延时调用的地方再包一层ObservationView,是一种负担。
  • 目前在尝试手动控制ObservationTracking._AccessList开闭,API上加个@Observing即可,运行看起来没有什么问题(玩具阶段)

https://github.com/winddpan/ObservationBP/blob/Observing-DynamicProperty/Demo/ObservationBPSwiftUIDemo/ObservationBPSwiftUIDemo/DevView.swift

@Rex-xingjl
Copy link

进展咋样了 老哥

  • _makeView方式无法做到局部刷新,已放弃。
  • ObservationView方式对延时加载的闭包无法处理,需要在延时调用的地方再包一层ObservationView,是一种负担。
  • 目前在尝试手动控制ObservationTracking._AccessList开闭,API上加个@Observing即可,运行看起来没有什么问题(玩具阶段)

https://github.com/winddpan/ObservationBP/blob/Observing-DynamicProperty/Demo/ObservationBPSwiftUIDemo/ObservationBPSwiftUIDemo/DevView.swift

我也在尝试ObservationView这种形式
@observing 这种方案不错 我也尝试一下

@Rex-xingjl
Copy link

Rex-xingjl commented Nov 7, 2023

进展咋样了 老哥

  • _makeView方式无法做到局部刷新,已放弃。
  • ObservationView方式对延时加载的闭包无法处理,需要在延时调用的地方再包一层ObservationView,是一种负担。
  • 目前在尝试手动控制ObservationTracking._AccessList开闭,API上加个@Observing即可,运行看起来没有什么问题(玩具阶段)

https://github.com/winddpan/ObservationBP/blob/Observing-DynamicProperty/Demo/ObservationBPSwiftUIDemo/ObservationBPSwiftUIDemo/DevView.swift

老哥 集成到项目里时 我遇到了一个问题:怎么把package结构移除掉呢
ObservationBP这个我打了个xcframework 但是Macro和Swift5.9的package 解决不掉了...
有下面这个报错 :
External macro implementation type 'ObservationBPMacrosMacros.ObservableMacro' could not be found for macro 'Observable()'

@winddpan
Copy link
Author

进展咋样了 老哥

  • _makeView方式无法做到局部刷新,已放弃。
  • ObservationView方式对延时加载的闭包无法处理,需要在延时调用的地方再包一层ObservationView,是一种负担。
  • 目前在尝试手动控制ObservationTracking._AccessList开闭,API上加个@Observing即可,运行看起来没有什么问题(玩具阶段)

https://github.com/winddpan/ObservationBP/blob/Observing-DynamicProperty/Demo/ObservationBPSwiftUIDemo/ObservationBPSwiftUIDemo/DevView.swift

老哥 集成到项目里时 我遇到了一个问题:怎么把package结构移除掉呢 ObservationBP这个我打了个xcframework 但是Macro和Swift5.9的package 解决不掉了... 有下面这个报错 : External macro implementation type 'ObservationBPMacrosMacros.ObservableMacro' could not be found for macro 'Observable()'

Macro的包官方只支持用SPM集成,貌似有Cocoapods的方式但是没有研究。
一周前的版本还有一些问题,今天我提交了一个新的版本,解决了实例保存、局部刷新部分情况失效、内存泄漏等问题。如果可以的话请帮我测试一下。

@Rex-xingjl
Copy link

Cocoapods的形式,我试了一下 没有成功,因为没法解决宏替换的问题。

  1. ObservationBP用到了swift5.9的Macro,而Macro必须由Package来包装。
  2. Package这种形式和LocalPods似乎不兼容。

最近在排期内,闲了再来进行这个继续研究。

@beforeold
Copy link

进展咋样了 老哥

  • _makeView方式无法做到局部刷新,已放弃。
  • ObservationView方式对延时加载的闭包无法处理,需要在延时调用的地方再包一层ObservationView,是一种负担。
  • 目前在尝试手动控制ObservationTracking._AccessList开闭,API上加个@Observing即可,运行看起来没有什么问题(玩具阶段)

https://github.com/winddpan/ObservationBP/blob/Observing-DynamicProperty/Demo/ObservationBPSwiftUIDemo/ObservationBPSwiftUIDemo/DevView.swift

很好很强大,我 fork 下来发现无法在 @observable 修饰的对象中新增计算属性,比如在 Person 中添加 var myName: String { name } 会报错:

'accessor' macro cannot be attached to getter

@winddpan
Copy link
Author

进展咋样了 老哥

  • _makeView方式无法做到局部刷新,已放弃。
  • ObservationView方式对延时加载的闭包无法处理,需要在延时调用的地方再包一层ObservationView,是一种负担。
  • 目前在尝试手动控制ObservationTracking._AccessList开闭,API上加个@Observing即可,运行看起来没有什么问题(玩具阶段)

https://github.com/winddpan/ObservationBP/blob/Observing-DynamicProperty/Demo/ObservationBPSwiftUIDemo/ObservationBPSwiftUIDemo/DevView.swift

很好很强大,我 fork 下来发现无法在 @observable 修饰的对象中新增计算属性,比如在 Person 中添加 var myName: String { name } 会报错:

'accessor' macro cannot be attached to getter

已同步apple swift lib 最新版本

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants