Debug Swift crash EXC_BREAKPOINT (SIGTRAP) - ios

I just downloaded a crash that happened and I am not sure how I could get more details as to what happened. All I can see is that crash happened inside of a Operation Queue, all I can see is that it happened inside of SyncToServer.sync(object:) method call and that it happened inside of a block.
I suspect that it could happen in this part of code:
self.queue.async {
if success {
self.syncObjects.remove(at: index)
self.saveState()
} else {
self.syncIndex += 1
}
self.syncing = false
self.sync()
}
I am also attaching the crash report below.

Related

Can two same closures on the main thread, from the same function call, crash an iOS program written in Swift?

My users experienced crashes when I sent them an update on TestFlight. After examining the eight crash reports they submitted, I've noticed a commonality - there are two of the same closures sitting on top of thread 0. Could this have caused the crash? What do you think is the cause, if not?
Please see image for crash report of thread 0. All other threads generally look the same in the report.
Note - when the users opened their app subsequent times after the initial opening, they did not experience further crashes.
Thank you in advance for your thoughts.
Update from comments, 9/29/22 -
Here's the closure code as requested by Damien and Tadreik:
When the app is opened, this line runs during initialization, which sets up the variables the connection view controller needs. Thus the empty closure.
if !twilioIDs.isEmpty {
ProfileModelManager.shared.getUsersForConnectionView(withTwilioIDs: twilioIDs) { _ in }
}
And the code below is invoked when the connection view is tapped on from the menu tab the second time:
if !twilioIDs.isEmpty {
ProfileModelManager.shared.getUsersForConnectionView(withTwilioIDs: twilioIDs) { result in
guard let success = result else { return }
if success {
self.handleSuccessOfGettingConnectionCards()
}
else {
self.handleFailureOfGettingConnectionCards()
}
}
}
Here is the code for handleSuccessOfGettingConnectionCards -
refreshControl.endRefreshing()
hideNoConnectionsLabel()
createChatViewControllersForChannels()
connectionsTableView.alpha = 1
errorConnectingLabel.alpha = 0
connectionsTableView.reloadData()
showActivitySpinner(false)
navigationController?.navigationBar.isHidden = true
Here is the code for handleFailureOfGettingConnectionCards -
showErrorConnectingMessage()
refreshControl.endRefreshing()
connectionsTableView.alpha = 0
hideNoConnectionsLabel()
showActivitySpinner(false)
Thanks in advance again for any intuition or experience you may share.
The crash log for thread 0
The code inside the closure ProfileModelManager.shared.getUsersForConnectionView(withTwilioIDs: twilioIDs) { result in captures a reference to self, your problem might be that at the time of execution, self is pointing to a null reference (has been deallocated), which can cause a crash. Try to set a weak reference to self like that and see if the crash still occurs :
if !twilioIDs.isEmpty {
ProfileModelManager.shared.getUsersForConnectionView(withTwilioIDs: twilioIDs) { [weak self] result in
guard let success = result else { return }
if success {
self?.handleSuccessOfGettingConnectionCards()
}
else {
self?.handleFailureOfGettingConnectionCards()
}
}
}
If you want you could also handle the nil case of the weak self as any other optional.

UIViewPropertyAnimator crashes on dealloc after calling finishAnimation(at:)

When using UIViewPropertyAnimator and calling finishAnimation(at:) after stopAnimation(false):
You either get a crash log of:
- [UIViewPropertyAnimator dealloc]
Or if you catch the assertion in a debugger (by setting an breakpoint for Objective-C exceptions)
Assertion failure in -[UIViewPropertyAnimator finishAnimationAtPosition:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3900.12.16/UIViewPropertyAnimator.m:1981
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'finishAnimationAtPosition: should only be called on a stopped animator!'
If you manage to catch the assertion, (the crash log isn't that helpful unless you catch those at release runtime as well), you will see that it complains about finishAnimation being called on a non-stopped animator.
Yet my code shows the animator always being stopped before finishAnimation is called.
animator.stopAnimation(false)
animator.finishAnimation(at: .start)
What could be causing this crash, the state should always be stopped after calling stopAnimation?
The reason that this happened for me was a timing issue with completion blocks:
It means that UIViewPropertyAnimator.state (UIViewAnimatingState) is in an invalid state. Since this is just an Objective C enum it can have any raw value beyond what is defined in the cases. In the case of my crash the value was 5.
I was doing a bunch of chained animations that referenced each other and had something like this:
animator.addCompletion({ _ in
// Here I called a piece of code that eventually referenced this animator
// and attempted to call `stopAnimation(false)` and `finishAnimation(at:)`
// on it causing the crash.
//
// You will notice if you examine the `state` of the animator here, it will have
// an invalid `rawValue` of 5, at least up to iOS 13.2, as this is an implementation detail.
})
The solution is to not attempt any operations on the animator until the completion block is finished executing.
If you can not avoid it, a workaround that works with current UIKit implementations is to DispatchQueue.main.async out of that completion block which will allow it to finish. Of course, this is not a guarantee that the state will be valid inside of that async in future versions of iOS.
On Swift, to debug this you can use the following extension:
extension UIViewAnimatingState: CustomStringConvertible, CustomDebugStringConvertible {
var isValid: Bool {
switch self {
case .inactive, .active, .stopped:
return true
default:
return false
}
}
public var description: String {
switch self {
case .inactive:
return "inactive"
case .active:
return "active"
case .stopped:
return "stopped"
default:
return "\(rawValue)"
}
}
public var debugDescription: String {
return description
}
}
Therefore when you call stopAnimation(false) and finishAnimation(at:) you can at least avoid a crash and alert yourself to a possible logical issue with your animators:
// This is for debugging, the code below should be safe even if isValid is false
assert(animator.state.isValid, "animator state is not valid")
animator.stopAnimation(false)
if animator.state == .stopped {
// This call is the one that can assert and crash the app
animator.finishAnimation(at: .start)
}

Saving then accessing entity from managedObjectContext in CoreData occasionally crashes

I'm struggling to debug a hard-to-reproduce-locally crash in my app. The crash reports show the exception type is EXC_BAD_ACCESS (SIGSEGV) with objc_msgSend at the top of the crashed thread, perhaps suggesting "The process may have attempted to message a deallocated object" (as per Apple's documents).
I have not been able to recreate the crash or find any zombies using the Zombies instrument, despite using an older device and the same simulated device causing the most crashes.
From the trace I'm pretty sure I've been able to work out where the crash is occurring, but I'm not sure what I'm doing wrong.
My app is basically a group database. The app takes a username and password, sending them to a server to check if the person can be logged in. If valid, the app downloads the data on all the people in the group, then finds the logged in person from the downloaded data based on their username.
Because of the way the server code is set up, the username must be present in the downloaded data (if it all downloads correctly), yet my code occasionally fails to find them, and (from the crash logs) also occasionally this attempted finding of the person causes a crash.
coreDataStack.storeContainer.performBackgroundTask() { context in
OverallNetworkCalls.deleteAllLoadPeople(moContext: context) { result in
self.coreDataStack.saveNewContext(context: context)
if result == .success {
let person = AdministrativeHelperFunctions.returnCurrentUserFromUserName(moContext: context)
if let person = person {
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
}
UserDefaults.standard.set(person.objectID.uriRepresentation(), forKey: udl.CurrentUserMOID.rawValue)
} else {
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
self.loadingLabel.text = "Data was downloaded but your username isn't there. Try deleting and reloading the app, or contact admin."
}
}
}
func saveNewContext(context: NSManagedObjectContext) {
context.perform {
guard context.hasChanges else { return }
do {
try context.save()
} catch {
//logs the issue locally
}
}
}
func returnCurrentUserFromUserName(moContext: NSManagedObjectContext) -> Person? {
let userName = UserDefaults.standard.string(forKey: "username")
if let userName = userName {
//Create predicate, arrayForResults etc
do {
userNameMatchesArray = try moContext.fetch(fetchRequest)
} catch {
//logs the issue locally
}
if userNameMatchesArray.count == 1 {
return userNameMatchesArray[0]
} else {
return nil
}
} else {
return nil
}
}
My suspicion is that the returnCurrentUserFromUserName function is returning the user before the saveNewContext function has completed its task and that this is contributing to the issue, though given the same managedObjectContext is saving then retrieving the person I had originally assumed this wasn't a problem. I'm also not sure that using "context.perform" in the saveNewContext function is necessary/wise/useful; I'd value feedback on this too if anyone knows best practice inside a performBackgroundTask block.
A non-reproduceable bug is always the most frustrating to fix, and because of this anything I try to fix it won't be shown until I send it to my beta testers and get crashlogs back (or not!).
Thanks in advance for reviewing this problem.

Send a log to Crashlytics without an app crash

How can I get Crashlytics to receive a log without my app crashing?
I have the following code:
if(!context.managedObjectContext save:&error) {
CLS_LOG(#"%#",error.description)
}
When an error occurs, I want the Crashlytics server to receive the error but the app should continue running.
I do not need the log right away. I would be happy to get the log on the next restart. I just do not want to have to trigger a crash in my app to receive the log.
Is this possible?
With the new update from crashlytics you can now use:
[[FIRCrashlytics crashlytics] recordError:error];
And in Swift:
Crashlytics.crashlytics().record(error: error)
You can check the documentation here.
Kinda old question, but now you can use Answers which is part of the Fabric suit (Crashlytics is part of Fabric as well):
Fabric can be found here. And further documentation here.
I tried the below lines and it works like charm. In try-catch block use the below lines in your catch block
#try {
// line of code here
}
#catch (NSException *exception) {
NSUncaughtExceptionHandler *handler = NSGetUncaughtExceptionHandler();
handler(exception);
}
as explained at http://support.crashlytics.com/knowledgebase/articles/222764-can-i-use-a-custom-exception-handler
[UPDATE]
Now in fabric's crashlytics we can use simple function [Crashlytics recordCustomExceptionName:reason:frameArray:] for sending handled exceptions
#try {
// line of code here
}
#catch (NSException *exception) {
NSArray *stack = [exception callStackReturnAddresses];
[[Crashlytics sharedInstance] recordCustomExceptionName: exception.name
reason: exception.reason
frameArray: stack];
}
as explained at
https://twittercommunity.com/t/crashlytics-ios-how-to-send-non-fatal-exceptions-without-app-crash/34592/32
For me the method .recordError() didn't helped, because it don't log the user information. Or i just didn't found where to watch it. Using recordCustomExceptionName fit to me. There is an example of implementation of the both ways:
func logMessage(_ message: String) {
let userInfo = ["message" : message]
let error = NSError(domain: "AppErrorDomain", code: 1, userInfo: userInfo)
Crashlytics.sharedInstance().recordCustomExceptionName("API Error", reason: message, frameArray: [])
Crashlytics.sharedInstance().recordError(error, withAdditionalUserInfo: userInfo)
}
Swift 5, Crashlytics SDK 4.0.0-beta.6:
let exceptionModel = ExceptionModel(name: "exception title", reason: "details")
Crashlytics.crashlytics().record(exceptionModel: exceptionModel)
...similar for NSError, with whatever you want to see in the Crashlytics dashboard.
let error = NSError(domain: "error title", code: 0, userInfo: ["message":"some details"])
Crashlytics.crashlytics().record(error: error)
Crashlytics is a crash tracking service, if you need to track custom messages choose other analytics service.
In reference from Crashlytics documents.
try {
myMethodThatThrows();
} catch (Exception e) {
Crashlytics.logException(e);
// handle your exception here!
}
https://docs.fabric.io/android/crashlytics/caught-exceptions.html?caught%20exceptions#caught-exceptions
As far as I know, if you dont protect your code correctly, your application will crash anyway. Crashlylytics, take this crashes and show them to you in a "readable" mode in the web application they have designed. If there is no crash, crashlytics will take anything. You can grab an exception in your code :
#try{
....
}
#catch(NSException ex){...}
in the critical parts, but you should always do that if you are afraid your application is going to crash or you find a potential error which can allow your application have a bad behavior and acting up. You can always force in your exception to send or track this error.
Hope it helps
The trick is to use the following :
http://support.crashlytics.com/knowledgebase/articles/202805-logging-caught-exceptions
Just use this :
Crashlytics.logException(new Exception("my custom log for crashLytics !!!"));
I use this and I get my non-fatal crash in about 5 minutes !

how do I debug a wait_fences error

wait_fences: failed to receive reply: 10004003
I continue to get this error. It happens when my app starts up. How would I debug this issue. Is there some way I can watch the wait_fences and see what is going on there. Is there some way to see what threads are blocking what ?
It feels like this error is nothing but a theading black hole with no tools or info getting into or coming out of the void.
Anyone with tips on debugging this error would be greatly appreciated.
New
I have now changed my threading, All threading calls go through this method set to do all their dispatches
NOTE: Also I wish you guys would not vote to close my question just because you have seen another question about this problem. There is no real information about this error. I need to know what causes this error and/or how to debug it. not the common "add the super calls to your viewDidAppear and the such. If those helped, I would not have asked this question.
+ (void) ensureDispatchOfBlock:(dispatch_block_t) block onQueue:(dispatch_queue_t) queue async:(BOOL) async{
if (dispatch_get_current_queue() == queue){
block();
}
else {
if (async){
dispatch_async(queue, block);
}
else {
dispatch_sync(queue, block);
}
}
}
+ (void) ensureDispatchOnMainThread:(dispatch_block_t) block async:(BOOL) async{
[self ensureDispatchOfBlock:block onQueue:dispatch_get_main_queue() async:async];
}
+ (BOOL) addBlock:(dispatch_block_t) block toQueue:(dispatch_queue_t) queue async:(BOOL) async {
if (!async && dispatch_get_current_queue() == queue){
return NO;
}
if (async){
dispatch_async(queue, block);
}
else {
dispatch_sync(queue, block);
}
return YES;
}
That error usually comes up as a result of trying to animate something that isn't visible on-screen. If it happens on launch, I'd guess that you're trying to start an animation in viewDidLoad.
I'd look for missing calls to super's view(Did/Will)Appear. If these are omitted you will see "wait fences", whatever that means.

Resources