Skip to content

Commit 5b09da0

Browse files
committed
fix(ios): use onMainSync instead of MainActor.assumeIsolated for JS-callable methods
Nitro calls hybrid methods on the JS thread, not the main thread. MainActor.assumeIsolated crashes at runtime when not on main.
1 parent a212ec7 commit 5b09da0

1 file changed

Lines changed: 21 additions & 10 deletions

File tree

ios/new/HybridRiveView.swift

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class HybridRiveView: HybridRiveViewSpec {
7575
}
7676

7777
func playIfNeeded() {
78-
MainActor.assumeIsolated {
78+
onMainSync {
7979
try? self.getRiveView().playIfNeeded()
8080
}
8181
}
@@ -109,13 +109,13 @@ class HybridRiveView: HybridRiveViewSpec {
109109
func bindViewModelInstance(viewModelInstance: (any HybridViewModelInstanceSpec)) throws {
110110
guard let vmi = (viewModelInstance as? HybridViewModelInstance)?.viewModelInstance
111111
else { return }
112-
try MainActor.assumeIsolated {
112+
try onMainSync {
113113
try getRiveView().bindViewModelInstance(viewModelInstance: vmi)
114114
}
115115
}
116116

117117
func getViewModelInstance() throws -> (any HybridViewModelInstanceSpec)? {
118-
return try MainActor.assumeIsolated {
118+
return try onMainSync {
119119
guard let vmi = try getRiveView().getViewModelInstance() else { return nil }
120120
guard let hybridFile = file as? HybridRiveFile, let worker = hybridFile.worker else {
121121
throw RuntimeError.error(withMessage: "No worker available from file")
@@ -133,43 +133,43 @@ class HybridRiveView: HybridRiveViewSpec {
133133
}
134134

135135
func setNumberInputValue(name: String, value: Double, path: String?) throws {
136-
try MainActor.assumeIsolated {
136+
try onMainSync {
137137
try getRiveView().setNumberInputValue(name: name, value: Float(value), path: path)
138138
}
139139
}
140140

141141
func getNumberInputValue(name: String, path: String?) throws -> Double {
142-
return try MainActor.assumeIsolated {
142+
return try onMainSync {
143143
try Double(getRiveView().getNumberInputValue(name: name, path: path))
144144
}
145145
}
146146

147147
func setBooleanInputValue(name: String, value: Bool, path: String?) throws {
148-
try MainActor.assumeIsolated {
148+
try onMainSync {
149149
try getRiveView().setBooleanInputValue(name: name, value: value, path: path)
150150
}
151151
}
152152

153153
func getBooleanInputValue(name: String, path: String?) throws -> Bool {
154-
return try MainActor.assumeIsolated {
154+
return try onMainSync {
155155
try getRiveView().getBooleanInputValue(name: name, path: path)
156156
}
157157
}
158158

159159
func triggerInput(name: String, path: String?) throws {
160-
try MainActor.assumeIsolated {
160+
try onMainSync {
161161
try getRiveView().triggerInput(name: name, path: path)
162162
}
163163
}
164164

165165
func setTextRunValue(name: String, value: String, path: String?) throws {
166-
try MainActor.assumeIsolated {
166+
try onMainSync {
167167
try getRiveView().setTextRunValue(name: name, value: value, path: path)
168168
}
169169
}
170170

171171
func getTextRunValue(name: String, path: String?) throws -> String {
172-
return try MainActor.assumeIsolated {
172+
return try onMainSync {
173173
try getRiveView().getTextRunValue(name: name, path: path)
174174
}
175175
}
@@ -263,6 +263,17 @@ class HybridRiveView: HybridRiveViewSpec {
263263
}
264264

265265
extension HybridRiveView {
266+
/// Runs a closure on the main thread. If already on main, executes directly
267+
/// to avoid deadlocking with DispatchQueue.main.sync.
268+
func onMainSync<T>(_ work: () throws -> T) rethrows -> T {
269+
if Thread.isMainThread {
270+
return try work()
271+
}
272+
return try onMainSync {
273+
try work()
274+
}
275+
}
276+
266277
func logged(tag: String, note: String? = nil, _ fn: () throws -> Void) {
267278
do {
268279
return try fn()

0 commit comments

Comments
 (0)