From e1e3156dd91b81592b1cd9173ef01d9c43f9dc96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 12 Jan 2026 12:02:07 +0100 Subject: [PATCH] fix(ios): fix memory leak in custom asset loader closure Use weak self capture in customLoader closure to prevent retain cycle between RiveReactNativeView and RiveFile. --- ios/RiveReactNativeView.swift | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/ios/RiveReactNativeView.swift b/ios/RiveReactNativeView.swift index d97926e3..f0791078 100644 --- a/ios/RiveReactNativeView.swift +++ b/ios/RiveReactNativeView.swift @@ -33,7 +33,13 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate var cachedRiveFile: RiveFile? var previousReferencedAssets: NSDictionary? var cachedFileAssets: [String: RiveFileAsset] = [:] - + + private var weakCustomLoader: ((RiveFileAsset, Data, RiveFactory) -> Bool) { + return { [weak self] asset, data, factory in + self?.customLoader(asset: asset, data: data, factory: factory) ?? false + } + } + @objc var resourceName: String? = nil { didSet { if (resourceName != nil) { @@ -330,11 +336,11 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate let updatedViewModel : RiveViewModel if let smName = stateMachineName { - updatedViewModel = RiveViewModel(fileName: name, stateMachineName: smName, fit: convertFit(fit), alignment: convertAlignment(alignment), autoPlay: autoplay, artboardName: artboardName, customLoader: customLoader) + updatedViewModel = RiveViewModel(fileName: name, stateMachineName: smName, fit: convertFit(fit), alignment: convertAlignment(alignment), autoPlay: autoplay, artboardName: artboardName, customLoader: weakCustomLoader) } else if let animName = animationName { - updatedViewModel = RiveViewModel(fileName: name, animationName: animName, fit: convertFit(fit), alignment: convertAlignment(alignment), autoPlay: autoplay, artboardName: artboardName, customLoader: customLoader) + updatedViewModel = RiveViewModel(fileName: name, animationName: animName, fit: convertFit(fit), alignment: convertAlignment(alignment), autoPlay: autoplay, artboardName: artboardName, customLoader: weakCustomLoader) } else { - updatedViewModel = RiveViewModel(fileName: name, fit: convertFit(fit), alignment: convertAlignment(alignment), autoPlay: autoplay, artboardName: artboardName, customLoader: customLoader) + updatedViewModel = RiveViewModel(fileName: name, fit: convertFit(fit), alignment: convertAlignment(alignment), autoPlay: autoplay, artboardName: artboardName, customLoader: weakCustomLoader) } cachedRiveFile = updatedViewModel.riveModel?.riveFile warnForUnusedAssets() @@ -360,7 +366,7 @@ class RiveReactNativeView: RCTView, RivePlayerDelegate, RiveStateMachineDelegate return } do { - let riveFile = try RiveFile(data: data, loadCdn: true, customAssetLoader: customLoader) + let riveFile = try RiveFile(data: data, loadCdn: true, customAssetLoader: weakCustomLoader) self.cachedRiveFile = riveFile let riveModel = RiveModel(riveFile: riveFile) let fit = self.convertFit(self.fit)