From 5402eff2614290faa5f227d8bebdbdca028deb2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Thu, 19 Feb 2026 09:14:18 +0100 Subject: [PATCH] feat: add name() to experimental ViewModelInstance Add async name() method to retrieve the VMI name from the C++ runtime via the command queue. This works for all creation paths including named, default, blank, nested, and list item instances. --- .../CommandQueue/RiveCommandQueue.h | 9 +++++ .../CommandQueue/RiveCommandQueue.mm | 30 ++++++++++++++++ .../RiveViewModelInstanceListener.h | 4 +++ .../DataBinding/ViewModelInstance.swift | 18 +++++++++- .../ViewModelInstanceService.swift | 34 +++++++++++++++++++ 5 files changed, 94 insertions(+), 1 deletion(-) diff --git a/Source/Experimental/CommandQueue/RiveCommandQueue.h b/Source/Experimental/CommandQueue/RiveCommandQueue.h index 27ac7c81..d97afcb1 100644 --- a/Source/Experimental/CommandQueue/RiveCommandQueue.h +++ b/Source/Experimental/CommandQueue/RiveCommandQueue.h @@ -636,6 +636,15 @@ NS_SWIFT_NAME(CommandQueueProtocol) path:(NSString*)path requestID:(uint64_t)requestID; +/** + * Requests the name of a view model instance. + * + * @param viewModelInstanceHandle The handle of the view model instance + * @param requestID The request ID for correlating the response + */ +- (void)requestViewModelInstanceName:(uint64_t)viewModelInstanceHandle + requestID:(uint64_t)requestID; + /** * Sets the string value of a view model property. * diff --git a/Source/Experimental/CommandQueue/RiveCommandQueue.mm b/Source/Experimental/CommandQueue/RiveCommandQueue.mm index 4eb85cee..5227596c 100644 --- a/Source/Experimental/CommandQueue/RiveCommandQueue.mm +++ b/Source/Experimental/CommandQueue/RiveCommandQueue.mm @@ -681,6 +681,11 @@ virtual void onViewModelListSizeReceived( std::string path, size_t size) override; + virtual void onViewModelInstanceNameReceived( + const rive::ViewModelInstanceHandle handle, + uint64_t requestId, + std::string name) override; + private: __weak id _observer; }; @@ -730,6 +735,21 @@ virtual void onViewModelListSizeReceived( } } +void _ViewModelInstanceListener::onViewModelInstanceNameReceived( + const rive::ViewModelInstanceHandle handle, + uint64_t requestId, + std::string name) +{ + if (_observer) + { + NSString* nsName = [NSString stringWithUTF8String:name.c_str()]; + [_observer + onViewModelInstanceNameReceived:reinterpret_cast(handle) + requestID:requestId + name:nsName]; + } +} + namespace { class _RenderImageListener : public rive::CommandQueue::RenderImageListener @@ -1748,6 +1768,16 @@ - (void)requestViewModelInstanceListSize:(uint64_t)viewModelInstanceHandle }]; } +- (void)requestViewModelInstanceName:(uint64_t)viewModelInstanceHandle + requestID:(uint64_t)requestID +{ + [self executeCommand:^{ + auto handle = reinterpret_cast( + viewModelInstanceHandle); + self->_commandQueue->requestViewModelInstanceName(handle, requestID); + }]; +} + - (void)setViewModelInstanceString:(uint64_t)viewModelInstanceHandle path:(NSString*)path value:(NSString*)value diff --git a/Source/Experimental/DataBinding/RiveViewModelInstanceListener.h b/Source/Experimental/DataBinding/RiveViewModelInstanceListener.h index d0a26498..5b0f70f8 100644 --- a/Source/Experimental/DataBinding/RiveViewModelInstanceListener.h +++ b/Source/Experimental/DataBinding/RiveViewModelInstanceListener.h @@ -52,6 +52,10 @@ NS_SWIFT_NAME(ViewModelInstanceListener) path:(NSString*)path size:(NSInteger)size; +- (void)onViewModelInstanceNameReceived:(uint64_t)viewModelInstanceHandle + requestID:(uint64_t)requestID + name:(NSString*)name; + @end NS_ASSUME_NONNULL_END diff --git a/Source/Experimental/DataBinding/ViewModelInstance.swift b/Source/Experimental/DataBinding/ViewModelInstance.swift index 11fbb5e1..d21a06f0 100644 --- a/Source/Experimental/DataBinding/ViewModelInstance.swift +++ b/Source/Experimental/DataBinding/ViewModelInstance.swift @@ -122,8 +122,24 @@ public class ViewModelInstance: Equatable { return lhs.viewModelInstanceHandle == rhs.viewModelInstanceHandle } + // MARK: - Name + + /// Retrieves the name of this view model instance. + /// + /// The name is resolved from the C++ runtime via the command queue. + /// For named instances (created via `.name("myInstance", from:)`), this returns + /// the instance name as defined in the Rive file. For default or blank instances, + /// the name comes from the Rive file's default instance naming. + /// + /// - Returns: The name of this view model instance + /// - Throws: An error if the name cannot be retrieved + @MainActor + public func name() async throws -> String { + return try await dependencies.viewModelInstanceService.name(for: viewModelInstanceHandle) + } + // MARK: - StringProperty - + /// Retrieves the current value of a string property. /// /// - Parameter property: The string property to read diff --git a/Source/Experimental/DataBinding/ViewModelInstanceService.swift b/Source/Experimental/DataBinding/ViewModelInstanceService.swift index 6ccb07df..669af97a 100644 --- a/Source/Experimental/DataBinding/ViewModelInstanceService.swift +++ b/Source/Experimental/DataBinding/ViewModelInstanceService.swift @@ -32,6 +32,24 @@ class ViewModelInstanceService: NSObject, ViewModelInstanceListener { self.dependencies = dependencies } + // MARK: - Name + + /// Retrieves the name of a view model instance. + /// + /// The continuation is resumed when `onViewModelInstanceNameReceived` is called. + /// + /// - Parameter instance: The view model instance handle + /// - Returns: The name of the view model instance + @MainActor + func name(for instance: ViewModelInstance.ViewModelInstanceHandle) async throws -> String { + let commandQueue = dependencies.commandQueue + return try await withCheckedThrowingContinuation { continuation in + let requestID = commandQueue.nextRequestID + continuations[requestID] = AnyContinuation(continuation) + commandQueue.requestViewModelInstanceName(instance, requestID: requestID) + } + } + /// Creates a blank view model instance from an artboard. /// /// Delegates to the command queue. Returns immediately with the instance handle. @@ -694,6 +712,22 @@ class ViewModelInstanceService: NSObject, ViewModelInstanceListener { } } + /// Called when a view model instance name is received. + /// + /// Listener callback invoked by the command server. Dispatches to main actor to access + /// continuations dictionary and resume the continuation with the instance name. + nonisolated public func onViewModelInstanceNameReceived( + _ viewModelInstanceHandle: UInt64, + requestID: UInt64, + name: String + ) { + Task { @MainActor in + if let continuation = continuations.removeValue(forKey: requestID) { + try continuation.resume(with: .success(name)) + } + } + } + /// Called when view model list size is received. /// /// Listener callback invoked by the command server. Dispatches to main actor to access