NSUncaughtExceptionHandler when using Realm with Crashlytics - ios

I've got Fabric installed in my app, with Crashlytics enabled via a simple Fabric.with([Crashlytics.self]) call in AppDelegate. Everything was working great, until I pulled in Realm. I have a dead-simple function;
class func listObjects() {
let realm = try? Realm()
if let realm = realm {
let objSet = realm.objects(TestObject.self)
print("Retrieved \(objSet.count) objects")
}
}
Calling the function actually works just fine, but I get an odd warning;
[Crashlytics:Crash] Warning: NSUncaughtExceptionHandler is '_ZZ34RLMInstallUncaughtExceptionHandlervEN3$_08__invokeEP11NSException' in '<...>/Frameworks/Realm.framework/Realm'
Has anyone come across this before?

I guess this is happening because Crashlytics checks whether the uncaught exception handler is overridden, because it is relying on that itself, but it is commonly misused for purposes where there would be less dangerous solutions. 🐉
Realm is using this for good reasons: we need to tear down open write transactions. While we are doing that, we still ensure to call the previously configured exception handler as you can see here. So Crashlytics won't loose it's ability to report any exceptions in your app.

Related

Core Data migratePersistentStore crash with perform/performAndWait

I'm attempting to use NSPersistentStoreCoordinator.migratePersistentStore(_:to:options:withType:) to move a persistent store from one location to another, using an approach like this. (My overall goal is to create a backup, so I'm also drawing on the Ole Begemann's work here, which is in turn inspired by a previous SO post, but the problem I encounter is the same in both cases.)
let sourceStore = ... // NSPersistentStore
let backupFileURL = ... // URL
try self.migratePersistentStore(sourceStore, to: backupFileURL, options: [:], withType: NSSQLiteStoreType)
When calling migratePersistentStore, Core Data loads objects into memory, triggering a call to awakeFromFetch() on each one. That uses some memory, no surprise, but not necessarily a big deal in this case.
However, if awakeFromFetch() happens to do something that uses NSManagedObjectContext.perform or NSManagedObjectContext.performAndWait, the app crashes while moving the persistent store with an error like this one:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Can only use -performBlock: on an NSManagedObjectContext that was created with a queue.'
As far as I can tell, the crash occurs because during the move/migration, awakeFromFetch() is being called in an NSManagedObjectContext that uses the deprecated confinementConcurrencyType and calls to perform/performAndWait aren't permitted on with that concurrency type.
(Here's a super simple example awakeFromFetch() that I'm using as a demonstration:)
public override func awakeFromFetch() {
super.awakeFromFetch()
managedObjectContext?.perform {
print("Hello, world!")
}
}
I could theoretically work around this by checking the context's concurrency type and only wrap calls in perform/performAndWait if it's not a confinement context, but that seems both unwieldy and out of line with best practices.
Does anyone have a recommendation for how to handle this? Is there a way to prompt migratePersistentStore to use a different concurrency type?

Firestore document creation fails without an error

Edit3: Okay, it seems like it's an issue with Firebase, someone else tweeted about having the same issue. I also contacted support.
A piece of Swift code that handles creating documents suddenly stopped working. No errors are thrown, Firebase doesn't complain in the log and I can verify from the console that the document is not created, I can verify that the device has a healthy internet connection. I also disabled offline persistence for Firebase just to be sure.
When I try debugging it, the debugger jumps straight over the block that handles errors or successes, never running it (i.e. never finishing the Firestore request?).
Here is the code
func createConversation(){
let conversation : [String : Any] = ["owners" : [
UserProfile().getProfile().uid!],
"seeking" : true,
"timestamp" : Timestamp(date: Date())
]
var ref: DocumentReference? = nil
ref = DB().firestore().collection("Conversations").addDocument(data: conversation){ err in
if let err = err {
print("Error creating a convo: \(err)")
} else {
print("Conversation created with ID: \(ref!.documentID)")
StateMachine().action(a: .seekingStarted(ref!.documentID))
}
print("Conversation Creation finished")
}
let documentID = ref?.documentID
print(ref.debugDescription)
}
I'm not sure how to approach this issue, any ideas?
Edit: Okay, the issue is not limited to this block of code, it looks like Firebase is not communicating with the servers. I've waited for more than 5min for the addDocument to return(with error or success) but that never happened.
I noticed that at the initiation of the App BoringSSL complains a bit but this is not new and I don't have problems with the other Firebase services, they work just fine - reading and creating data with no problems.
Edit2: Apparently I can fetch collections and documents from Firestore, the issue seems to be limited to document/collection creation.
The document creation operation takes a little time, if you place the breakpoint inside the asynchronus completion block you will surely get an error or success.

How can I save the fatalError message to the iOS crash log?

I have an iOS application written in Swift 2 in Xcode 8.2.1, that's built for iOS 10.2.
I've had a number of crash reports from TestFlight and despite symbolication, none of the crash logs show any program state besides the stack-traces (no argument values, no locals, no heap objects, etc).
...but inside those functions I can see code which is likely to fail (e.g. a forced unwrap) but the crash log isn't telling me where or why it's failing.
When debugging in Xcode, I can use fatalError(message: String) where I can put my own message like "functionFoo returned nil" or "variable bar == \"" + bar + "\"", except when deployed using TestFlight or the App Store the fatalError will be hit and the program terminates, but the message value is not saved to the crash log, making it pointless.
In other environments, like C#/.NET and Java I can simply throw new SomeExceptionType("my message") and all information is available in whatever global catch(Exception) handler I have.
How can I achieve the same goal in iOS / Swift?
Swift does support error handling. You can create your own error type by confirming to Error protocol or use existing error types and then throw an error by invoking throw error.
But Swift forces you add error handling to any code that can throw an error. There are multiple way you can handle error in swift.
Apply throws keyword to your function, this indicates that the function can throw an error when invoked and the error should be handled by the caller.
func canThrowErrors() throws -> String
When invoking methods with throws keyword you have to add try keyword at the beginning of the invocation. All these try invocations should be handled either by applying throws to method to just propagate the errors or wrapping inside a do-catch block:
do {
try canThrowErrors()
try canThrowOtherErrors()
} catch is SpecificError {
// handling only specific error type
} catch let error as SpecificError {
// catches only specific error for type
} catch {
// catches all errors
}
Additionally you can use try? and try! for throwing function invocation to disable error propagation and retrieve optional result that returns nil in case of error and runtime assertions respectively.
By forcing you to handle all the errors at compile time swift avoids any undefined runtime behavior and debugging nightmare.
I would suggest to use fatalError or any other runtime assertion only if scenarios when there is no way to recover from a state without crashing the app. Unfortunately, there is no way to handle errors from fatalError as its use is only reserved for such scenarios only. Also, in your crashlog you will only get the line number that caused the crash to get additional info for the cause of crash I would suggest to use custom logging or analytics.

Setting cachePolicy of a parse query crashes application

I'm developing in Swift using the latest version of Parse from the website. I am attempting to set my cache policy to the NetworkElseCache value, which is displayed below:
let userRelation = User.currentUser()?.relationForKey("friends")
let userQuery = userRelation!.query()
userQuery.cachePolicy = .NetworkElseCache
userQuery.findObjectsInBackgroundWithBlock {
(users, error) -> Void in
print("Success")
}
The error occurs on the line:
userQuery.cachePolicy = .NetworkElseCache
and if the line is removed, the application runs fine, the error produced is:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'Method not allowed when Pinning is enabled.'
I really don't have any idea what to do from here, I only started iOS development about a week ago using Parse and Swift, so I'm a bit on the lost side. I don't understand the callstack either, or how it will help me find my problem.
Disable the LocalDataStore in your AppDelegate.m
So comment following line
Parse.enableLocalDatastore() // Comment this line and try
Here's the full explanation from the engineers at Parse. But yes, long story short, if you're using local datastore, you will not be able to also use different cache policies.

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