Skip to content

Commit a107571

Browse files
committed
Add initial ObservationRegistrar logic
1 parent 25b516b commit a107571

File tree

4 files changed

+123
-0
lines changed

4 files changed

+123
-0
lines changed

KMMViewModel.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
1D198B302933C01800EF778D /* KMMVMViewModelScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D198B2E2933C01800EF778D /* KMMVMViewModelScope.h */; settings = {ATTRIBUTES = (Public, ); }; };
1818
1D198B312933C01800EF778D /* KMMViewModelCoreObjC.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D198B2F2933C01800EF778D /* KMMViewModelCoreObjC.m */; };
1919
1D198B322933C04400EF778D /* KMMViewModelCoreObjC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D198B272933BFD900EF778D /* KMMViewModelCoreObjC.framework */; };
20+
1D2AAC592BB1F528005F1344 /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D2AAC582BB1F528005F1344 /* Observable.swift */; };
21+
1D2AAC5B2BB2053D005F1344 /* ViewModelObservationRegistrar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D2AAC5A2BB2053D005F1344 /* ViewModelObservationRegistrar.swift */; };
22+
1D2AAC5D2BB205AC005F1344 /* ObservableProperty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D2AAC5C2BB205AC005F1344 /* ObservableProperty.swift */; };
2023
1D43F3EC2ABAFCA600EB3DFE /* ObservableViewModelPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D43F3EB2ABAFCA600EB3DFE /* ObservableViewModelPublisher.swift */; };
2124
1D43F3EE2ABAFD7D00EB3DFE /* ObservableViewModelPublishers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D43F3ED2ABAFD7D00EB3DFE /* ObservableViewModelPublishers.swift */; };
2225
1D6641DD2A5175C3000180D7 /* ChildViewModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D6641DC2A5175C3000180D7 /* ChildViewModels.swift */; };
@@ -52,6 +55,9 @@
5255
1D198B292933BFD900EF778D /* KMMViewModelCoreObjC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KMMViewModelCoreObjC.h; sourceTree = "<group>"; };
5356
1D198B2E2933C01800EF778D /* KMMVMViewModelScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KMMVMViewModelScope.h; sourceTree = "<group>"; };
5457
1D198B2F2933C01800EF778D /* KMMViewModelCoreObjC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KMMViewModelCoreObjC.m; sourceTree = "<group>"; };
58+
1D2AAC582BB1F528005F1344 /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = "<group>"; };
59+
1D2AAC5A2BB2053D005F1344 /* ViewModelObservationRegistrar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModelObservationRegistrar.swift; sourceTree = "<group>"; };
60+
1D2AAC5C2BB205AC005F1344 /* ObservableProperty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableProperty.swift; sourceTree = "<group>"; };
5561
1D43F3EB2ABAFCA600EB3DFE /* ObservableViewModelPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableViewModelPublisher.swift; sourceTree = "<group>"; };
5662
1D43F3ED2ABAFD7D00EB3DFE /* ObservableViewModelPublishers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableViewModelPublishers.swift; sourceTree = "<group>"; };
5763
1D6641DC2A5175C3000180D7 /* ChildViewModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChildViewModels.swift; sourceTree = "<group>"; };
@@ -114,6 +120,9 @@
114120
1D0DA80129336AD40057DDAD /* ObservableViewModel.swift */,
115121
1D43F3EB2ABAFCA600EB3DFE /* ObservableViewModelPublisher.swift */,
116122
1D43F3ED2ABAFD7D00EB3DFE /* ObservableViewModelPublishers.swift */,
123+
1D2AAC582BB1F528005F1344 /* Observable.swift */,
124+
1D2AAC5A2BB2053D005F1344 /* ViewModelObservationRegistrar.swift */,
125+
1D2AAC5C2BB205AC005F1344 /* ObservableProperty.swift */,
117126
);
118127
path = KMMViewModelCore;
119128
sourceTree = "<group>";
@@ -303,9 +312,12 @@
303312
buildActionMask = 2147483647;
304313
files = (
305314
1D43F3EE2ABAFD7D00EB3DFE /* ObservableViewModelPublishers.swift in Sources */,
315+
1D2AAC5B2BB2053D005F1344 /* ViewModelObservationRegistrar.swift in Sources */,
306316
1D43F3EC2ABAFCA600EB3DFE /* ObservableViewModelPublisher.swift in Sources */,
307317
1DDAF21E293545DD0049C114 /* KMMViewModel.swift in Sources */,
308318
1D6641DD2A5175C3000180D7 /* ChildViewModels.swift in Sources */,
319+
1D2AAC5D2BB205AC005F1344 /* ObservableProperty.swift in Sources */,
320+
1D2AAC592BB1F528005F1344 /* Observable.swift in Sources */,
309321
1D0DA80229336AD40057DDAD /* ObservableViewModel.swift in Sources */,
310322
);
311323
runOnlyForDeploymentPostprocessing = 0;

KMMViewModelCore/Observable.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// Observable.swift
3+
// KMMViewModelCore
4+
//
5+
// Created by Rick Clephas on 25/03/2024.
6+
//
7+
8+
import Foundation
9+
import Observation
10+
import KMMViewModelCoreObjC
11+
12+
@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
13+
public protocol Observable: Observation.Observable {
14+
associatedtype ViewModel: Observable
15+
16+
@ViewModelObservationRegistrarBuilder<ViewModel>
17+
var viewModelObservationRegistrar: ViewModelObservationRegistrar<ViewModel> { get }
18+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//
2+
// ObservableProperty.swift
3+
// KMMViewModelCore
4+
//
5+
// Created by Rick Clephas on 25/03/2024.
6+
//
7+
8+
import Observation
9+
10+
@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
11+
public protocol ObservableProperty<ViewModel> {
12+
associatedtype ViewModel: Observable
13+
14+
func willSet(registrar: ObservationRegistrar, viewModel: ViewModel)
15+
16+
func didSet(registrar: ObservationRegistrar, viewModel: ViewModel)
17+
18+
func access(registrar: ObservationRegistrar, viewModel: ViewModel)
19+
}
20+
21+
@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
22+
extension KeyPath: ObservableProperty where Root: Observable {
23+
public typealias ViewModel = Root
24+
25+
public func willSet(registrar: ObservationRegistrar, viewModel: ViewModel) {
26+
registrar.willSet(viewModel, keyPath: self)
27+
}
28+
29+
public func didSet(registrar: ObservationRegistrar, viewModel: ViewModel) {
30+
registrar.didSet(viewModel, keyPath: self)
31+
}
32+
33+
public func access(registrar: ObservationRegistrar, viewModel: ViewModel) {
34+
registrar.access(viewModel, keyPath: self)
35+
}
36+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//
2+
// ViewModelObservationRegistrar.swift
3+
// KMMViewModelCore
4+
//
5+
// Created by Rick Clephas on 25/03/2024.
6+
//
7+
8+
import Foundation
9+
import Observation
10+
11+
@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
12+
public struct ViewModelObservationRegistrar<ViewModel: Observable> {
13+
14+
private let registrar = ObservationRegistrar()
15+
16+
private let properties: [NSObject:[any ObservableProperty<ViewModel>]]
17+
18+
internal init(properties: [any ObservableProperty<ViewModel>]) {
19+
self.properties = [:] // TODO: store and map properties
20+
}
21+
22+
internal func willSet(_ viewModel: ViewModel, property: NSObject) -> Bool {
23+
guard let properties = properties[property] else { return false }
24+
for property in properties {
25+
property.willSet(registrar: registrar, viewModel: viewModel)
26+
}
27+
return true
28+
}
29+
30+
internal func didSet(_ viewModel: ViewModel, property: NSObject) -> Bool {
31+
guard let properties = properties[property] else { return false }
32+
for property in properties {
33+
property.didSet(registrar: registrar, viewModel: viewModel)
34+
}
35+
return true
36+
}
37+
38+
internal func access(_ viewModel: ViewModel, property: NSObject) -> Bool {
39+
guard let properties = properties[property] else { return false }
40+
for property in properties {
41+
property.access(registrar: registrar, viewModel: viewModel)
42+
}
43+
return true
44+
}
45+
}
46+
47+
@resultBuilder
48+
@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
49+
public struct ViewModelObservationRegistrarBuilder<ViewModel: Observable> {
50+
public static func buildExpression<Member>(_ expression: KeyPath<ViewModel, Member>) -> any ObservableProperty<ViewModel> {
51+
return expression
52+
}
53+
54+
public static func buildBlock(_ components: any ObservableProperty<ViewModel>...) -> ViewModelObservationRegistrar<ViewModel> {
55+
return ViewModelObservationRegistrar(properties: components)
56+
}
57+
}

0 commit comments

Comments
 (0)