mirror of
https://github.com/rive-app/rive-ios.git
synced 2026-01-18 17:11:28 +01:00
feat(ios): add support for data binding
Adds support for data binding to the iOS runtime.
## Changes
### RiveFile
Updated to match c++ runtime support for getting view models by index, name, and default for an artboard.
### RiveArtboard
Updated to match c++ runtime support for binding a view model instance.
### RiveStateMachine
Updated to match c++ runtime support for binding a view model instance. Additionally, this holds a strong reference to the view model instance that is currently bound (which aids in knowing which state machine should have its property listeners called after advance).
### RiveModel
Adds support for enabling / disabling autoBind functionality. This has to be done _after_ initialization, since Swift default arguments don't bridge to ObjC, and I didn't want to add n number of new initializers. When enabled, a callback will be called with the bound instance.
## New Types
Currently, type names are similar to those found within the c++ runtime. They're kind of ugly when it comes to ObjC, but the Swift names are cleaned up (e.g `RiveViewModelRuntime.Instance` instead of `RiveViewModelInstanceRuntime`).
### RiveDataBindingViewModel
The bridging type between the c++ runtime equivalent (`rive::ViewModelRuntime`) and ObjC.
### RiveDataBindingViewModelInstance
Swift: `RiveDataBindingViewModel.Instance`
The bridging type between the c++ runtime equivalent (`rive::ViewModelInstanceRuntime`) and ObjC.
### RiveDataBindingViewModelInstanceProperty
Swift: `RiveDataBindingViewModel.Instance.Property`
The superclass bridging type between the c++ runtime equivalent (`rive::ViewModelInstanceValueRuntime`) and ObjC.
### Subclasses
- Strings: `RiveDataBindingViewModelInstanceStringProperty`
- Swift: `RiveDataBindingViewModel.Instance.StringProperty`
- Numbers: `RiveDataBindingViewModelInstanceNumberProperty`
- Swift: `RiveDataBindingViewModel.Instance.NumberProperty`
- Boolean: `RiveDataBindingViewModelInstanceBooleanProperty`
- Swift: `RiveDataBindingViewModel.Instance.BooleanProperty`
- Color: `RiveDataBindingViewModelInstanceColorProperty`
- Swift: `RiveDataBindingViewModel.Instance.ColorProperty`
- Enum: `RiveDataBindingViewModelInstanceEnumProperty`
- Swift: `RiveDataBindingViewModel.Instance.EnumProperty`
- Trigger: `RiveDataBindingViewModelInstanceTriggerProperty`
- Swift: `RiveDataBindingViewModel.Instance.TriggerProperty`
### Observability
KVO has (temporarily) been disabled, "forcing" observability to be done through the explicit `addListener` and `removeListener` functions of each property type. `removeListener` exists on the superclass, however the matching `addListener` functions exist on each property type, primarily due to the fact that there is no "pretty" way of handing these functions as generics (where only the value type of the property differs for each callback) other than utilizing `id`. Listeners exist within the context of a property; however, an instance will use its (cached) properties to request that it calls its listeners.
## Details
### Caching properties
When a property getter is called on a view model instance, a cache is first checked for the property, otherwise a new one is returned and cached. This helps with ensuring we are using the same pointer under-the-hood. This isn't strictly necessary (per testing) but does allow for some niceties, such as not having to explicitly maintain a strong reference to a property if you want to just observe: `instance.triggerProperty(from: "...").addListener { ... }`.
Properties are cached for the first component when parsing the path for the property. In the instance that a path with > 1 component is provided to a property (e.g `instance.triggerProperty(from: 'nested/trigger")`, then the appropriate nested view models are created, and the property is associated with the correct view model (e.g above, the view model `nested will be cached, and the trigger property will be cached within that view model).
### Caching nested view models
Similar to caching properties, when a (nested) view model getter is called on a view model instance, a cache is first checked for the view model, otherwise a new one is returned and cached. This helps ensure that when (re)binding an instance to a (new) state machine or artboard, that all properties within that view model still have its listeners attached, regardless of how nested a path goes. This will likely help with implementing replacing instance functionality in v2.
## Testing
Unit tests have been added for data binding, attempting to capture high-level expectations rather than totally verifying the c++ runtime expectations. This includes things like: all getters returning object-or-nil, listeners being called with the correct values, autoBinding, property and view model caching, etc. The `.riv` file for unit tests is the same one that is used within the Example app.
## Example
A new `.riv` file has been added that shows basic usage of each property type (including observability). The same `.riv` file is used in the unit tests.
Diffs=
b2f1db73d7 feat(ios): add support for data binding (#9227)
Co-authored-by: David Skuza <david@rive.app>
This commit is contained in:
BIN
Tests/Assets/data_binding_test.riv
Normal file
BIN
Tests/Assets/data_binding_test.riv
Normal file
Binary file not shown.
1021
Tests/DataBindingTests.swift
Normal file
1021
Tests/DataBindingTests.swift
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user