Skip to content

Commit e4b4fe3

Browse files
committed
refactor(ios): advanced property listener store and cleanup
1 parent 44143b1 commit e4b4fe3

1 file changed

Lines changed: 145 additions & 76 deletions

File tree

ios/RiveReactNativeView.swift

Lines changed: 145 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
77
private var eventEmitter: RiveReactNativeEventModule? {
88
return self.bridge?.module(for: RiveReactNativeEventModule.self) as? RiveReactNativeEventModule
99
}
10-
private var propertyListeners: [(key: String, property: RiveDataBindingViewModel.Instance.Property, listener: UUID)] = []
10+
private var propertyListeners: [String: PropertyListener] = [:]
1111
private var dataBindingConfig: DataBindingConfig?
1212

1313
// MARK: RiveReactNativeView Properties
@@ -146,10 +146,11 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
146146
if let loadedTag = generateLoadedTag() {
147147
eventEmitter?.removeListener(byName: loadedTag)
148148
}
149-
propertyListeners.forEach {(key: String, property: RiveDataBindingViewModel.Instance.Property, listener: UUID) in
150-
property.removeListener(listener)
149+
propertyListeners.forEach { (key, value) in
150+
value.property.removeListener(value.listener)
151151
eventEmitter?.removeListener(byName: key)
152152
}
153+
propertyListeners.removeAll()
153154
dataBindingViewModelInstance = nil
154155
}
155156

@@ -213,12 +214,22 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
213214

214215
private func configureDataBinding(viewModel: RiveViewModel) {
215216
guard let artboard = viewModel.riveModel?.artboard,
216-
let dataBindingViewModel = viewModel.riveModel?.riveFile.defaultViewModel(for: artboard) else { return }
217+
let dataBindingViewModel = viewModel.riveModel?.riveFile.defaultViewModel(for: artboard) else { return }
217218

218219
func bindInstance(_ instance: RiveDataBindingViewModel.Instance?) {
219220
guard let instance = instance else { return }
220221
viewModel.riveModel?.stateMachine?.bind(viewModelInstance: instance)
221222
self.dataBindingViewModelInstance = instance
223+
224+
// As we can't control whether `configureDataBinding` is called
225+
// before/after/between `registerPropertyListener` (if it is called again) we
226+
// re-add the current registered listeners if the instance is not the same
227+
// to ensure they are attached to the current active dataBindingViewModelInstance
228+
propertyListeners.forEach { (_, value) in
229+
if (value.dataBindingInstance != self.dataBindingViewModelInstance) {
230+
registerPropertyListener(path: value.path, propertyType: value.propertyType)
231+
}
232+
}
222233
}
223234

224235
switch dataBindingConfig {
@@ -230,12 +241,16 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
230241
} else {
231242
viewModel.riveModel?.disableAutoBind()
232243
}
244+
break
233245
case .index(let index):
234246
bindInstance(dataBindingViewModel.createInstance(fromIndex: UInt(index)))
247+
break
235248
case .name(let name):
236249
bindInstance(dataBindingViewModel.createInstance(fromName: name))
250+
break
237251
case .empty:
238252
bindInstance(dataBindingViewModel.createInstance())
253+
break
239254
case nil:
240255
break
241256
}
@@ -633,82 +648,110 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
633648
dataBindingViewModelInstance?.triggerProperty(fromPath: path)?.trigger()
634649
}
635650

636-
private func storeProperty(key: String, property: RiveDataBindingViewModel.Instance.Property?, listener: UUID?) {
637-
if let property = property, let listener = listener {
638-
propertyListeners.append((key: key, property: property, listener: listener))
651+
private func storeProperty(key: String, propertyListener: PropertyListener) {
652+
if let existingListener = propertyListeners[key]?.listener, let existingProperty = propertyListeners[key]?.property {
653+
existingProperty.removeListener(existingListener)
639654
}
655+
propertyListeners[key] = propertyListener
656+
}
657+
658+
private struct PropertyRegistration {
659+
let property: RiveDataBindingViewModel.Instance.Property
660+
let initialValue: Any
661+
let createListener: () -> UUID?
640662
}
641663

642664
func registerPropertyListener(path: String, propertyType: String) {
643-
guard let reactTag = self.reactTag else { return }
665+
guard let reactTag = self.reactTag,
666+
let dataBindingInstance = dataBindingViewModelInstance,
667+
let propertyTypeEnum = safePropertyType(propertyType) else { return }
668+
644669
let key = "\(propertyType):\(path):\(reactTag)"
645-
let propertyTypeEnum = safePropertyType(propertyType)
646-
switch propertyTypeEnum {
647-
case .String:
648-
let stringProperty = dataBindingViewModelInstance?.stringProperty(fromPath: path)
649-
if let initialValue = stringProperty?.value {
650-
eventEmitter?.sendEvent(withName: key, body: initialValue)
651-
}
652-
let listener = stringProperty?.addListener { [weak self] newValue in
653-
self?.eventEmitter?.sendEvent(withName: key, body: newValue)
654-
}
655-
storeProperty(key: key, property: stringProperty, listener: listener)
656-
case .Boolean:
657-
let booleanProperty = dataBindingViewModelInstance?.booleanProperty(fromPath: path)
658-
if let initialValue = booleanProperty?.value {
659-
eventEmitter?.sendEvent(withName: key, body: initialValue)
660-
}
661-
let listener = booleanProperty?.addListener { [weak self] newValue in
662-
self?.eventEmitter?.sendEvent(withName: key, body: newValue)
663-
}
664-
storeProperty(key: key, property: booleanProperty, listener: listener)
665-
case .Number:
666-
let numberProperty = dataBindingViewModelInstance?.numberProperty(fromPath: path)
667-
if let initialValue = numberProperty?.value {
668-
eventEmitter?.sendEvent(withName: key, body: initialValue)
669-
}
670-
let listener = numberProperty?.addListener { [weak self] newValue in
671-
self?.eventEmitter?.sendEvent(withName: key, body: newValue)
672-
}
673-
storeProperty(key: key, property: numberProperty, listener: listener)
674-
case .Color:
675-
let colorProperty = dataBindingViewModelInstance?.colorProperty(fromPath: path)
676-
if let initialValue = colorProperty?.value {
677-
eventEmitter?.sendEvent(withName: key, body: colorToHexInt(initialValue))
678-
}
679-
let listener = colorProperty?.addListener { [weak self] newValue in
680-
self?.eventEmitter?.sendEvent(withName: key, body: self?.colorToHexInt(newValue))
681-
}
682-
storeProperty(key: key, property: colorProperty, listener: listener)
683-
case .Enum:
684-
let enumProperty = dataBindingViewModelInstance?.enumProperty(fromPath: path)
685-
if let initialValue = enumProperty?.value {
686-
eventEmitter?.sendEvent(withName: key, body: initialValue)
687-
}
688-
let listener = enumProperty?.addListener { [weak self] newValue in
689-
self?.eventEmitter?.sendEvent(withName: key, body: newValue)
670+
671+
// Get registration info based on property type
672+
let registration: PropertyRegistration? = {
673+
switch propertyTypeEnum {
674+
case .String:
675+
guard let prop = dataBindingInstance.stringProperty(fromPath: path) else { return nil }
676+
return PropertyRegistration(
677+
property: prop,
678+
initialValue: prop.value,
679+
createListener: { [weak self] in
680+
prop.addListener { newValue in
681+
self?.eventEmitter?.sendEvent(withName: key, body: prop.value)
682+
}
683+
}
684+
)
685+
686+
case .Boolean:
687+
guard let prop = dataBindingInstance.booleanProperty(fromPath: path) else { return nil }
688+
return PropertyRegistration(
689+
property: prop,
690+
initialValue: prop.value,
691+
createListener: { [weak self] in
692+
prop.addListener { newValue in
693+
self?.eventEmitter?.sendEvent(withName: key, body: prop.value)
694+
}
695+
}
696+
)
697+
698+
case .Number:
699+
guard let prop = dataBindingInstance.numberProperty(fromPath: path) else { return nil }
700+
return PropertyRegistration(
701+
property: prop,
702+
initialValue: prop.value,
703+
createListener: { [weak self] in
704+
prop.addListener { newValue in
705+
self?.eventEmitter?.sendEvent(withName: key, body: prop.value)
706+
}
707+
}
708+
)
709+
710+
case .Color:
711+
guard let prop = dataBindingInstance.colorProperty(fromPath: path) else { return nil }
712+
return PropertyRegistration(
713+
property: prop,
714+
initialValue: prop.value.toHexInt(),
715+
createListener: { [weak self] in
716+
prop.addListener { newValue in
717+
self?.eventEmitter?.sendEvent(withName: key, body: prop.value.toHexInt())
718+
}
719+
}
720+
)
721+
722+
case .Enum:
723+
guard let prop = dataBindingInstance.enumProperty(fromPath: path) else { return nil }
724+
return PropertyRegistration(
725+
property: prop,
726+
initialValue: prop.value,
727+
createListener: { [weak self] in
728+
prop.addListener { newValue in
729+
self?.eventEmitter?.sendEvent(withName: key, body: prop.value)
730+
}
731+
}
732+
)
733+
734+
case .Trigger:
735+
return nil
690736
}
691-
storeProperty(key: key, property: enumProperty, listener: listener)
692-
case .Trigger: break
693-
case .none: break
694-
}
695-
}
696-
697-
func colorToHexInt(_ color: UIColor) -> Int {
698-
var red: CGFloat = 0
699-
var green: CGFloat = 0
700-
var blue: CGFloat = 0
701-
var alpha: CGFloat = 0
737+
}()
702738

703-
color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
739+
guard let reg = registration else { return }
704740

705-
let r = UInt32(red * 255)
706-
let g = UInt32(green * 255)
707-
let b = UInt32(blue * 255)
708-
let a = UInt32(alpha * 255)
741+
// Send initial value
742+
eventEmitter?.sendEvent(withName: key, body: reg.initialValue)
709743

710-
let hex = (a << 24) | (r << 16) | (g << 8) | b
711-
return Int(hex)
744+
// Create and store listener
745+
if let listener = reg.createListener() {
746+
let propertyListener = PropertyListener(
747+
dataBindingInstance: dataBindingInstance,
748+
property: reg.property,
749+
listener: listener,
750+
path: path,
751+
propertyType: propertyType
752+
)
753+
storeProperty(key: key, propertyListener: propertyListener)
754+
}
712755
}
713756

714757
// MARK: - StateMachineDelegate
@@ -839,11 +882,37 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate
839882
RCTLogError(error.localizedDescription)
840883
}
841884
}
885+
886+
private enum DataBindingConfig {
887+
case autoBind(Bool)
888+
case index(Int)
889+
case name(String)
890+
case empty
891+
}
892+
893+
private struct PropertyListener {
894+
let dataBindingInstance: RiveDataBindingViewModel.Instance
895+
let property: RiveDataBindingViewModel.Instance.Property
896+
let listener: UUID
897+
let path: String
898+
let propertyType: String
899+
}
842900
}
843901

844-
enum DataBindingConfig {
845-
case autoBind(Bool)
846-
case index(Int)
847-
case name(String)
848-
case empty
902+
extension UIColor {
903+
func toHexInt() -> Int {
904+
var red: CGFloat = 0
905+
var green: CGFloat = 0
906+
var blue: CGFloat = 0
907+
var alpha: CGFloat = 0
908+
909+
self.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
910+
911+
let r = UInt32(red * 255)
912+
let g = UInt32(green * 255)
913+
let b = UInt32(blue * 255)
914+
let a = UInt32(alpha * 255)
915+
916+
return Int((a << 24) | (r << 16) | (g << 8) | b)
917+
}
849918
}

0 commit comments

Comments
 (0)