mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-11 20:00:30 +01:00
fetchExisting() crashes the app when object is not found #90
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @mkadan on GitHub (Sep 29, 2016).
please check DataStack+Querying.swift and BaseDataTransaction+Querying.swift
Four fetchExisting() methods there have the "as!" cast, e.g.:
This cast crashes the app if nil is casted (that may happen if the object is not available in the context), and this crash if not catched, as this is a fatal crash.
Why not use "as?" instead?
A bit another, though related issue is if the object was never saved, then the app crashes with:
In my opinion it should not crash and just return nil.
Could easily be replacing it with this in singe object fetchExisting() methods:
and with this in multi object fetchExisting() methods:
Here I attach fixed sources that resolve these two issues:
fixed-sources.zip
@JohnEstropia commented on GitHub (Sep 29, 2016):
@mkadan The documentation you are referring to is for Objective-C
In Objective-C, errors are returned from methods by returning
niland populating theerrorparameter:In Swift, this method does not return an optional, and so all
nilreturns will be bridged by swift as athrow:existingObject()'s documentation:https://developer.apple.com/reference/coredata/nsmanagedobjectcontext/1506686-existingobject
The
as! Tcasting done by CoreStore just casts the non-optionalNSManagedObjecttoT.None of CoreStore's
fetchExisting()methods accepts an optional. So if you are passingnilthere then Swift has nothing else to do but crash. I am guessing that anilvalue from your Objective-C code is getting through Swift bridged code, so you should start to debug where that happens.@mkadan commented on GitHub (Sep 29, 2016):
Ok, I see your point for the issue N1, and if it works like this, that's ok for me.
But please check this sample code for issue N2, it crashes, though in my opinion it should not:
It may happen not only if the instance was just created but also when it was deleted, like it happens in our app.
@JohnEstropia commented on GitHub (Sep 29, 2016):
You can't create
NSManagedObject's directly through their default initializer. That object won't have a parentNSManagedObjectContext. Use thetransaction's create method for this:@mkadan commented on GitHub (Sep 29, 2016):
Ok thanks for your explanation. I tried to create a sample based on our code that fails, but seems it's not that easy to do, as it involves cascaded deletes. I'll provide it later when I extract the problem, of course if that's really a problem in CoreStore.
@mkadan commented on GitHub (Sep 29, 2016):
I was able to reproduce the problem on sample project:
Company has ToMany-DeleteCascade relation to User, User has ToOne-NullifyCascade relation to Company.
using
in fetchExisting() implementation fixes this crash
@JohnEstropia commented on GitHub (Sep 29, 2016):
Thats expected behavior. You accessed an object that is supposedly deleted. My guess is that user.community is supposed to return nil, but the Swift compiler cannot infer that for you during compile time, so you end up with a broken 0-object. In any case, CoreStore can't protect you from accessing deleted objects directly like this, as that is Core Data's internal behavior
@mkadan commented on GitHub (Sep 29, 2016):
But is it possible to add this check to CoreStore just as a workaround for CoreData bug?
@JohnEstropia commented on GitHub (Sep 29, 2016):
That's not exactly a Core Data bug. It is the responsibility of the app to design an architecture so this doesn't happen. If anything, CoreStore should be guarding against this with an
assert(!object.isDeleted), because tolerating this will most likely just cause another problem somewhere else. On iOS 8 and below, your code will probably already crash on the line(iOS 9's new
shouldDeleteInaccessibleFaultsjust keeps the object up for a while)In any case, I suggest that this be handled on the app side.
@mkadan commented on GitHub (Sep 29, 2016):
Thanks for your answer. I think we will try to review our code to exclude such cases from it, and if it is not possible for some reason, will write an extension method to CoreStore transaction to perform this check outside of the core library functionality and test it carefully for our app, as you have a point that it may break something else inside the CoreStore.
From your side, is it possible you include this assert with some reasonable message, so that users may know what actually caused the crash?
@JohnEstropia commented on GitHub (Sep 29, 2016):
We could do that, but iOS 9 should already be giving you a warning log when this happens. Also, on iOS 8 your app will be crashing with the original exception anyway.
@JohnEstropia commented on GitHub (Sep 29, 2016):
I'll experiment for a while with that
assert(). If it changes the current behavior of fetchExisting(), I might not put it in (example, if there are previous cases whereisDeletedis marked but goes throughfetchExisting()returning nil, adding anassert()will be a breaking change for those use cases)