Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash at changeValue of SwitchCell #2208

Open
jserrafindster opened this issue Mar 1, 2022 · 9 comments
Open

Crash at changeValue of SwitchCell #2208

jserrafindster opened this issue Mar 1, 2022 · 9 comments

Comments

@jserrafindster
Copy link

Eureka (5.3.4)
Xcode Version 13.2.1 (13C100)
iOS 15.3.0

I'm having some crashes which I believe are related with refreshing the form/sections

Crashed: com.apple.main-thread
0  Eureka                         0x99914 SwitchCell.valueChanged() + 64 (SwitchRow.swift:64)
1  Eureka                         0x99930 @objc SwitchCell.valueChanged() + 4342569264 (<compiler-generated>:4342569264)
2  UIKitCore                      0x49d1fc -[UIApplication sendAction:to:from:forEvent:] + 96
3  UIKitCore                      0x5be774 -[UIControl sendAction:to:forEvent:] + 124
4  UIKitCore                      0x3516fc -[UIControl _sendActionsForEvents:withEvent:] + 348
5  UIKitCore                      0x97ca04 -[UISwitchModernVisualElement sendStateChangeActions] + 80
6  UIKitCore                      0x97c6dc -[UISwitchMVEGestureTrackingSession _sendStateChangeActionsIfNecessary] + 68
7  UIKitCore                      0x97d450 __88-[UISwitchModernVisualElement _handleLongPressWithGestureLocationInBounds:gestureState:]_block_invoke + 64
8  UIKitCore                      0x30f52c -[_UIAfterCACommitBlock run] + 64
9  UIKitCore                      0x229690 -[_UIAfterCACommitQueue flush] + 200
10 libdispatch.dylib              0x631a4 _dispatch_call_block_and_release + 24
11 libdispatch.dylib              0x641a8 _dispatch_client_callout + 16
12 libdispatch.dylib              0x454b8 _dispatch_main_queue_callback_4CF$VARIANT$armv81 + 908
13 CoreFoundation                 0x4d888 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
14 CoreFoundation                 0xb188 __CFRunLoopRun + 2528
15 CoreFoundation                 0x1de1c CFRunLoopRunSpecific + 572
16 GraphicsServices               0x19a0 GSEventRunModal + 160
17 UIKitCore                      0x4ebb90 -[UIApplication _run] + 1080
18 UIKitCore                      0x28116c UIApplicationMain + 332
19 libswiftUIKit.dylib            0x27d24 UIApplicationMain(_:_:_:_:) + 100

This is the code from eureka:

    @objc (valueDidChange) func valueChanged() {
        row.value = switchControl?.isOn ?? false   // crashing here with found nil value
    }

any ideas?

@mats-claassen
Copy link
Member

Hi, could you please provide exact crash error. Also, are you using a custom nib file for this row? Some code to reproduce the error would also be helpful as it does not seem to happen in the Example project.

@jserrafindster
Copy link
Author

not sure if what your asking is this message
0 Eureka 0x0000000103c65914 Swift runtime failure: Unexpectedly found nil while implicitly unwrapping an Optional value + 0 (SwitchRow.swift:0)

this is what is in the crash report

Exception Type:  EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x0000000103c65914
Exception Note:  EXC_CORPSE_NOTIFY
Termination Reason: SIGNAL 5 Trace/BPT trap: 5
Terminating Process: exc handler [3368]

Triggered by Thread:  0


Thread 0 name:
Thread 0 Crashed:
0   Eureka                        	0x0000000103c65914 Swift runtime failure: Unexpectedly found nil while implicitly unwrapping an Optional value + 0 (SwitchRow.swift:0)
1   Eureka                        	0x0000000103c65914 SwitchCell.valueChanged() + 204 (SwitchRow.swift:63)
2   Eureka                        	0x0000000103c65888 row.get + 44 (Cell.swift:0)
3   Eureka                        	0x0000000103c65888 SwitchCell.valueChanged() + 64 (SwitchRow.swift:63)
4   Eureka                        	0x0000000103c65930 @objc SwitchCell.valueChanged() + 24 (<compiler-generated>:0)
5   UIKitCore                     	0x00000001858f01fc -[UIApplication sendAction:to:from:forEvent:] + 96 (UIApplication.m:5361)
6   UIKitCore                     	0x0000000185a11774 -[UIControl sendAction:to:forEvent:] + 124 (UIControl.m:871)
7   UIKitCore                     	0x00000001857a46fc -[UIControl _sendActionsForEvents:withEvent:] + 348 (UIControl.m:942)
8   UIKitCore                     	0x0000000185dcfa04 -[UISwitchModernVisualElement sendStateChangeActions] + 80 (UISwitchModernVisualElement.m:418)
9   UIKitCore                     	0x0000000185dcf6dc -[UISwitchMVEGestureTrackingSession _sendStateChangeActionsIfNecessary] + 68 (UISwitchMVEGestureTrackingSession.m:123)
10  UIKitCore                     	0x0000000185dd0450 __88-[UISwitchModernVisualElement _handleLongPressWithGestureLocationInBounds:gestureState:]_block_invoke + 64 (UISwitchModernVisualElement.m:562)
11  UIKitCore                     	0x000000018576252c -[_UIAfterCACommitBlock run] + 64 (_UIAfterCACommitQueue.m:137)
12  UIKitCore                     	0x000000018567c690 -[_UIAfterCACommitQueue flush] + 200 (_UIAfterCACommitQueue.m:228)
13  libdispatch.dylib             	0x0000000182e4a1a4 _dispatch_call_block_and_release + 24 (init.c:1517)
14  libdispatch.dylib             	0x0000000182e4b1a8 _dispatch_client_callout + 16 (object.m:560)
15  libdispatch.dylib             	0x0000000182e2c4b8 _dispatch_main_queue_callback_4CF$VARIANT$armv81 + 908 (inline_internal.h:2601)
16  CoreFoundation                	0x000000018313a888 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12 (CFRunLoop.c:1795)
17  CoreFoundation                	0x00000001830f8188 __CFRunLoopRun + 2528 (CFRunLoop.c:3144)
18  CoreFoundation                	0x000000018310ae1c CFRunLoopRunSpecific + 572 (CFRunLoop.c:3268)
19  GraphicsServices              	0x00000001a34b59a0 GSEventRunModal + 160 (GSEvent.c:2200)
20  UIKitCore                     	0x000000018593eb90 -[UIApplication _run] + 1080 (UIApplication.m:3493)
21  UIKitCore                     	0x00000001856d416c UIApplicationMain + 332 (UIApplication.m:5047)
22  libswiftUIKit.dylib           	0x00000001995fdd24 UIApplicationMain(_:_:_:_:) + 100 (UIKit.swift:530)

we are not using nibs

we do everything by code

Also I'm not able to reproduce in my iPhone XR, but I see this sort of error ocurring more often than it should, but not actually related with a SwitchRow. More as when we reload and refresh.

This is the reason why I've start limiting my refresh/reloads to smaller parts of the form rather than update the whole form.

For instance

We use realm with observers.
Instead refreshing the whole form each time the collection in database changes we specifically just refresh a section in the form if a certain property in database is update

@mats-claassen
Copy link
Member

mats-claassen commented Mar 1, 2022

OK. So there are two variables which could be nil but shouldn't in that row. If you have been able to debug this, would you know which variable is nil?
It would be easier to debug this issue with some code to reproduce it. Specifically how that SwitchRow is set up. For example, avoid accessing the cell in the row initializer

@jserrafindster
Copy link
Author

jserrafindster commented Mar 3, 2022

Not sure if i understood correctly, you are refering "Two variables" as switchControl and isOn? because I don't think thats the issue. If one of them is nil it will just assume ?? false right?
for what I've seen I would bet more in the row variable which is defined as
public weak var row: RowOf<T>!

-- edit --
just notice that .value is also an optional

@mats-claassen
Copy link
Member

mats-claassen commented Mar 3, 2022

Right, I was referring to row or switchControl. I agree it looks more like the row being nil but the row should always be set (it is set immediately after creating the cell, which should happen before calling setup and therefore you should never get into valueChanged before that). So I can only imagine this happening because the row is deinstantiated and somehow the switch changes value after that. Please review your code to check whether that could be possible. I repeat, a code to reproduce this would be helpful.

@jserrafindster
Copy link
Author

jserrafindster commented Mar 4, 2022

I'm going back to talk about reload refreshing the sections cause we are also gettting this issue which may as well be related with our implementation

Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out)
for what i understood this issue is related with updating our form UI but having the datasource outdated or something like that ?

So I slightly change our code to share it here
we update our form with reload() function

    struct MySection : FormSectionProtocol  {
        var title: String = "Section title"
        var tag : String { return title }
        var vc: FormViewController
        let sectionType : Sections = .sectionTypeId
        
        init(vc : FormViewController) {
            self.vc = vc
            generate()
        }
        
        enum rows {
            case firstRowType // image cell
            case secondRowType // switch cell
            
            var row : BaseRow {
                switch self {
                    
                case .firstRowType:
                    return  ImageRow(){ row in
                        row.cell.textTitle = "row title1"
                        row.cell.cellImage = #imageLiteral(resourceName: "image")
                    }
                    .onCellSelection({ cell,row in
                        row.deselect()
                        // navigate
                    })
                case .secondRowType:
                    let row = SwitchRow(){ row in
                        row.cell.height = { 60 }
                        row.cell.imageView?.image = #imageLiteral(resourceName: "image")
                        row.title = "this is the second row"
                        
                        row.cell.switchControl.isOn = true // TODO: fetch value
                        row.value = row.cell.switchControl.isOn
                    }.onCellSelection {
                        cell,row in
                        cell.switchControl.setOn(!cell.switchControl.isOn, animated: true)
                        row.value = cell.switchControl.isOn
                        row.deselect()
                    }.onChange { row in
                        guard let cell = row.cell else { return }
                        
                        let currentValue = true // TODO: fetch value
                        guard currentValue != row.value else { return } // prevent rollback from triggering the api request
                        
                        remoteProcedure(status: cell.switchControl.isOn) {
                            success in
                            DispatchQueue.main.async {
                                switch success {
                                case true: break // update value
                                case false:
                                    cell.switchControl.setOn(!cell.switchControl.isOn, animated: true) // roll back to previous value
                                    row.value = cell.switchControl.isOn // keeps row value consistent, switch value doesnt do it automatically
                                }
                            }
                            
                        }
                        
                    }
                        .cellUpdate { cell,row in
                            cell.textLabel?.textColor = UIColor.Global.Text
                        }
                    row.tag = "myRow"
                    return row
                }
            }
        }
        
        mutating func generate()
        {
            self.title = title
            self.vc.form +++ self.section
            self.populate()
        }

        func populate() {
            if let mySection = vc.form.sectionBy(tag: tag) {
                mySection <<< rows.firstRowType.row
                mySection <<< rows.secondRowType.row
            }
        }
        
        func reload() {
            UIView.performWithoutAnimation {
                vc.tableView.beginUpdates()
                if let mySection = vc.form.sectionBy(tag: tag) {
                    mySection.removeAll()
                }
                populate()
                vc.tableView.reloadData() // this might be causing a crash, should not be called inside the begin end (?)
                vc.tableView.endUpdates()
                
            }
        }
    }   

@mats-claassen
Copy link
Member

Thanks for posting the code. First comment on it is that you are accessing the cell in the row initializer which you should avoid. Use cellSetup for everything that relates to the cell.
Example:

SwitchRow() { row in
    row.title = "..."
    row.value = ...
}.cellSetup { row, cell in
    cell.height = { 60 }
    ...
}

As for the invalid number of rows in section 0. issue: try just calling the removeAll and populate functions. Or maybe reloadData after that without the begin and end updates which should be called internally.

@jserrafindster
Copy link
Author

Just an update
I'm still testing but...
removing the beginUpdates and endUpdates seems to help (still not sure if it fixes it )
about using setup instead update it was not working, the style was not being applied. for instance to text color and font (not sure if cell height is being setup correctly)

@mats-claassen
Copy link
Member

Hey, it is correct to use either cellSetup or cellUpdate and for certain modifications you need cellUpdate. Just don't access the cell in the row's initializer block.

SwitchRow() { row in
    row.title = "..."
    row.value = ...
    row.cell.... = ... // <-- Don't do this
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants