Crashlytics logging of Swift Try-Catch - ios

I'm facing a weird issue where Crashlytics is logging caught exceptions from within a try-catch block.
Here's some sample code:
do {
try managedContext.save() // Exception!!!
} catch let error as NSError {
error = error
print("Could not save \(error), \(error?.userInfo)")
}
The try-catch block is supposed to prevent crashes, and yet this line is logged by Crashlytics as an issue (and NOT a non-fatal). How is this possible?
Some background info:
The managedContext object is of type NSManagedObjectContext, which is initialized by the AppDelegate (written in Objective-C).
I'm using the latest version of Fabric - 2.3.0
What's going on?

Related

EXC_BAD_ACCESS error in swift with getting data from firebase

I am connecting to Firestore from iOS.
But when I ran this code it gave me a EXC_BAD_ACCESS error.
db.collection("chats").document(code).setData(["users":[Constants.User.currentUID!]]) { (err) in
if err != nil{
print("error registering with firebase")
}
else{
//...
}
}
So I went to the scheme editor and enabled the Address Sanitizer.
The error I got was highlighting code and said:
Thread 1: Use of deallocated memory
When I click on code it does show its correct value of QAWQ
What do I do?
EDIT
Under further investigation it is wherever the variable code is used

Swift compiler error while adding Core Data to existing project

I'm trying to add Core Data to my existing project. I've created a Core Data model and I've added some codes to the AppDelegate but I'm getting this error.
"lazy' may not be used on an already-lazy glob"
I copied this from a empty project and then I try to fix this error it says on my project
Value of type 'AppDelegate' has no member 'persistentContainer'
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "IOS_Swift_CoreData")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
The code you are using should be like this. i.e. inside AppDelegate curly braces.
You need to import CoreData as well.
import UIKit
import Coredata
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
// Your AppDelegate code here...
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "CoreDataTest")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}

Coredata concurrency issues

I have a NSOperation subclass that has its private context, and a singleton data manager class that has a context on main queue. All the UI and crud operation are done by this singleton class and a background fetch from cloud kit is done by the NSOperation subclass. Had few doubts as below.
Following is the code i have in NSoperation subclass.Can below code create deadlock?
self.localStoreMOC?.performBlockAndWait({ () -> Void in
//Long process of fetching data from cloud and pushing changes to cloud happens here.
var error:NSErrorPointer = nil
if self.localStoreMOC!.hasChanges
{
do
{
try self.localStoreMOC!.save()
}
catch let error1 as NSError
{
error.memory = error1
}
if error == nil
{
self.localStoreMOC!.parentContext!.performBlockAndWait({
do
{
try self.localStoreMOC!.parentContext!.save()
}
catch let error1 as NSError
{
print("wasSuccessful error1 \(error1)")
}
})
}
}
}
If i have a another singleton class using this class NSManagedOBject do i need to pass them though ID ?
First, you need to turn on -com.apple.CoreData.ConcurrencyDebug 1 in your run time arguments. That will help insure you are calling everything on the proper thread/queue.
Second, you are doing a lot of forced unwrapping of optionals, that is a very bad habit to be in. Best to unwrap them properly or use the optional unwrapping.
Third, what happens when you pause the debugger? Where is the line of code that it is pausing on and what queues are you on?
Just turning on the concurrency debug will most likely show you your issue.
Item 2
If you are wanting to pass a reference to a NSManagedObject from one context to another then yes, you need to use the NSManagedObjectID as the NSManagedObject is not safe to pass between contexts.
Code Formatting
Was playing with the formatting a bit, the results may be of interest to you:
guard let local = localStoreMOC else { fatalError("Local store is nil") }
guard let parent = local.parentContext else { fatalError("Parent store is nil") }
local.performBlockAndWait {
//Long process of fetching data from cloud and pushing changes to cloud happens here.
if !local.hasChanges { return }
do {
try local.save()
parent.performBlockAndWait {
do {
try parent.save()
} catch {
print("wasSuccessful error1 \(error)")
}
}
} catch {
print("Failed to save local: \(error)")
}
}
This removes the forced unwrapping of optionals and prints both errors out if you get one in either case.
Update
Also, some developers, say that nested performblockandwait like above will cause deadlock.
performBlockAndWait will never cause a deadlock. It is more intelligent than that.
If you are going from one queue to another, then you want each queue to block and wait like the code describes.
If you were on the right queue and called performBlockAndWait then the call would effectively be a no-op and will NOT deadlock

Core Data Concurrency on iOS 9

If I create a NSManagedObjectContext on the main thread with NSMainQueueConcurrencyType must I use performBlock() method for all the save and performFetch methods. IE is it possible to do the following:
do {
managedObjectContext.save()
} catch let error as NSError {
print(error)
}
or should I always do this:
managedObjectContext.performBlock({
do {
managedObjectContext.save()
} catch let error as NSError {
print(error)
}
})
If I understand the documentation correctly I always have to use performBlock() or performBlockAndWait() but in the template code from XCode 7 they are not using blocks. Any help is appreciated!
If you are already on the main thread and have a NSMainQueueConcurrencyType context, you do not need to use performBlock.

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 !

Resources