SwiftUI: app crashed with "UITableView internal inconsistency" error - ios

Update: After spending several hours doing experiment, I finally identify the root cause. See this. I'll close the question.
My app crashed with "UITableView internal inconsistency" error in a random manual testing. Below is the error message (I removed the stack trace because it's long).
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView internal inconsistency: encountered out of bounds global row index while preparing batch updates (oldRow=2, oldGlobalRowCount=2)'
The error usually occurs in UIKit apps. In my SwiftUI app, however, I don't deal with UIKit directly, it's SwiftUI that does it. So my first thought was that this was a bug in SwiftUI. But this is a very basic error (it was an "out of index" error). That, plus the fact it occurred after I made a minor change to the app, makes me not sure. I wonder if anyone saw this issue in your app? Could it be caused by the an incorrect way of doing SwiftUI programming?
More information:
This was first time I observed the crash. I observed it more than once since then but couldn't reproduce it consistently, not to mention provide a simple example to demonstrate it.
I first observed the crash after I made a minor change to my app (though I'm not sure if the change is relevant). The purpose of the change it to generalize the code to implement a custom binding. In the old code, the custom binding reads/writes data by capturing data in closure. In the new code, the custom binding reads/writes data through another binding (could the use of that additional binding cause the issue?). The text field is in a form. The crash occurred when I clicked save button in the form.
Old code:
#ObservedObject var param: Param
...
TextField("Notes", text: Binding(
get: { param.notes ?? "" },
set: { param.note = ($0 != "" ? $0 : nil) } ))
New code:
func nonOptionalText(_ value: Binding<String?>) -> Binding<String> {
return Binding() {
value.wrappedValue ?? ""
} set: { newValue in
value.wrappedValue = (newValue != "" ? newValue : nil)
}
}
...
#ObservedObject var param: Param
...
TextField("Notes", text: nonOptionalText($param.notes))

Related

IOS -Core Data Stacl

My question are as follows:
Will creating a custom enum to handle the coredata errors be the best way to handle errors in this case
If the persistent store container doesn't load or we can't save the context then there is no need to use the app and crashing is the best option correct?
In order to present these error messages to the user, wouldn't I have to adapt UIAlertAction? Which would also mean I would need to register notifications?
By law we have to get permission to send notifications, would it be best to create a whole new file for notification & add the error enum in that file or would it make more sense to have the core data stack class conform to the Notification protocol?
The end goal is to notify the user that either the persistent store wouldn't load or the moc wouldn't save
Thanks for your time in advance!
enum CoreDataError: Error {
// This is my custom Error handling enum
case persistenStore(description: String )
case saveChanges(description: String )
}
func coreDataErrors(throwError: Bool) throws -> CoreDataError {
// This is my Error Handling function
}
class CoreDataStack {
lazy var managedObjectContext: NSManagedObjectContext = {
let container = self.persistentContainer
return container.viewContext
}()
private lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "SafeHouseCDPhotoVault")
container.loadPersistentStores() { storeDescription, error in
if let error = error as NSError? {
fatalError("Unresolved error: \(error), \(error.userInfo)")
}
}
return container
}()
}
/* In almost every app tutorial available we learn to deal with errors using this fatal error logic to speed through the project
*/
extension NSManagedObjectContext {
func saveChanges() {
if self.hasChanges {
do {
try save()
} catch {
fatalError("Error: \(error.localizedDescription)")
}
}
}
}
// Again the demos have us deal with errors using fatal error
I typically use enums for error cases, but I wouldn't name them according to their source. Name them according to how you will will them for recovery or error-handler behavior. If you can't handle them, there's not much point in generating them.
That gets to the second question; if you can't handle a case at all (such as the MOM missing on launch), crashing is about all you can do. I don't like alerts in that case. What's the user going to do with that information? A crash at least will be sent to Apple and you can see it and do something about it.
If the user can do something, then absolutely provide an error. If there is any hope that the error is transitory (such as a save failure, which may be due to a full disk), then maybe provide them an error/retry. But on iOS this generally is not worth the trouble and the risk of generating bugs. How are you going to test your error/retry system? If you can't test it, how do you know it's better than crashing? (This isn't an idle question; I once built a crash-catching system that had a bug and caused the crash handler to go into a tight loop and drain the battery rapidly. That's worse than crashing.)
If you're a beginner, then you're probably not in a place to handle uncommon Core Data errors and the best and safest thing you're going to do is crash. Handling these things well is quite complex and difficult to test, and I generally do not recommend it on iOS (macOS is a bit different because write-errors are much more often transitory).
That's up to you.
Exit the app properly with an alert and not by crashing it
Why would you need notifications for showing an alert?
This question makes no sense to me.

(lldb) error with Firebase

I'm getting an lldb error whenever I click on a specific tab. The tab is called Account and its supposed to pull data from a child node called "requests" and display that data in a list(table view controller).
I've done this multiple times before. I'm even using my own recycled code for this task, but the error keeps showing up. I've already made sure each button/label is connected to the view controller and I've made sure none are disconnected.
The error message is saying that my request object isn't key-code compliant, but that's not the case either. What else could it be?
This is the code along with some screenshots:
Here is the method I'm using. The error only comes up when I call this function. Other than that, the screen is just blank, so I figured that the issue has to be within the function. btw, requestsArray is an array of requests declared as such: var requestsArray = [requests]()
Heres the rest of the code from that view controller:
EDIT
In Swift 4, the vars need to be marked as #objc like this
class requests: NSObject {
#objc var from: String?
#objc var location: String?
...
}

NSException Error - Swift 4 [duplicate]

This question already has answers here:
Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]
(6 answers)
Closed 5 years ago.
stack overflow community. Please don't mark this question as a duplicate as I have seen all the other posts regarding this and have pretty much gone through most of the famous solutions.
Currently, I have some code that allows a user to send messages to a group and also change the group thumbnail and title. However, upon the user doing any of this, my code crashes. I am storing my data in firebase (the data is being successfully uploaded). The problem began when I upgraded from swift 3 to swift 4. Also, this is the specific error I am getting:
libc++abi.dylib: terminating with uncaught exception of type NSException
Here are some solutions/methods I have tried to solve my problem:
I have outruled that this is an outlet problem as the view controller is programmatically created.
I tried adding an exception breakpoint but it didn't show me any specific lines of code for where the crash occurred.
I have also tried cleaning, re-building, and closing Xcode however that doesn't work as well.
I don't think its a problem with uploading data to firebase as I am able to in other parts of my app.
I have also tried scrolling through the threads to get a deeper understanding of where the crash occurs, however that has not been of much help.
Here is a picture of my console output:
Here is the code I use to update the collection view:
func observeConversationMessagesWithConversationId(conversationId: String) {
Database.database().reference().child("conversation-messages").child(conversationId).observe(.childAdded, with: { (snapshot) in
if let messages = ConversationMessages(snapshot: snapshot) {
self.conversationMessages.append(messages)
DispatchQueue.main.async(execute: {
self.collectionView?.reloadData()
let indexPath = NSIndexPath(item: self.conversationMessages.count - 1, section: 0)
self.collectionView?.scrollToItem(at: indexPath as IndexPath, at: .bottom, animated: true)
})
}
}, withCancel: nil)
}
Here is my podfile
Any help would be appreciated. Please note that I would have added some code but I have no clue where the error occurs.
Since you don't know where the error occurs, it might be a good idea to place a few breakpoints in your code so you can pinpoint the crash. Then it will be a lot easier to fix the error when you know the exact line where it happens.
I had the same crash and it was related to UIVisualEffectView, and specifically with adding a subview to it instead of adding it to its .contentView.
If that is your case too, you can find more details here.
Good luck bug hunting.

Scope of causes for EXC_BREAKPOINT crash

I have this stack trace in Fabric:
My question: From the crash log, is the function 'formatMessageAuthorName' the only cause for this EXC_BREAKPOINT crash? E.g., are there other possible causes for the crash apart from the code inside this function?
Here is my formatMessageAuthorName function:
private static func formatMessageAuthorName(firstname: String, lastname: String?=nil) -> String {
// Capitalise first character of firstname
var Cap_firstname = firstname
Cap_firstname.replaceRange(Cap_firstname.startIndex...Cap_firstname.startIndex, with: String(Cap_firstname[Cap_firstname.startIndex]).capitalizedString)
guard let lastname = lastname else { return Cap_firstname }
// if has lastname & first char, capitalise too and concat with firstname.
if let firstCharLastName = lastname.characters.first {
return "\(Cap_firstname) \(String(firstCharLastName).uppercaseString)."
} else {
return firstname
}
}
My assumption
The only clue that I know that will make the function crash is when 'firstname' is an empty string, it will crash here since it accesses invalid array index:
String(Cap_firstname[Cap_firstname.startIndex])
However, I'm still skeptical about this assumption, since I'm quite sure that 'firstname' is not empty (it's retrieved from server). I even tested it by logging into some user accounts that has this crash, and using that page (MessageViewController), but I never had the crash myself and firstname is shown correctly. It also seems to not be about iOS versions as I received crash from iOS 8, 9, and 10.
I have this crash a lot (>300) after my recent app update and I have no idea why as it never happens before, the code here does not change through the update, and I can never reproduce it with the effected users.
If the culprit can only be the code in this function, and no other possibilities (like multi-threading, Realm etc.), I can turn to focus on the server issues instead, like how 'firstname' might be an empty string. But still, I can't imagine how it could happen, since I already used those user accounts and never have this crash myself.
Thanks a lot.
EXC_BREAKPOINT is always triggered by executing a trap instruction of some sort(*) and the exception gets delivered directly to the thread that executed the trap instruction. So if you are seeing a crash report that says this thread with this stack got an EXC_BREAKPOINT, that means that thread really did to something that executed a trap instruction.
You could probably see this by looking at the crash address in your binary, you will see some kind of trap instruction there. The swift standard library uses trap instructions to signal various kinds of invalid access errors, and that code probably got inlined into the function that's crashing. So this makes sense of your example above.
(*) EXC_BREAKPOINT can also be used for data watches, but that's not what's happening here, and anyway they would still be delivered to the thread that accessed the watched data...

Firebase crashes with 'listen() called twice for the same query' error

I was trying to follow the advice and remove the listener when needed and register the listener when needed. So in my UIViewController.viewDidAppear I have the following:
let chatRef = messagesRef.childByAppendingPath(chat.objectId!)
var query = chatRef.queryOrderedByChild("createdAt")
if let since = since {
query = query.queryStartingAtValue(since.timeIntervalSince1970 * 1000)
}
let handle = query.observeEventType(FEventType.ChildAdded, withBlock: completion, withCancelBlock: { (error: NSError!) -> Void in
println("error listening for new Chat messages: \(error)")
});
In my UIViewController.viewWillDisappear() I have
let chatRef = messagesRef.childByAppendingPath(chat.objectId!)
if chatRef != nil {
chatRef.removeAllObservers()
}
But the program crashes every time the ViewController is entered the second time (going to the view controller, navigate away, then come back) with the following error:
*** Assertion failure in -[FPersistentConnection listen:tagId:hashFn:onComplete:], /Users/mtse/Dev/firebase/firebase-client-objc/Firebase/Firebase/Core/FPersistentConnection.m:127
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'listen() called twice for the same query'
The program runs fine if I don't remove the observers and call observeEventType only once in viewDidLoad instead of viewDidAppear.
The program also runs fine even if I remove the observer then add it back if I don't do queryOrderedByChild and queryStartingAtValue.
So what am I doing wrong here?
Disclaimer: I work for Firebase
Listeners in Firebase are specific to the path or query that you register them on. Calling removeAllObservers() removes all observers, but only from that path.
So in your viewWillDisappear() you will need to remove the listeners from the query, instead of the ref.
query.removeAllObservers()
We just made this more explicit in our documentation and are looking at ways to make the API more intuitive.
Update (20150724)
It turns out that calling removeAllObservers() on a FFirebase should remove all observers on queries on that same location too. It will not remove observers at child() locations, but should have worked in your case.
We are investigating what is going wrong, but it seems you have hit a bug in our iOS SDK. Once we find it, we'll release a fixed version. In the meantime the above serves (and will continue to serve) as a valid workaround.

Resources