$30 off During Our Annual Pro Sale. View Details »

Resolve Nested ObservableObject issues in Observation

elmetal
November 01, 2023

Resolve Nested ObservableObject issues in Observation

ObservableObjectをプロパティに持つObsevableObjectはCombineの監視の仕組み上問題があります。Observationでは、Combineの監視とは異なる手段で監視することでこの問題を解決しています。
CombineとObservationの監視の仕組みの違いを見て、元の問題がどう解決されるかを紹介します。

elmetal

November 01, 2023
Tweet

More Decks by elmetal

Other Decks in Programming

Transcript

  1. 3FTPMWF/FTUFE0CTFSWBCMF0CKFDU
    JTTVFTJO0CTFSWBUJPO
    $ZCP[V.PCJMF.FFUVQ
    FMNFUBM$ZCP[V *OD

    View Slide

  2. "CPVUNF
    !FM@NFUBM@

    J04"QQ
    %FWFMPQFS J04ΞϓϦ։ൃ ڝഅ ରઓήʔϜ

    View Slide

  3. /FTUFE0CTFSWBCMF0CKFDU

    View Slide

  4. /FTUFE0CTFSWBCMF0CKFDU
    8IBUBOJTTVFPG/FTUFE0CTFSWBCMF0CKFDU
    0CTFSWBCMF0CKFDUΛϓϩύςΟʹ࣋ͭ0CTFSWBCMF0CKFDUͰมߋ͕௨஌͞Εͳ͍

    View Slide

  5. final class Parent: ObservableObject {
    @Published var child: Child
    init(child: Child) {
    self.child = child
    }
    }
    final class Child: ObservableObject {
    @Published var age: Int
    init(age: Int) {
    self.age = age
    }
    func haveBirthday() {
    age += 1
    }
    }
    /FTUFE0CTFSWBCMF0CKFDU
    3FQSPEVDFUIFJTTVFPG0CTFSWBCMF0CKFDU

    View Slide

  6. /FTUFE0CTFSWBCMF0CKFDU
    3FQSPEVDFUIFJTTVFPG0CTFSWBCMF0CKFDU
    final class Parent: ObservableObject {
    @Published var child: Child
    init(child: Child) {
    self.child = child
    }
    }
    final class Child: ObservableObject {
    @Published var age: Int
    init(age: Int) {
    self.age = age
    }
    func haveBirthday() {
    age += 1
    }
    }
    struct BirthdayView: View {
    @StateObject var parent = Parent(child: Child(age: 1))
    var body: some View {
    Text("child is \(parent.child.age) years old")
    Button("Have Birthday", action: { parent.child.haveBirthday() })
    }
    }

    View Slide

  7. final class Parent: ObservableObject {
    @Published var child: Child
    private var cancellables = Set()
    init(child: Child) {
    self.child = child
    child.objectWillChange.sink {
    [weak self] in self?.objectWillChange.send()
    }
    .store(in: &cancellables)
    }
    }
    final class Child: ObservableObject {
    @Published var age: Int
    init(age: Int) {
    self.age = age
    }
    func haveBirthday() {
    age += 1
    }
    }
    /FTUFE0CTFSWBCMF0CKFDU
    'JYUIFJTTVFPG0CTFSWBCMF0CKFDU

    View Slide

  8. final class Parent: ObservableObject {
    @Published var child: Child
    private var cancellables = Set()
    init(child: Child) {
    self.child = child
    child.objectWillChange.sink {
    [weak self] in self?.objectWillChange.send()
    }
    .store(in: &cancellables)
    }
    }
    final class Child: ObservableObject {
    @Published var age: Int
    init(age: Int) {
    self.age = age
    }
    func haveBirthday() {
    age += 1
    }
    }
    struct BirthdayView: View {
    @StateObject var parent = Parent(child: Child(age: 1))
    var body: some View {
    Text("child is \(parent.child.age) years old")
    Button("Have Birthday", action: { parent.child.haveBirthday() })
    }
    }
    /FTUFE0CTFSWBCMF0CKFDU
    'JYUIFJTTVFPG0CTFSWBCMF0CKFDU

    View Slide

  9. /FTUFE!0CTFSWBCMF

    View Slide

  10. @Observable final class Parent {
    var child: Child
    init(child: Child) {
    self.child = child
    }
    }
    @Observable final class Child {
    var age: Int
    init(age: Int) {
    self.age = age
    }
    func haveBirthday() {
    age += 1
    }
    }
    /FTUFE!0CTFSWBCMF
    8JMMUIFJTTVFCFSFQSPEVDFE

    View Slide

  11. 5IFJTTVFJTSFTPMWFE
    @Observable final class Parent {
    var child: Child
    init(child: Child) {
    self.child = child
    }
    }
    @Observable final class Child {
    var age: Int
    init(age: Int) {
    self.age = age
    }
    func haveBirthday() {
    age += 1
    }
    }
    struct BirthdayView: View {
    @State var parent = Parent(child: Child(age: 1))
    var body: some View {
    Text("child is \(parent.child.age) years old")
    Button("Have Birthday", action: { parent.child.haveBirthday() })
    }
    }
    /FTUFE!0CTFSWBCMF

    View Slide

  12. *OMJOF.BDSP

    View Slide

  13. *OMJOF!0CTFSWBCMF.BDSP
    final class Child {
    @ObservationTracked var age: Int
    init(age: Int) {
    self.age = age
    }
    func haveBirthday() {
    age += 1
    }
    @ObservationIgnored private let _$observationRegistrar = Observation.ObservationRegistrar()
    internal nonisolated func access(
    keyPath: KeyPath
    ) {
    _$observationRegistrar.access(self, keyPath: keyPath)
    }
    internal nonisolated func withMutation(
    keyPath: KeyPath,
    _ mutation: () throws -> MutationResult
    ) rethrows -> MutationResult {
    try _$observationRegistrar.withMutation(of: self, keyPath: keyPath, mutation)
    }
    }
    extension Child: Observation.Observable {
    }

    View Slide

  14. *OMJOF!0CTFSWBUJPO5SBDLFE.BDSP
    final class Child {
    var age: Int{
    @storageRestrictions(initializes: _age)
    init(initialValue) {
    _age = initialValue
    }
    get {
    access(keyPath: \.age)
    return _age
    }
    set {
    withMutation(keyPath: \.age) {
    _age = newValue
    }
    }
    }

    }

    View Slide

  15. 4VNNBSZ

    View Slide

  16. 4VNNBSZ
    w 0CTFSWBCMF0CKFDUΛϓϩύςΟʹ࣋ͭ0CTFSWBCMF0CKFDUͰࢠڙͷมߋΛ௨஌
    ͢Δʹ͸खಈͰPCKFDU8JMM$IBOHFͷTFOE͕ඞཁ
    w 0CTFSWBUJPOͰ͸0CTFSWBCMF0CKFDUͱҟͳΔ࢓૊ΈΛఏڙ͢Δ͜ͱͰ໰୊Λ
    ղܾͨ͠

    View Slide

  17. *OUFSBDUJWF5VUPSJBMT
    /FTUFE0CTFSWBCMF0CKFDU
    IUUQTFMNFUBMHJUIVCJP0CTFSWBUJPO*O%FQUIUVUPSJBMTPCTFSWBUJPOJOEFQUIOFTUFEPCTFSWBCMFPCKFDU

    View Slide