rive-app / rive-ios

iOS runtime for Rive
MIT License
496 stars 57 forks source link

Application crashes (EXC_CRASH (SIGABRT)) when displaying an animation with state machine in SwiftUI. #330

Closed leonschrijvers closed 2 months ago

leonschrijvers commented 3 months ago

Description

Application crashes (EXC_CRASH (SIGABRT)) when displaying an animation with state machine in SwiftUI.

Some of our users have reported an immediate application crash when a Rive animation is displayed. Analysis of crash reports indicate that this is caused by a call from the RiveRuntime framework, causing a NSInvalidArgumentException, see snippet below: (Full crash reports are available if needed)

...
  "exception" : {"codes":"0x0000000000000000, 0x0000000000000000","rawCodes":[0,0],"type":"EXC_CRASH","signal":"SIGABRT"},
  "termination" : {"flags":0,"code":6,"namespace":"SIGNAL","indicator":"Abort trap: 6","byProc":"Application","byPid":1045},
  "asi" : {"libsystem_c.dylib":["abort() called"]},
  "exceptionReason" : {"arguments":["-[__NSArrayM insertObject:atIndex:]"],"format_string":"*** %s: object cannot be nil","name":"NSInvalidArgumentException","type":"objc-exception","composed_message":"*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil","class":"NSException"},
  "extMods" : {"caller":{"thread_create":0,"thread_set_state":0,"task_for_pid":0},"system":{"thread_create":0,"thread_set_state":0,"task_for_pid":0},"targeted":{"thread_create":0,"thread_set_state":0,"task_for_pid":0},"warnings":0},
  "lastExceptionBacktrace" : [
{"imageOffset":978358,"symbol":"__exceptionPreprocess","symbolLocation":226,"imageIndex":9},
{"imageOffset":81565,"symbol":"objc_exception_throw","symbolLocation":48,"imageIndex":7},
{"imageOffset":123841,"symbol":"-[__NSArrayM insertObject:atIndex:]","symbolLocation":1762,"imageIndex":9},
{"imageOffset":35776,"symbol":"-[RiveStateMachineInstance stateChanges]","symbolLocation":178,"imageIndex":1},
...

Provide a Repro

This is a stripped down version of our code. Note that state machine inputs have been anonimized, but the logic is the same as we use in our application. If needed, we can privately share our production code and .riv/.rev file.

struct ContentView: View {
    @EnvironmentObject var appState: AppState

    let property1 = false

    let min = 1.0
    let max = 3.0
    @State private var property2 = 1.0

    @State var timer = Timer.publish(every: 60,  tolerance: 1, on: .main, in: .common).autoconnect()

    @StateObject var riveViewModel = RiveViewModel(
        fileName: "animation",
        stateMachineName: "Default State Machine"
    )

    var body: some View {
        VStack {
            if appState.showAnimation {
                riveViewModel.view()
                    .backgroundStyle(.clear)
            }
        }
        .onAppear(perform: {
            riveViewModel.setInput("Property1", value: property1)
            riveViewModel.setInput("Property2", value: property2)
            riveViewModel.setInput("Property3", value: false)
        })
        .onHover { hover in
            if (hover) {
                riveViewModel.setInput("Property3", value: true)
                property2 = min
            }
            else {
                riveViewModel.setInput("Property3", value: false)
            }
        }
        .onReceive(timer) { value in
            if property2 < max {
                property2 += 1
            }
        }
        .onChange(of: property2) {
            riveViewModel.setInput("Property2", value: property2)
        }
    }
}

Expected behavior

Present the animation without any crashes.

Device & Versions (please complete the following information)

Additional context

Unfortunately, we cannot reproduce this issue ourselves, which makes it hard to troubleshoot. However, several users have experienced this unexpected behavior. We are open to test/validate any suggestions with our test users.

dskuza commented 2 months ago

Closing this issue as it was resolved with the developer. Notable changes were made to the runtime to help with this crash in releases v5.14.3 and v5.14.4.

Note: if you are seeing a similar stack trace to above, a workaround is, if you are not utilizing RiveView.stateMachineDelegate, set it to nil before your animation is set up, with one option being subclassing RiveViewModel, and overriding createRiveView:

class CustomViewModel: RiveViewModel {
    override func createRiveView() -> RiveView {
        let view = super.createRiveView()
        view.stateMachineDelegate = nil
        return view
    }
}

Additionally, check naming within your state machine; sometimes certain characters are not decoded properly, and cause an attempted insertion of a nil value, causing this crash.