fix(apple): update listeners on list instance changes (#10469) 8ce5e60a6d

Co-authored-by: David Skuza <david@rive.app>
This commit is contained in:
dskuza
2025-09-10 14:30:21 +00:00
parent 892d8bee75
commit 099f8bcf3d
4 changed files with 121 additions and 29 deletions

View File

@@ -1 +1 @@
2d7b1b788fead944d5765726ee4e37925357463d
8ce5e60a6d58cc2e6a8950961150f1fc8affa2f9

View File

@@ -423,10 +423,7 @@
NSString* _Nonnull key,
RiveDataBindingViewModelInstanceProperty* _Nonnull obj,
BOOL* _Nonnull stop) {
if (obj.hasChanged)
{
[obj handleListeners];
}
[obj handleListeners];
}];
[_properties enumerateKeysAndObjectsUsingBlock:^(

View File

@@ -193,10 +193,13 @@
- (void)handleListeners
{
for (RiveDataBindingViewModelInstanceStringPropertyListener listener in self
.listeners.allValues)
if (self.hasChanged)
{
listener(self.value);
for (RiveDataBindingViewModelInstanceStringPropertyListener listener in
self.listeners.allValues)
{
listener(self.value);
}
}
}
@@ -243,10 +246,13 @@
- (void)handleListeners
{
for (RiveDataBindingViewModelInstanceNumberPropertyListener listener in self
.listeners.allValues)
if (self.hasChanged)
{
listener(self.value);
for (RiveDataBindingViewModelInstanceNumberPropertyListener listener in
self.listeners.allValues)
{
listener(self.value);
}
}
}
@@ -296,10 +302,13 @@
- (void)handleListeners
{
for (RiveDataBindingViewModelInstanceBooleanPropertyListener listener in
self.listeners.allValues)
if (self.hasChanged)
{
listener(self.value);
for (RiveDataBindingViewModelInstanceBooleanPropertyListener listener in
self.listeners.allValues)
{
listener(self.value);
}
}
}
@@ -409,10 +418,13 @@
- (void)handleListeners
{
for (RiveDataBindingViewModelInstanceColorPropertyListener listener in self
.listeners.allValues)
if (self.hasChanged)
{
listener(self.value);
for (RiveDataBindingViewModelInstanceColorPropertyListener listener in
self.listeners.allValues)
{
listener(self.value);
}
}
}
@@ -484,10 +496,13 @@
- (void)handleListeners
{
for (RiveDataBindingViewModelInstanceEnumPropertyListener listener in self
.listeners.allValues)
if (self.hasChanged)
{
listener(self.value);
for (RiveDataBindingViewModelInstanceEnumPropertyListener listener in
self.listeners.allValues)
{
listener(self.value);
}
}
}
@@ -528,10 +543,13 @@
- (void)handleListeners
{
for (RiveDataBindingViewModelInstanceTriggerPropertyListener listener in
self.listeners.allValues)
if (self.hasChanged)
{
listener();
for (RiveDataBindingViewModelInstanceTriggerPropertyListener listener in
self.listeners.allValues)
{
listener();
}
}
}
@@ -573,10 +591,13 @@
- (void)handleListeners
{
for (RiveDataBindingViewModelInstanceImagePropertyListener listener in self
.listeners.allValues)
if (self.hasChanged)
{
listener();
for (RiveDataBindingViewModelInstanceImagePropertyListener listener in
self.listeners.allValues)
{
listener();
}
}
}
@@ -684,10 +705,18 @@
- (void)handleListeners
{
for (RiveDataBindingViewModelInstanceListPropertyListener listener in self
.listeners.allValues)
if (self.hasChanged)
{
listener();
for (RiveDataBindingViewModelInstanceListPropertyListener listener in
self.listeners.allValues)
{
listener();
}
}
for (RiveDataBindingViewModelInstance* instance in _instances.allValues)
{
[instance updateListeners];
}
}

View File

@@ -865,6 +865,72 @@ class DataBindingTests: XCTestCase {
wait(for: [removeExpectation], timeout: 1)
}
func test_listProperty_listener_canAddAndRemoveListener() throws {
let instance = file.viewModelNamed("Test")!.createDefaultInstance()!
let artboard = try file.artboard()
let stateMachine = try artboard.stateMachine(from: 0)
stateMachine.bind(viewModelInstance: instance)
let addExpectation = expectation(description: "listener called")
let property = instance.listProperty(fromPath: "List")!
let listener = property.addListener {
addExpectation.fulfill()
}
let testInstance = file.viewModelNamed("Test")!.createInstance()!
property.append(testInstance)
stateMachine.advance(by: 0)
instance.updateListeners()
wait(for: [addExpectation], timeout: 1)
let removeExpectation = expectation(description: "listener called")
removeExpectation.isInverted = true
property.removeListener(listener)
property.remove(testInstance)
stateMachine.advance(by: 0)
instance.updateListeners()
wait(for: [removeExpectation], timeout: 1)
}
func test_listProperty_whenListItemUpdates_callsListener() throws {
let instance = file.viewModelNamed("Test")!.createDefaultInstance()!
let artboard = try file.artboard()
let stateMachine = try artboard.stateMachine(from: 0)
stateMachine.bind(viewModelInstance: instance)
let testExpectation = expectation(description: "listener called")
testExpectation.expectedFulfillmentCount = 2
let testInstance = file.viewModelNamed("Test")!.createInstance()!
let testTrigger = testInstance.triggerProperty(fromPath: "Trigger Red")!
testTrigger.addListener {
testExpectation.fulfill()
}
let list = instance.listProperty(fromPath: "List")!
list.append(testInstance)
testTrigger.trigger()
stateMachine.advance(by: 0)
instance.updateListeners()
let nestedList = testInstance.listProperty(fromPath: "List")!
let nestedInstance = file.viewModelNamed("Test")!.createInstance()!
nestedList.append(nestedInstance)
let nestedTrigger = nestedInstance.triggerProperty(fromPath: "Trigger Red")!
nestedTrigger.addListener {
testExpectation.fulfill()
}
nestedTrigger.trigger()
stateMachine.advance(by: 0)
instance.updateListeners()
wait(for: [testExpectation], timeout: 1)
}
func test_property_listener_afterAddingSingleListener_callsListener() throws {
let instance = file.viewModelNamed("Test")!.createDefaultInstance()!
let artboard = try file.artboard()