@@ -3,6 +3,9 @@ import RiveRuntime
33
44/// Protocol for Rive property types that support listener management
55protocol RivePropertyWithListeners : AnyObject {
6+ associatedtype ListenerValueType
7+
8+ func addListener( _ callback: @escaping ( ListenerValueType ) -> Void ) -> UUID
69 func removeListener( _ id: UUID )
710}
811
@@ -15,16 +18,24 @@ typealias TriggerPropertyType = RiveDataBindingViewModel.Instance.TriggerPropert
1518typealias ImagePropertyType = RiveDataBindingViewModel . Instance . ImageProperty
1619
1720// Make all Rive property types conform to the protocol
18- extension BooleanPropertyType : RivePropertyWithListeners { }
19- extension NumberPropertyType : RivePropertyWithListeners { }
20- extension StringPropertyType : RivePropertyWithListeners { }
21- extension EnumPropertyType : RivePropertyWithListeners { }
22- extension ColorPropertyType : RivePropertyWithListeners { }
23- extension TriggerPropertyType : RivePropertyWithListeners { }
24- extension ImagePropertyType : RivePropertyWithListeners { }
21+ extension BooleanPropertyType : RivePropertyWithListeners {
22+ typealias ListenerValueType = Bool // Native: Bool → Bool (no conversion)
23+ }
24+ extension NumberPropertyType : RivePropertyWithListeners {
25+ typealias ListenerValueType = Float // Native: Float → Double (needs conversion)
26+ }
27+ extension StringPropertyType : RivePropertyWithListeners {
28+ typealias ListenerValueType = String // Native: String → String (no conversion)
29+ }
30+ extension EnumPropertyType : RivePropertyWithListeners {
31+ typealias ListenerValueType = String // Native: String → String (no conversion)
32+ }
33+ extension ColorPropertyType : RivePropertyWithListeners {
34+ typealias ListenerValueType = UIColor // Native: UIColor → Double (needs conversion)
35+ }
36+ // Note: TriggerProperty doesn't fit the pattern - it has () -> Void listeners, not (Void) -> Void
2537
2638/// Helper class for managing ViewModel property listeners
27- /// Similar to Android's BaseHybridViewModelPropertyImpl, provides reusable listener management
2839class PropertyListenerHelper < PropertyType: RivePropertyWithListeners > {
2940 private var listenerIds : [ UUID ] = [ ]
3041 weak var property : PropertyType ?
@@ -33,11 +44,10 @@ class PropertyListenerHelper<PropertyType: RivePropertyWithListeners> {
3344 self . property = property
3445 }
3546
36- /// Adds a listener and automatically tracks its ID for later cleanup
37- /// - Parameter addListener: Closure that adds the listener to the property and returns the listener ID
38- func trackListener( _ addListener: ( PropertyType ) -> UUID ) {
47+ /// Adds a listener to the property and automatically tracks its ID for cleanup
48+ func addListener( _ callback: @escaping ( PropertyType . ListenerValueType ) -> Void ) {
3949 guard let property = property else { return }
40- let id = addListener ( property )
50+ let id = property . addListener ( callback )
4151 listenerIds. append ( id)
4252 }
4353
@@ -54,19 +64,23 @@ class PropertyListenerHelper<PropertyType: RivePropertyWithListeners> {
5464 }
5565}
5666
57- /// Protocol that provides common functionality for all hybrid ViewModel property classes
58- /// Reduces boilerplate by providing default implementations for listener management
59- protocol ViewModelPropertyProtocol {
67+ /// Protocol for properties that have typed values (Bool, String, Double, etc.)
68+ /// Provides a default addListener implementation
69+ protocol ValuedPropertyProtocol < ValueType > {
6070 associatedtype PropertyType : RivePropertyWithListeners
71+ associatedtype ValueType
6172
73+ var property : PropertyType ! { get }
6274 var helper : PropertyListenerHelper < PropertyType > { get }
63-
75+
76+ func addListener( onChanged: @escaping ( ValueType ) -> Void ) throws
6477 func removeListeners( ) throws
6578 func dispose( ) throws
6679}
6780
68- /// Default implementations for common listener management methods
69- extension ViewModelPropertyProtocol {
81+
82+ /// Default implementations for lifecycle methods (always available)
83+ extension ValuedPropertyProtocol {
7084 func removeListeners( ) throws {
7185 try helper. removeListeners ( )
7286 }
@@ -76,3 +90,10 @@ extension ViewModelPropertyProtocol {
7690 }
7791}
7892
93+ /// Automatic addListener() ONLY when ListenerValueType == ValueType (no conversion needed)
94+ extension ValuedPropertyProtocol where PropertyType. ListenerValueType == ValueType {
95+ func addListener( onChanged: @escaping ( ValueType ) -> Void ) throws {
96+ helper. addListener ( onChanged) // Types match, just forward directly!
97+ }
98+ }
99+
0 commit comments