NSFetchedResultsController does not detect stale cache

Originator:ben.dolman
Number:rdar://17396603 Date Originated:20-Jun-2014 11:00 AM
Status:Open Resolved:
Product:iOS SDK Product Version:iOS 8.0 (12A4297e)
Classification: Reproducible:Always
 
Summary:
This bug is in both iOS 7 and iOS 8.

Beginning in iOS 7, Core Data enabled WAL mode on the SQLite database by default. But in determining if a cache file is valid, an NSFetchedResultsController cache only looks for date changes on the .sqlite file, not on the .sqlite-wal file.

In WAL mode, saved data is often written first to the .sqlite-wal file and only later flushed to the main .sqlite file. But this means that often NSFRC cache does not detect that there have been any changes to the database, so it assumes its cache is up-to-date. In this invalid state, if an object has been added to the DB it is not included in the NSFRC. If an object has been deleted from the DB the NSFRC still thinks it's there and an app will crash when it tries to access that object.

Steps to Reproduce:
Steps to use in the sample project:

1. Run the project on an iOS 7 or iOS 8 device
2. Enable WAL mode
3. Enable caching
4. Tap "Add Record"
5. Tap "Load TableView". Note the number of results.
6. Tap "Back" to unload the TableView and its NSFRC
7. Tap "Delete Record"
8. Tap "Load TableView".


Expected Results:
The table view would load and a record will be gone (because it was deleted).

Actual Results:
The app crashes because NSFRC still includes the deleted record in its results.

You can work around this with these additional steps:

9. Restart the project
10. Disable WAL mode
11. Perform steps 4-8. Note that there is no longer a crash.

Version:
iOS 8.0 (12A4297e)

Notes:
The workaround for this is to disable WAL mode by passing the following dictionary to the store options dictionary you pass to NSPersistentStoreCoordinator’s addPersistentStoreWithType:configuration:URL:options:error: method:

@{ NSSQLitePragmasOption : @{ @"journal_mode" : @"DELETE" } }

Configuration:
iPhone 4S 16GB

Attachments:
'FetchedResultsController.zip' was successfully uploaded.

Download it: https://dl.dropboxusercontent.com/u/8211174/FetchedResultsController.zip

Comments

Still exists in iOS 13.1

Slight typo in the fix, should be NSSQLitePragmasOption

Fixed, thanks!

By ben.dolman at June 23, 2014, 9:59 p.m. (reply...)

'FetchedResultsController.zip'

Hey, where can I download the example project?

By kaskaaddnb at June 21, 2014, 7:55 p.m. (reply...)

Project Download Link

I just added a link to it at the end of the description.

By ben.dolman at June 23, 2014, 2:59 p.m. (reply...)

*DEPRESSING*

Core Data used to be rock solid. It is slowly becoming a pile of untested, too complex APIs. The last additions in 10.10 or iOS8 in Core Data are both dangerous and not really useful.

Simplify Multi threading usage and ensure integrity is what we want.

thanks for this report

"Simplify Multi threading usage and ensure integrity is what we want."

They did that several releases ago. Unfortunately they did not update the Xcode templates or documentation, and many developers are still doing it the deprecated way.

Nope, I don't talk about the new performBlock* API. This is not simple enough.


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!