From 6ba92bdc6b0d1f6ce31825d3ad0d1322853b7da5 Mon Sep 17 00:00:00 2001 From: David Skuza Date: Tue, 13 Jan 2026 08:14:13 -0600 Subject: [PATCH 1/5] docs(apple): update apple.mdx to include experimental api --- runtimes/apple/apple.mdx | 354 +++++++++++++++++++++++++++++---------- snippets/constants.mdx | 1 + 2 files changed, 271 insertions(+), 84 deletions(-) create mode 100644 snippets/constants.mdx diff --git a/runtimes/apple/apple.mdx b/runtimes/apple/apple.mdx index c7ec9cbe..bf9e1ac9 100644 --- a/runtimes/apple/apple.mdx +++ b/runtimes/apple/apple.mdx @@ -5,6 +5,7 @@ description: 'Apple runtime for Rive. ' import NoteOnFeatureSupport from "/snippets/runtimes/rendering-feature-support.mdx" import { Demos } from '/snippets/demos.jsx' +import { Apple } from "/snippets/constants.mdx" @@ -16,115 +17,300 @@ This guide documents how to get started using the Apple runtime library. Rive ru This library contains an API for Apple apps to easily integrate their Rive assets for both UIKit/AppKit and SwiftUI. The runtime can also be installed via Cocoapods or Swift Package Manager. -The minimum iOS target is **14.0,** and the target for macOS is `13.1` +The Apple runtime currently supports iOS 14.0+, visionOS 1.0+, tvOS 16.0+, macOS 13.1+, and Mac Catalyst 14.0+ - - **Note:** macOS runtime support is included in `v4.0.1+` - - - - -You can run our Apple example app from the Rive GitHub repository. - -```bash -git clone https://github.com/rive-app/rive-ios -``` - -Open the `Example-iOS` app in XCode and be sure to select the `Preview (iOS)` or `Preview (macOS)` [scheme](https://developer.apple.com/documentation/xcode/customizing-the-build-schemes-for-a-project). The other schemes are for development purposes and require additional configuration, see[ ](https://github.com/rive-app/rive-ios/blob/main/CONTRIBUTING.md)[CONTRIBUTING.MD](https://github.com/rive-app/rive-ios/blob/main/CONTRIBUTING.md). - -![Image](/images/runtimes/apple/f4e4f632-f24d-47ed-b19c-0c961da458e8.webp) - -## Getting Started +## The Two Apple APIs -Follow the steps below for a quick start on integrating Rive into your Apple app. +The Rive Apple runtime provides two main APIs for integrating Rive into your application. - - - #### Via Cocoapods +### The Experimental API - Add the following to your Podspec file: + + The new API is currently experimental and may be subject to breaking changes. + - ```bash - pod 'RiveRuntime' - ``` +This API is designed as a Swift-first API leveraging Swift Concurrency, introducing multi-threading support for Rive, in contrast to the current API which is single-threaded on the main thread. - #### Via Swift Package Manager +The entry point for this API is the `Rive` type, which is a container for the configuration of a Rive view. This includes the file, artboard, state machine, fit, and background color. - To install via Swift Package Manager, in the package finder in Xcode, search for `rive-ios` or the full Github path: `https://github.com/rive-app/rive-ios` - - - Add the following to the top of your file where you utilize the Rive runtime: +Adding a Rive view via the new API is done by first creating a `RiveUIView` with a `Rive` object, and then adding it to the view hierarchy. In SwiftUI, this is as easy as calling `.view()` on a `RiveUIView` instance. - ```bash - import RiveRuntime - ``` - - +It is important to note that while this new API support multi-threading, the calls to the Rive API must still be made on the main thread. This is enforced at compile time by marking functions and types as `@MainActor`. - In Rive Apple runtimes of versions 2.x.x or later, the primary object you'll use is a `RiveViewModel`. It is responsible for creating and interacting with Rive assets. +In addition to supporting multi-threading, this API introduces better error handling by introducing `Error` types for the different Rive primitives, as well as throwing functions. - #### SwiftUI +It is recommended to begin using the new API in new projects and provide feedback, and to investigate migrating existing projects to the new API when feasible. - **Set up RiveViewModel w/ View** +### The Current API - ```javascript - struct AnimationView: View { - var body: some View { - RiveViewModel(fileName: "cool_rive_animation").view() - } - } - ``` +This API is based on the Objective-C runtime and NSObject subclasses, and is the version of the runtime that has been widely deployed in production applications. - In the above example, you reference the name of a `.riv` asset bundled into your application, but you can also load in a `.riv` file hosted on a remote URL like so: +Contrary to the new experimental API, this API is single-threaded on the main thread. - ```javascript - struct AnimationView: View { - var body: some View { - RiveViewModel( - webURL: "https://cdn.rive.app/animations/off_road_car_v7.riv" - ).view() - } - } - ``` +The entry point for this API is the `RiveViewModel` type, which acts as a controller for a Rive view. This object is then responsible for the creation of a `RiveView` which is the view that displays the Rive animation. - #### UIKit - Storyboard +## Getting Started - #### Set up RiveViewModel w/ Controller formatted on a Storyboard +Follow the steps below for a quick start on integrating Rive into your Apple app. - The simplest way of adding Rive to a controller using Storyboards is to make a `RiveViewModel`, and set its view to be the `RiveView` you made in the Storyboard. + + + + + With CocoaPods going into [maintenance mode](https://blog.cocoapods.org/CocoaPods-Support-Plans/), we recommend using Swift Package Manager. + + To install via Xcode, you can follow Apple's instructions for [adding a package dependency to your app](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app), with the Apple runtime's GitHub URL: https://github.com/rive-app/rive-ios. + + Alternatively, you can add the dependency manually by adding the following to your `Package.swift` file: + + ```swift + dependencies: [ + .package(url: "https://github.com/rive-app/rive-ios", from: "6.13.0") + ] + ``` + + Then add the dependency to your target: + + ```swift + targets: [ + .target( + name: "MyApp", + dependencies: [ + .product(name: "RiveRuntime", package: "rive-ios") + ] + ) + ] + ``` + + + The Experimental API types are behind the `RiveExperimental` SPI, so the standard runtime import must be prefixed with `@_spi(RiveExperimental)`. + + ```swift + @_spi(RiveExperimental) import RiveRuntime + ``` + + + A `Worker` is what handles concurrency in the Rive runtime. This type handles starting a background thread for processing, in addition to handline global (out-of-band) assets. + + Each `Worker` spawns one background thread, limited by system availability. + + + A `Worker` is backed by a `DispatchQueue`, which handles the creation and reuse of threads. + + + A `Worker` must be alive for the duration of Rive usage. A `File` creates a strong reference to a `Worker`, so a `Worker` will at least be alive for the duration of use of a `File`, unless a reference to a `Worker` is kept outside of a file. + + To create a worker, add the following: + ```swift + let worker = Worker() + ``` + + If multiple `File` objects share the same worker, they will share the same global assets (e.g out-of-band images, fonts, and audio) and background thread. + + If you are rendering multiple heavy Rive graphics, you can create one `Worker` per file to have each processed on its own background thread. + + + Once you have created a `Worker`, you can move onto creating a `File`. Each `File` object takes a source and a worker. + + + The `File` initializer is marked `@MainActor`. + + + + + ```swift + let worker = Worker() + let file = File(source: .local("my_file", Bundle.main), worker: worker) + ``` + + + ```swift + let worker = Worker() + let file = File(source: .url(URL(string: "https://example.com/my_file.riv")!, worker: worker) + ``` + + + ```swift + let worker = Worker() + let data: Data = ... + let file = File(source: .data(data), worker: worker) + ``` + + + + If you are creating a one-off view, you can create a worker inline: + ```swift + let file = File(source: .local("my_file", Bundle.main), worker: Worker()) + ``` + + + Once you have created a `File`, you can move onto creating a `Rive` object. This object defines the configuration of a view. The most basic implementation is created with just a file; Rive will handle loading the correct artboard and state machine for rendering. + + Once you have created a `Rive` object, you can initialize a `RiveUIView`. This view is used both in UIKit _and_ SwiftUI. Bridging to SwiftUI is as easy as calling `.view()`. + + There is a convenience initializer on `RiveUIView` that runs the creation of a `Rive` object on the main actor, ensuring the correct concurrency requirement is met. + + + ```swift + var body: some View { + RiveUIView({ + let worker = Worker() + let file = File(source: .local("my_file", Bundle.main)) + return Rive(file: file) + }).view() + } + ``` + + + ```swift + let riveView = RiveUIView({ + let worker = Worker() + let file = File(source: .local("my_file", Bundle.main)) + return Rive(file: file) + }).view() + view.addSubview(riveView) + ``` + + + + + For information on the new `Artboard` type and API, see [Artboards](/runtimes/artboards.mdx). + + For information on the new `StateMachine` type and API, see [State Machine Playback](/runtimes/state-machines.mdx). + + For information on the new Data Binding APIs, see [Data Binding](/runtimes/data-binding.mdx). + + For information on caching the new `File` type, see [Caching a Rive File](/runtimes/caching-a-rive-file.mdx). + + + + + + + **Swift Package Manager** + + To install via Swift Package Manager, in the package finder in Xcode, search for `rive-ios` or the full Github path: `https://github.com/rive-app/rive-ios` + + **Cocoapods** + + Add the following to your Podspec file: + + ```bash + pod 'RiveRuntime' + ``` + + + Add the following to the top of your file where you utilize the Rive runtime: + + + The Experimental API requires a slightly different import, as its types are behind `@_spi(RiveExperimental)` + + + + + ```swift + import RiveRuntime + ``` + + + + + + + The primary object you'll use is a `RiveViewModel`. It is responsible for creating and interacting with Rive assets. + + ### SwiftUI + + **Set up a RiveViewModel** + + ```javascript + struct AnimationView: View { + var body: some View { + RiveViewModel(fileName: "cool_rive_animation").view() + } + } + ``` + + In the above example, you reference the name of a `.riv` asset bundled into your application, but you can also load in a `.riv` file hosted on a remote URL like so: + + ```javascript + struct AnimationView: View { + var body: some View { + RiveViewModel( + webURL: "https://cdn.rive.app/animations/off_road_car_v7.riv" + ).view() + } + } + ``` + + ### UIKit + + #### Programmatic + + You can also add Rive to a controller purely with code by making the `RiveViewModel`, telling it to create a fresh `RiveView` and then adding it to the view hierarchy. + + ```javascript + class AnimationViewController: UIViewController { + var simpleVM = RiveViewModel(fileName: "cool_rive_animation") + + override func viewWillAppear(_ animated: Bool) { + let riveView = simpleVM.createRiveView() + view.addSubview(riveView) + riveView.frame = view.bounds + } + } + ``` + + #### Storyboard + + The simplest way of adding Rive to a controller using Storyboards is to make a `RiveViewModel`, and set its view to be the `RiveView` you made in the Storyboard. + + ```javascript + class AnimationViewController: UIViewController { + @IBOutlet weak var riveView: RiveView! + var simpleVM = RiveViewModel(fileName: "cool_rive_animation") + + override public func viewDidLoad() { + simpleVM.setView(riveView) + } + } + ``` + + + + + + + +## Enabling Logging + +The { Apple.legacyRuntimeName } includes logging capabilities to help with debugging. Logging can be enabled using `RiveLogger`: + +Enabling logging is as simple as setting `RiveLogger.isEnabled` to `true`. + +```swift +RiveLogger.isEnabled = true +``` - ```javascript - class AnimationViewController: UIViewController { - @IBOutlet weak var riveView: RiveView! - var simpleVM = RiveViewModel(fileName: "cool_rive_animation") + +The { Apple.currentRuntimeName } does not yet include logging. + - override public func viewDidLoad() { - simpleVM.setView(riveView) - } - } - ``` +For more details on logging levels, categories, and verbose logs, see the [Logging](/runtimes/logging) page. - #### UIKit - Programmatic +See subsequent runtime pages to learn how to control animation playback, state machines, and more. - #### Set up RiveViewModel w/ Controller from scratch in code +## Example App - You can also add Rive to a controller purely with code by making the `RiveViewModel`, telling it to create a fresh `RiveView` and then adding it to the view hierarchy. +You can run our Apple example app from the Rive GitHub repository. - ```javascript - class AnimationViewController: UIViewController { - var simpleVM = RiveViewModel(fileName: "cool_rive_animation") +```bash +git clone https://github.com/rive-app/rive-ios +``` - override func viewWillAppear(_ animated: Bool) { - let riveView = simpleVM.createRiveView() - view.addSubview(riveView) - riveView.frame = view.bounds - } - } - ``` - - +Open the `Example-iOS` app in Xcode and be sure to select the `Preview (iOS)` or `Preview (macOS)` [scheme](https://developer.apple.com/documentation/xcode/customizing-the-build-schemes-for-a-project). The other schemes are for development purposes and require additional configuration, see[ ](https://github.com/rive-app/rive-ios/blob/main/CONTRIBUTING.md)[CONTRIBUTING.MD](https://github.com/rive-app/rive-ios/blob/main/CONTRIBUTING.md). -See subsequent runtime pages to learn how to control animation playback, state machines, and more. +![Image](/images/runtimes/apple/f4e4f632-f24d-47ed-b19c-0c961da458e8.webp) ## Resources diff --git a/snippets/constants.mdx b/snippets/constants.mdx new file mode 100644 index 00000000..9ec4168c --- /dev/null +++ b/snippets/constants.mdx @@ -0,0 +1 @@ +export const Apple = { legacyRuntimeName: "Legacy Runtime (Current)", currentRuntimeName: "New Runtime (Experimental)" }; \ No newline at end of file From 61747accbf96f7bfd7a68346938508cdd1f3486e Mon Sep 17 00:00:00 2001 From: David Skuza Date: Wed, 14 Jan 2026 13:12:10 -0600 Subject: [PATCH 2/5] docs(apple): update artboards.mdx to include experimental api --- runtimes/apple/apple.mdx | 4 +- runtimes/artboards.mdx | 84 ++++++++++++++++++++++++++++------------ 2 files changed, 62 insertions(+), 26 deletions(-) diff --git a/runtimes/apple/apple.mdx b/runtimes/apple/apple.mdx index bf9e1ac9..e9560f21 100644 --- a/runtimes/apple/apple.mdx +++ b/runtimes/apple/apple.mdx @@ -156,7 +156,7 @@ Follow the steps below for a quick start on integrating Rive into your Apple app RiveUIView({ let worker = Worker() let file = File(source: .local("my_file", Bundle.main)) - return Rive(file: file) + return try await Rive(file: file) }).view() } ``` @@ -166,7 +166,7 @@ Follow the steps below for a quick start on integrating Rive into your Apple app let riveView = RiveUIView({ let worker = Worker() let file = File(source: .local("my_file", Bundle.main)) - return Rive(file: file) + return try await Rive(file: file) }).view() view.addSubview(riveView) ``` diff --git a/runtimes/artboards.mdx b/runtimes/artboards.mdx index 290ea598..21e815d9 100644 --- a/runtimes/artboards.mdx +++ b/runtimes/artboards.mdx @@ -3,6 +3,8 @@ title: 'Artboards' description: 'Selecting which artboard to render at runtime' --- +import { Apple } from "/snippets/constants.mdx" + For more information on creating artboards in the Rive editor, please refer to [Artboards](/editor/fundamentals/artboards). ## Choosing an Artboard @@ -95,33 +97,67 @@ Only one artboard can be rendered at a time. - #### SwiftUI - ```swift - struct AnimationView: View { - var body: some View { - RiveViewModel( - fileName: "my_rive_file", - artboardName: "My Artboard" - ).view() - } - } - ``` + + + The following section assumes that you have read through the [Apple](/runtimes/apple/apple.mdx) overview. + + ### Getting an Artboard - #### UIKit - ```swift - class AnimationViewController: UIViewController { - @IBOutlet weak var riveView: RiveView! + Once you have created a `File`, you can then retrieve information for and create `Artboard` types. - var bananaVM = RiveViewModel( - fileName: "my_rive_file", - artboardName: "My Artboard", - ) + ```swift + // Get all artboard names + let artboardNames = try await file.getArtboardNames() + let defaultArtboard = try await file.createArtboard() + let artboardByName = try await file.createArtboard("Artboard") + ``` - override func viewDidLoad() { - bananaVM.setView(riveView) - } - } - ``` + Note that these are all async throwing functions marked as `@MainActor`. Since they are functions called on a `File` object, any thrown errors will be of type `FileError`. + + An example of when one of these functions will throw is if you call `.createArtboard(_:)` with a name that is not in the origin `File`, which will throw a `FileError.invalidArtboard(String)`. + + ### Using an Artboard + + Remember that the Rive configuration for a view is the `Rive` type. In the overview, we show initializing a `Rive` object with just a file. However, you can initialize a `Rive` object with a specific artboard: + ```swift + let worker = Worker() + let file = try await File(source: .local("my_file", Bundle.main)) + let artboardByName = try await file.createArtboard("Artboard") + let rive = try await Rive(file: file, artboard: artboardByName) + ``` + + Artboards then become the source of truth for state machines. See [State Machine](/runtimes/state-machines.mdx) for more details. + + + **SwiftUI** + ```swift + struct AnimationView: View { + var body: some View { + RiveViewModel( + fileName: "my_rive_file", + artboardName: "My Artboard" + ).view() + } + } + ``` + + **UIKit** + ```swift + class AnimationViewController: UIViewController { + @IBOutlet weak var riveView: RiveView! + + var bananaVM = RiveViewModel( + fileName: "my_rive_file", + artboardName: "My Artboard", + ) + + override func viewDidLoad() { + bananaVM.setView(riveView) + } + } + ``` + + From d7a764c541ec04506bfcd9bae0e26a4360ce6f93 Mon Sep 17 00:00:00 2001 From: David Skuza Date: Wed, 14 Jan 2026 14:05:19 -0600 Subject: [PATCH 3/5] docs(apple): update state-machines.mdx to include experimental api --- runtimes/artboards.mdx | 4 +- runtimes/state-machines.mdx | 126 +++++++++++++++++++++++++----------- 2 files changed, 90 insertions(+), 40 deletions(-) diff --git a/runtimes/artboards.mdx b/runtimes/artboards.mdx index 21e815d9..ac3a7fbc 100644 --- a/runtimes/artboards.mdx +++ b/runtimes/artboards.mdx @@ -106,9 +106,11 @@ Only one artboard can be rendered at a time. Once you have created a `File`, you can then retrieve information for and create `Artboard` types. ```swift - // Get all artboard names + // Get all artboard names in the file let artboardNames = try await file.getArtboardNames() + // Get the default artboard for the file let defaultArtboard = try await file.createArtboard() + // Get an artboard by name from the file let artboardByName = try await file.createArtboard("Artboard") ``` diff --git a/runtimes/state-machines.mdx b/runtimes/state-machines.mdx index 358e6ab5..93431db4 100644 --- a/runtimes/state-machines.mdx +++ b/runtimes/state-machines.mdx @@ -3,6 +3,8 @@ title: "State Machine Playback" description: "Playing a state machine" --- +import { Apple } from "/snippets/constants.mdx" + ## Overview For more information on designing and building state machines in the Rive editor, please refer to [State Machine Overview](/editor/state-machine). @@ -334,59 +336,105 @@ In addition to the paused/stopped state, state machines may also "settle". This - #### Autoplay the State Machine + + + By default, the state machine of a `Rive` object will automatically play when in use by a `RiveUIView`. - By default, RiveViewModel will automatically play the given state machine. + + In the { Apple.currentRuntimeName }, there is currently no way of manually playing and pausing a state machine. - ### SwiftUI + Additionally, the framerate is set to the framerate of the device. - ```swift - var stateChanger = RiveViewModel( - fileName: "skills", - stateMachineName: "Designer's Test", - artboardName: "Banana" - ) - ``` + APIs for manually playing and pausing the state machine and setting a custom frame rate will come in the future. + - ### UIKit + The following sections assumes that you have read through the [Getting an Artboard](http://localhost:3000/runtimes/artboards#getting-an-artboard) overview. - ```swift - class StateMachineViewController: UIViewController { - var viewModel = RiveViewModel( - fileName: "skills", - stateMachineName: "Designer's Test", - artboardName: "Banana" - ) + ### Getting a State Machine - override public func loadView() { - super.loadView() + Once you have created a `File`, you can then retrieve information for and create `Artboard` types. - guard let stateMachineView = view as? StateMachineView else { - fatalError("Could not find StateMachineView") - } + ```swift + // Get all state machine names + let stateMachineNames = try await artboard.getStateMachineNames() + // Get the default state machine for the artboard + let defaultStateMachine = try await artboard.createStateMachine() + // Get a state machine by name from the artboard + let stateMachineByName = try await artboard.createStateMachine("StateMachine") + ``` - viewModel.setView(stateMachineView.riveView) - } - } - ``` + Note that these are all async throwing functions marked as `@MainActor`. Since they are functions called on an `Artboard` object, any thrown errors will be of type `ArtboardError`. - ### Play + An example of when one of these functions will throw is if you call `.createStateMachine(_:)` with a name that is not in the origin `Artboard`, which will throw a `ArtboardError.invalidStateMachine(String)`. - If you set autoplay to false you can simply play the active animation or state machine. + ### Using a State Machine - ```swift - simpleVM.play() - ``` + Remember that the Rive configuration for a view is the `Rive` type. In the overview, we show initializing a `Rive` object with just a file. However, you can initialize a `Rive` object with a specific state machine: + + ```swift + let worker = Worker() + let file = try await File(source: .local("my_file", Bundle.main)) + let artboardByName = try await file.createArtboard("Artboard") + let stateMachine = try await artboardByName.createStateMachine("StateMachine") + let rive = try await Rive(file: file, artboard: artboardByName, stateMachine: stateMachine) + ``` + + + #### Autoplay the State Machine - ### Pause/Stop/Reset + By default, RiveViewModel will automatically play the given state machine. - Based on certain events in your app you may want to adjust the playback further. + ### SwiftUI - ```swift - simpleVM.pause() - simpleVM.stop() - simpleVM.reset() - ``` + ```swift + var stateChanger = RiveViewModel( + fileName: "skills", + stateMachineName: "Designer's Test", + artboardName: "Banana" + ) + ``` + + ### UIKit + + ```swift + class StateMachineViewController: UIViewController { + var viewModel = RiveViewModel( + fileName: "skills", + stateMachineName: "Designer's Test", + artboardName: "Banana" + ) + + override public func loadView() { + super.loadView() + + guard let stateMachineView = view as? StateMachineView else { + fatalError("Could not find StateMachineView") + } + + viewModel.setView(stateMachineView.riveView) + } + } + ``` + + ### Play + + If you set autoplay to false you can simply play the active animation or state machine. + + ```swift + simpleVM.play() + ``` + + ### Pause/Stop/Reset + + Based on certain events in your app you may want to adjust the playback further. + + ```swift + simpleVM.pause() + simpleVM.stop() + simpleVM.reset() + ``` + + From 6215a74ace1e6973b640e2bf0bbb840f0e559744 Mon Sep 17 00:00:00 2001 From: David Skuza Date: Wed, 14 Jan 2026 16:51:14 -0600 Subject: [PATCH 4/5] docs(apple): update data-binding.mdx to include experimental api --- runtimes/data-binding.mdx | 670 ++++++++++++++++++++++++++------------ 1 file changed, 461 insertions(+), 209 deletions(-) diff --git a/runtimes/data-binding.mdx b/runtimes/data-binding.mdx index be232172..6a41122e 100644 --- a/runtimes/data-binding.mdx +++ b/runtimes/data-binding.mdx @@ -5,6 +5,7 @@ description: "Connect your code to bound editor elements using View Models" import { YouTube } from "/snippets/youtube.mdx"; import { Demos } from "/snippets/demos.jsx"; +import { Apple } from "/snippets/constants.mdx" # Overview @@ -105,22 +106,38 @@ To begin, we need to get a reference to a particular view model. This can be don ``` - ```swift - let riveViewModel = RiveViewModel(...) - let file = riveViewModel.riveModel!.riveFile + + + View models are not their own type; rather, they are a source to create a view model instance from. - // Data binding view model by name - let viewModelByName = file.viewModelNamed("...") + There are currently two options: - // Data binding view model by index - for index in 0.. + + ```swift + let riveViewModel = RiveViewModel(...) + let file = riveViewModel.riveModel!.riveFile + + // Data binding view model by name + let viewModelByName = file.viewModelNamed("...") + + // Data binding view model by index + for index in 0.. + @@ -343,26 +360,55 @@ Once we have a reference to a view model, it can be used to create an instance. - ```swift - let riveViewModel = RiveViewModel(...) - let viewModel = riveViewModel.riveModel!.riveFile.viewModelNamed("...")! + + + The following section assumes that you have read through the [Apple](/runtimes/apple/apple.mdx) overview. + + ```swift + // From a file + let file: File = ... + + // When using a view model by name: + // A blank view model instance + var blankInstance = try await file.createViewModelInstance(from: .blank(from: .name("ViewModel"))) + // The default instance for the view model + var defaultInstance = try await file.createViewModelInstance(from: .name("ViewModel")) + // An instance by name from the view model + var namedInstance = try await file.createViewModelInstance(from: .name("Instance", from: .name("ViewModel"))) + + // Alternatively, using the default view model for an artboard + let artboard: Artboard = ... + // A blank view model instance + blankInstance = try await file.createViewModelInstance(from: .blank(from: .artboardDefault(Artboard))) + // The default instance for the view model + defaultInstance = try await file.createViewModelInstance(from: .viewModelDefault(from: .artboardDefault(Artboard))) + // An instance by name from the view model + namedInstance = try await file.createViewModelInstance(from: .name("Instance", from: .artboardDefault(Artboard))) + ``` + + + ```swift + let riveViewModel = RiveViewModel(...) + let viewModel = riveViewModel.riveModel!.riveFile.viewModelNamed("...")! - // Create blank - let blankInstance = viewModel.createInstance() + // Create blank + let blankInstance = viewModel.createInstance() - // Create default - let defaultInstance = viewModel.createDefaultInstance() + // Create default + let defaultInstance = viewModel.createDefaultInstance() - // Create by index - for index in 0.. + @@ -631,18 +677,46 @@ It is preferred to assign to a state machine, as this will automatically apply t - ```swift - let riveViewModel = RiveViewModel(...) - let artboard = riveViewModel.riveModel!.artboard, - let instance = riveViewModel.riveModel!.riveFile.defaultViewModel(for: artboard).createDefaultInstance()! - - // Apply the instance to the state machine (preferred) - // Applying to a state machine will automatically bind to its artboard - riveViewModel.riveModel!.stateMachine.bind(instance) + + + Given the following example code: + ```swift + let file: File = ... + let artboard: Artboard = try await file.createArtboard() + let stateMachine: StateMachine = try await artboard.createStateMachine() + let viewModelInstance = try await file.createViewModelInstance(...) + ``` + + You can manually bind the view model instance to the state machine: + ```swift + stateMachine.bindViewModelInstance(viewModelInstance) + ``` + + Alternatively, you can utilize the `Rive` type to (automatically) data bind a view model instance: + ```swift + // Automatically find a default view model instance to bind. This is the default value, if you do not pass in a dataBind argument. + var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .auto) + // Bind a view model instance + var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .viewModelInstance(viewModelInstance)) + // Do not bind. This assumes you have manually bound a view model instance earlier + var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .none) + ``` + + + ```swift + let riveViewModel = RiveViewModel(...) + let artboard = riveViewModel.riveModel!.artboard, + let instance = riveViewModel.riveModel!.riveFile.defaultViewModel(for: artboard).createDefaultInstance()! + + // Apply the instance to the state machine (preferred) + // Applying to a state machine will automatically bind to its artboard + riveViewModel.riveModel!.stateMachine.bind(instance) - // Alternatively, apply the instance to the artboard - artboard.bind(viewModelInstance: instance) - ``` + // Alternatively, apply the instance to the artboard + artboard.bind(viewModelInstance: instance) + ``` + + @@ -793,16 +867,37 @@ Alternatively, you may prefer to use auto-binding. This will automatically bind ``` - ```swift - let riveViewModel = RiveViewModel(...) - riveViewModel.riveModel?.enableAutoBind { instance in - // Store a reference to `instance` to later access properties - // The instance may change as state machines and artboards change - } + + + Given the following example code: + ```swift + let file: File = ... + let artboard: Artboard = try await file.createArtboard() + let stateMachine: StateMachine = try await artboard.createStateMachine() + let viewModelInstance = try await file.createViewModelInstance(...) + ``` + + When creating a `Rive` object, you can elect to auto bind: + ```swift + // Automatically find a default view model instance to bind. This is the default value, if you do not pass in a dataBind argument. + var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine, dataBind: .auto) + // or + var rive = try await Rive(file: file, artboard: artboard, stateMachine: stateMachine) + ``` + + + ```swift + let riveViewModel = RiveViewModel(...) + riveViewModel.riveModel?.enableAutoBind { instance in + // Store a reference to `instance` to later access properties + // The instance may change as state machines and artboards change + } - // If you'd like to disable autoBind after enabling… - riveViewModel.riveModel!.disableAutoBind() - ``` + // If you'd like to disable autoBind after enabling… + riveViewModel.riveModel!.disableAutoBind() + ``` + + @@ -965,14 +1060,29 @@ Property descriptors can be inspected on a view model to discover at runtime whi - ```swift - let riveViewModel = RiveViewModel(...) - let viewModel = riveViewModel.riveModel!.file.viewModelNamed(...)! - for property in viewModel.properties { - print(property.type) // String, number, boolean, etc - print(property.name) // The name of the property within the view model - } - ``` + + + ```swift + let file: File = ... + let properties = try await file.getProperties(of: "ViewModel") + for property in properties { + print(property.type) // enum of string, number, boolean, etc + print(property.name) // The name of the property within the view model + print(property.metaData) // Additional metadata for the property, if available + } + ``` + + + ```swift + let riveViewModel = RiveViewModel(...) + let viewModel = riveViewModel.riveModel!.file.viewModelNamed(...)! + for property in viewModel.properties { + print(property.type) // String, number, boolean, etc + print(property.name) // The name of the property within the view model + } + ``` + + @@ -1172,77 +1282,120 @@ Some properties are mutable and have getters, setters, and observer operations f - ```swift - let riveViewModel = RiveViewModel(...) - - var viewModelInstance: RiveDataBindingViewModel.Instance! - - // You can get the view model instance when enabling auto binding - riveViewModel.riveModel?.enableAutoBind { instance in - // Store a reference to instance - viewModelInstance = instance - } - - // Alternatively, you can create a view model instance manually - viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()! - - // Strings - let stringProperty = instance.stringProperty(fromPath: "...")! - // Updating its value - stringProperty.value = "Hello, Rive" - // Get its value - print(stringProperty.value) - - // You can also set and get values without storing a strong reference - instance.stringProperty(fromPath: "...").value = "Hello again, Rive" - - // Numbers - let numberProperty = instance.numberProperty(fromPath: "...")! - // Updating its value - numberProperty.value = 1337 - // Get its value - print(numberProperty.value) + + + Contrary to the {Apple.legacyRuntimeName} API, properties are not reference types; instead, they are a very thin wrapper around the path and return type of a property. + + Setters, getters, and triggers for types are now a part of `ViewModelInstance` objects. + + ```swift + let file: File = ... + let viewModelInstance = try await file.createViweModelInstance(...) + + // String + let stringProperty = StringProperty(path: "path/to/string") + let stringValue = try await viewModelInstance.value(of: stringProperty) + viewModelInstance.setValue(of: stringProperty, to: "value") + + // Number + let numberProperty = NumberProperty(path: "path/to/number") + let numberValue = try await viewModelInstance.value(of: numberProperty) + viewModelInstance.setValue(of: numberValue, to: 9001) + + // Bool + let boolProperty = BoolProperty(path: "path/to/bool") + let boolValue = try await viewModelInstance.value(of: boolProperty) + viewModelInstance.setValue(of: boolProperty, to: true) + + // Color + let colorProperty = ColorProperty(path: "path/to/color") + let colorValue = try await viewModelInstance.value(of: colorProperty) + viewModelInstance.setValue(of: colorProperty, to: Color(red: 255, green: 255, blue: 255, alpha: 255)) + + // Enum + let enumProperty = EnumProperty(path: "path/to/enum") + let enumValue = try await viewModelInstance.value(of: enumProperty) + viewModelInstance.setValue(of: enumProperty, to: "value") + + // Trigger + let triggerProperty = TriggerProperty(path: "path/to/trigger") + viewModelInstance.fire(trigger: triggerProperty) + ``` + + + ```swift + let riveViewModel = RiveViewModel(...) + + var viewModelInstance: RiveDataBindingViewModel.Instance! + + // You can get the view model instance when enabling auto binding + riveViewModel.riveModel?.enableAutoBind { instance in + // Store a reference to instance + viewModelInstance = instance + } - // You can also set and get values without storing a strong reference - instance.numberProperty(fromPath: "...").value = 1337 + // Alternatively, you can create a view model instance manually + viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()! - // Booleans - let booleanProperty = instance.booleanProperty(fromPath: "...")! - // Updating its value - booleanProperty.value = true - // Get its value - print(booleanProperty.value) + // Strings + let stringProperty = instance.stringProperty(fromPath: "...")! + // Updating its value + stringProperty.value = "Hello, Rive" + // Get its value + print(stringProperty.value) - // You can also set and get values without storing a strong reference - instance.booleanProperty(fromPath: "...").value = true + // You can also set and get values without storing a strong reference + instance.stringProperty(fromPath: "...").value = "Hello again, Rive" - // Colors - let colorProperty = instance.colorProperty(fromPath: "...")! - // Updating its value, which is a UIColor/NSColor, so all static helpers apply. - colorProperty.value = .red - // Get its value - print(colorProperty.value) + // Numbers + let numberProperty = instance.numberProperty(fromPath: "...")! + // Updating its value + numberProperty.value = 1337 + // Get its value + print(numberProperty.value) - // You can also set and get values without storing a strong reference - instance.colorProperty(fromPath: "...").value = .red + // You can also set and get values without storing a strong reference + instance.numberProperty(fromPath: "...").value = 1337 - // Enums - let enumProperty = instance.enumProperty(fromPath: "...")! - // Updating its value - enumProperty.value = "Foo" - // Get its value - print(enumProperty.value) - // Print all possible values - print(enumProperty.values) + // Booleans + let booleanProperty = instance.booleanProperty(fromPath: "...")! + // Updating its value + booleanProperty.value = true + // Get its value + print(booleanProperty.value) - // You can also set and get values without storing a strong reference - instance.enumProperty(fromPath: "...").value = "Foo" + // You can also set and get values without storing a strong reference + instance.booleanProperty(fromPath: "...").value = true - // Trigger - let triggerProperty = instance.triggerProperty(fromPath: "...")! - // Fire the trigger - triggerProperty.trigger() - ``` + // Colors + let colorProperty = instance.colorProperty(fromPath: "...")! + // Updating its value, which is a UIColor/NSColor, so all static helpers apply. + colorProperty.value = .red + // Get its value + print(colorProperty.value) + + // You can also set and get values without storing a strong reference + instance.colorProperty(fromPath: "...").value = .red + + // Enums + let enumProperty = instance.enumProperty(fromPath: "...")! + // Updating its value + enumProperty.value = "Foo" + // Get its value + print(enumProperty.value) + // Print all possible values + print(enumProperty.values) + + // You can also set and get values without storing a strong reference + instance.enumProperty(fromPath: "...").value = "Foo" + + // Trigger + let triggerProperty = instance.triggerProperty(fromPath: "...")! + // Fire the trigger + triggerProperty.trigger() + ``` + + @@ -1568,27 +1721,36 @@ View models can have properties of type view model, allowing for arbitrary nesti - ```swift - let riveViewModel = RiveViewModel(...) + + + Property types are no longer reference types, and require the name or full path to a property when initializing the property value type. There is no longer an API to chain nested properties. - var viewModelInstance: RiveDataBindingViewModel.Instance! + See [Properties](#properties) for usage details. + + + ```swift + let riveViewModel = RiveViewModel(...) - // You can get the view model instance when enabling auto binding - riveViewModel.riveModel?.enableAutoBind { instance in - // Store a reference to instance - viewModelInstance = instance - } + var viewModelInstance: RiveDataBindingViewModel.Instance! - // Alternatively, you can create a view model instance manually - viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()! + // You can get the view model instance when enabling auto binding + riveViewModel.riveModel?.enableAutoBind { instance in + // Store a reference to instance + viewModelInstance = instance + } - let nestedNumberByChain = instance - .viewModelInstanceProperty(fromPath: "Nested View Model") - .viewModelInstanceProperty(fromPath: "Another Nested View Model") - .numberProperty(fromPath: "Number") + // Alternatively, you can create a view model instance manually + viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()! - let nestedNumberByPath = instance.numberProperty(fromPath: "Nested View Model/Another Nested View Model/Number") - ``` + let nestedNumberByChain = instance + .viewModelInstanceProperty(fromPath: "Nested View Model") + .viewModelInstanceProperty(fromPath: "Another Nested View Model") + .numberProperty(fromPath: "Number") + + let nestedNumberByPath = instance.numberProperty(fromPath: "Nested View Model/Another Nested View Model/Number") + ``` + + @@ -1754,36 +1916,60 @@ You can observe changes over time to property values, either by using listeners - ```swift - let riveViewModel = RiveViewModel(...) + + + Property listeners now utilize Swift Concurrency's async throwing streams. + + ```swift + let file: File = ... + let viewModelInstance = try await file.createViewModelInstance(...) + let stringProperty = StringProperty(path: "path/to/string") + let valueStream = viewModelInstance.valueStream(of: stringProperty) + do { + for try await value in valueStream { + print(value) + } + } catch let error as ViewModelInstanceError { + // The thrown error should always be a ViewModelInstanceError type + print(error) + } catch { + print(error) + } + ``` + + + ```swift + let riveViewModel = RiveViewModel(...) - var viewModelInstance: RiveDataBindingViewModel.Instance! + var viewModelInstance: RiveDataBindingViewModel.Instance! - // You can get the view model instance when enabling auto binding - riveViewModel.riveModel?.enableAutoBind { instance in - // Store a reference to instance - viewModelInstance = instance - } + // You can get the view model instance when enabling auto binding + riveViewModel.riveModel?.enableAutoBind { instance in + // Store a reference to instance + viewModelInstance = instance + } - // Alternatively, you can create a view model instance manually - viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()! + // Alternatively, you can create a view model instance manually + viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()! - // Get the string property - let stringProperty = instance.stringProperty(fromPath: "...")! + // Get the string property + let stringProperty = instance.stringProperty(fromPath: "...")! - // Add a listener - let listener = stringProperty.addListener { newValue in - print(newValue) - } + // Add a listener + let listener = stringProperty.addListener { newValue in + print(newValue) + } - // Remove a listener, where listener is the return value of addListener - stringProperty.removeListener(listener) + // Remove a listener, where listener is the return value of addListener + stringProperty.removeListener(listener) - // Trigger properties can also be listened to for when they are triggered - instance.triggerProperty(fromPath: "...")!.addListener { - print("Triggered!") - } - ``` + // Trigger properties can also be listened to for when they are triggered + instance.triggerProperty(fromPath: "...")!.addListener { + print("Triggered!") + } + ``` + + @@ -2104,35 +2290,52 @@ Image properties let you set and replace raster images at runtime, with each ins - ```swift - let riveViewModel = RiveViewModel(...) - - var viewModelInstance: RiveDataBindingViewModel.Instance! - - // You can get the view model instance when enabling auto binding - riveViewModel.riveModel?.enableAutoBind { instance in - // Store a reference to instance - viewModelInstance = instance - } + + + To set an image, you first need to decode an image from a `Worker`. This has to be the `Worker` that was used when initializing a `File`, from which you are setting the image property of a view model instance. + + ```swift + let worker = Worker() + let file = try await File(source: ..., worker: worker) + let viewModelInstance = file.createViewModelInstance(...) + let imageProperty = ImageProperty(path: "path/to/image") + let imageData: Data = ... + let image = try await decodeImage(from: imageData) + viewModelInstance.setValue(of: imageProperty, to: image) + ``` + + + ```swift + let riveViewModel = RiveViewModel(...) + + var viewModelInstance: RiveDataBindingViewModel.Instance! + + // You can get the view model instance when enabling auto binding + riveViewModel.riveModel?.enableAutoBind { instance in + // Store a reference to instance + viewModelInstance = instance + } - // Alternatively, you can create a view model instance manually - viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()! + // Alternatively, you can create a view model instance manually + viewModelInstance = riveViewModel.riveModel!.riveFile.viewModelNamed("...")!.createDefaultInstance()! - // Create a RiveRenderImage from data - let data = Data(...) - var image = RiveRenderImage(data: data)! // This can return nil if the data is not a valid image + // Create a RiveRenderImage from data + let data = Data(...) + var image = RiveRenderImage(data: data)! // This can return nil if the data is not a valid image - // Or, create a RiveRenderImage from a UIImage - image = RiveRenderImage(image: UIImage(named: "my_image")!, format: .png)! // This can return nil if the image is not a valid jpg or png image + // Or, create a RiveRenderImage from a UIImage + image = RiveRenderImage(image: UIImage(named: "my_image")!, format: .png)! // This can return nil if the image is not a valid jpg or png image - let imageProperty = viewModelInstance.imageProperty(fromPath: "image")! + let imageProperty = viewModelInstance.imageProperty(fromPath: "image")! - // Once you have your data binding view model instance, you can set the image property value - imageProperty.setValue(image) + // Once you have your data binding view model instance, you can set the image property value + imageProperty.setValue(image) - // You can also pass nil to clear the image - imageProperty.setValue(nil) - ``` + // You can also pass nil to clear the image + imageProperty.setValue(nil) + ``` + + @@ -2471,27 +2674,63 @@ For more information on list properties, see the [Data Binding List Property](/e - ```swift - let listProperty = viewModelInstance.listProperty(fromPath: "list")! + + + ```swift + let file: File = ... + let viewModelInstance = try await file.createViewModelInstance(...) + let listProperty = ListProperty(path: "path/to/list") - // Create a new view model instance and add it to the end of the list - let firstInstance = viewModel.createInstanceByName("First Instance")! - listProperty.add(firstInstance) + let size = try await viewModelInstance.size(of: listProperty) - // Create a new view model instance and add it to the beginning of the list - let secondInstance = myViewModel.createInstanceByName("Second Instance")! - listProperty.add(secondInstance, atIndex: 0) + // Result: [newInstance] + let newInstance = try await file.createViewModelInstance(...) + viewModelInstance.appendInstance(newInstance, to: listProperty) - // Swap the first and second instances - listProperty.swapInstance(atIndex: 0, withInstanceAtIndex: 1) + // Result: [insertedInstance, newInstance] + let insertedInstance = try await file.createViewModelInstance(...) + viewModelInstance.insertInstance(instance, to: listProperty, at: 0) - // Remove both instances - listProperty.removeInstance(secondInstance) - listProperty.removeInstance(atIndex: 0) + // Result: [newInstance, insertedInstance] + viewModelInstance.swapInstance(atIndex: 0, withIndex: 1, in: listProperty) - // Get and print the size of the list - print(listProperty.size) // Prints 0 - ``` + // Result: [newInstance] + viewModelInstance.removeInstance(at: 1, from: listProperty) + + // Result: newInstance + let _ = viewModelInstance.value(of: listProperty, at: 0) + + // Result: [] + viewModelInstance.removeInstance(newInstance, from: listProperty) + + // Result: 0 + let size = try await viewModelInstance.size(of: listProperty) + ``` + + + ```swift + let listProperty = viewModelInstance.listProperty(fromPath: "list")! + + // Create a new view model instance and add it to the end of the list + let firstInstance = viewModel.createInstanceByName("First Instance")! + listProperty.add(firstInstance) + + // Create a new view model instance and add it to the beginning of the list + let secondInstance = myViewModel.createInstanceByName("Second Instance")! + listProperty.add(secondInstance, atIndex: 0) + + // Swap the first and second instances + listProperty.swapInstance(atIndex: 0, withInstanceAtIndex: 1) + + // Remove both instances + listProperty.removeInstance(secondInstance) + listProperty.removeInstance(atIndex: 0) + + // Get and print the size of the list + print(listProperty.size) // Prints 0 + ``` + + @@ -2878,23 +3117,36 @@ Artboard properties allows you to swap out entire components at runtime. This is - Use the `artboardProperty` method on a `RiveDataBindingViewModel.Instance` object to get the artboard property. + + + ```swift + let file: File = ... + let viewModelInstance = try await file.createViewModelInstance(...) + let artboardProperty = ArtboardProperty(path: "path/to/artboard") + let artboard = try await file.createArtboard(...) + viewModelInstance.setValue(of: artboardProperty, to: artboard) + ``` + + + Use the `artboardProperty` method on a `RiveDataBindingViewModel.Instance` object to get the artboard property. - Then use the `setValue` method on the artboard property object to set the new artboard value. + Then use the `setValue` method on the artboard property object to set the new artboard value. - `setValue` accepts a `RiveBindableArtboard` object, which is a wrapper for an artboard that can be used to set the artboard property value. + `setValue` accepts a `RiveBindableArtboard` object, which is a wrapper for an artboard that can be used to set the artboard property value. - You can get a `RiveBindableArtboard` object by using the `bindableArtboard` methods on a `RiveFile` object. + You can get a `RiveBindableArtboard` object by using the `bindableArtboard` methods on a `RiveFile` object. - ```swift - let artboardProperty = instance.artboardProperty(fromPath: "Artboard")! + ```swift + let artboardProperty = instance.artboardProperty(fromPath: "Artboard")! - let components = RiveFile(...) - let bindableArtboard = components.bindableArtboard(at: 0)! - let bindableArtboard2 = components.bindableArtboard(withName: "...")! + let components = RiveFile(...) + let bindableArtboard = components.bindableArtboard(at: 0)! + let bindableArtboard2 = components.bindableArtboard(withName: "...")! - artboardProperty.setValue(bindableArtboard) - ``` + artboardProperty.setValue(bindableArtboard) + ``` + + From 4ecdb4a02fd8ea94360901e2c0d71bde0d2eb7c5 Mon Sep 17 00:00:00 2001 From: David Skuza Date: Wed, 14 Jan 2026 19:12:14 -0600 Subject: [PATCH 5/5] refactor(docs): remove snippet --- runtimes/apple/apple.mdx | 2 -- 1 file changed, 2 deletions(-) diff --git a/runtimes/apple/apple.mdx b/runtimes/apple/apple.mdx index e9560f21..9dd435a9 100644 --- a/runtimes/apple/apple.mdx +++ b/runtimes/apple/apple.mdx @@ -179,8 +179,6 @@ Follow the steps below for a quick start on integrating Rive into your Apple app For information on the new `StateMachine` type and API, see [State Machine Playback](/runtimes/state-machines.mdx). For information on the new Data Binding APIs, see [Data Binding](/runtimes/data-binding.mdx). - - For information on caching the new `File` type, see [Caching a Rive File](/runtimes/caching-a-rive-file.mdx).