7

I am using Xcode 5 compiling exclusively for iOS 7.

I am reading data from a UTF8 txt file to populate a core data entity. After reading the data, I log it to the console. The data is there. I populate the entity and log the entity to the console. I save it. No error. No crash.

I see 3 files on the device:

  • MyDatabase.sqlite
  • MyDatabase.sqlite-shm
  • MyDatabase.sqlite-wal

When the app starts and MyDatabase.sqlite is created empty it has 40 kb. At this point the shm file is 32kb and the wal file is zero.

After I write the data to the database, the wal files grows to 1,7 Mb but the other two files keep their initial sizes or in other words, the data is not being saved to the database. I have confirmed that by inspecting the sqlite file with an external database viewer.

This is the code I am using:

// NSArray *arrayOfYears = ... 
// this contains an array of numbers read from the CSV file
// at this point the array contains numbers in text format

// NSArray *arrayOfBrands = ...
// this is an array of brands

for (int i=0; i<[arrayOfBrands count]; i++) {

            Cars *car = [NSEntityDescription insertNewObjectForEntityForName:@"Cars" inManagedObjectContext:context];

            car.brand = [arrayOfBrands objectAtIndex:i];
            car.year = [NSNumber numberWithInt:[[arrayOfYears objectAtIndex:i] integerValue]];

            NSError *error = nil;
            if (![context save:&error]) {
                // Handle the error.
                NSLog(@"error = %@", error);
            }
}

This produces no error or crash. It goes like it was saving. I have increased mySql debug level to 3 and this is what I see on console...

when the table is created empty

CoreData: annotation: Connecting to sqlite database file at "/var/mobile/Applications/BB22334C4-550A-4C44-B17A-3F02062EC687/Documents/MyDatabase.sqlite"
CoreData: annotation: creating schema.
CoreData: sql: pragma page_size=4096
CoreData: sql: pragma auto_vacuum=2
CoreData: sql: BEGIN EXCLUSIVE
CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_METADATA'
CoreData: sql: CREATE TABLE ZCARS ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZYEAR INTEGER, ZBRAND VARCHAR ) 
CoreData: annotation: Creating primary key table.
CoreData: sql: CREATE TABLE Z_PRIMARYKEY (Z_ENT INTEGER PRIMARY KEY, Z_NAME VARCHAR, Z_SUPER INTEGER, Z_MAX INTEGER)
CoreData: sql: INSERT INTO Z_PRIMARYKEY(Z_ENT, Z_NAME, Z_SUPER, Z_MAX) VALUES(1, 'Cars', 0, 0)
CoreData: sql: CREATE TABLE Z_METADATA (Z_VERSION INTEGER PRIMARY KEY, Z_UUID VARCHAR(255), Z_PLIST BLOB)
CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER WHERE TBL_NAME = 'Z_METADATA'
CoreData: sql: DELETE FROM Z_METADATA WHERE Z_VERSION = ?
CoreData: details: SQLite bind[0] = 1
CoreData: sql: INSERT INTO Z_METADATA (Z_VERSION, Z_UUID, Z_PLIST) VALUES (?, ?, ?)
CoreData: details: SQLite bind[0] = 1
CoreData: details: SQLite bind[1] = "C6E2268B-6792-4298-B292-5025E5BDE31A"
CoreData: details: SQLite bind[2] = <NSData len=455>
CoreData: annotation: Saving new meta data; version = 1 ; UUID = C6E2268B-6792-4298-B292-5025E5BDE31A
CoreData: sql: COMMIT
CoreData: sql: pragma journal_mode=wal
CoreData: sql: pragma journal_mode=wal
CoreData: sql: pragma cache_size=200
CoreData: sql: SELECT Z_VERSION, Z_UUID, Z_PLIST FROM Z_METADATA

after saving one brand/year

CoreData: sql: BEGIN EXCLUSIVE
CoreData: sql: SELECT Z_MAX FROM Z_PRIMARYKEY WHERE Z_ENT = ?
CoreData: annotation: getting max pk for entityID = 4
CoreData: sql: UPDATE Z_PRIMARYKEY SET Z_MAX = ? WHERE Z_ENT = ? AND Z_MAX = ?
CoreData: annotation: updating max pk for entityID = 4 with old = 0 and new = 1
CoreData: sql: COMMIT
CoreData: sql: BEGIN EXCLUSIVE
CoreData: sql: INSERT INTO ZTABULEIRO(Z_PK, Z_ENT, Z_OPT, ZYEAR, ZBRAND) VALUES(?, ?, ?, ?, ?)
CoreData: details: SQLite bind[0] = (int64)1
CoreData: details: SQLite bind[1] = (int64)4
CoreData: details: SQLite bind[2] = (int64)1
CoreData: details: SQLite bind[3] = "FORD"
CoreData: details: SQLite bind[4] = (int64)1989
CoreData: sql: COMMIT
CoreData: annotation: Changing objectID 0x14e71730 <x-coredata:///Cars/t38FA86EE-4124-4FD7-A8C0-8CE7BBAC73782> to 0x14e73ef0 <x-coredata://C6E3368B-6792-4298-B292-5025E5BDE31A/Cars/p1>
CoreData: sql: pragma page_count
CoreData: annotation: sql execution time: 0.0014s
CoreData: sql: pragma freelist_count
CoreData: annotation: sql execution time: 0.0016s

one strange thing I see here is the year variable like int64 when I have defined it like integer 16. Why it is using integer 64 is beyond me...

The data is not being saved at all. The data is there. If I log the arrays of data to console I see the data. If I log the entities populated before saving I see the data.

I am testing this on a device running iOS 7. Thanks.

What am I missing? How do I debug that?

Duck
  • 34,902
  • 47
  • 248
  • 470
  • 1
    How many NSManagedObjectContexts do you have? Is this particular context associated with a NSPersistentStoreCoordinator? – bneely Sep 19 '13 at 08:19
  • as far as I know, one. Why do you ask that? Is there a way to have more than one? for what purpose? – Duck Sep 19 '13 at 08:21
  • If you have multiple contexts in a parent-child relationships, saves only go one level up. Saves to the file are only triggered by saving the root context. If you only have one context, this doesn't apply. – bneely Sep 19 '13 at 08:23
  • 1
    What you have said is Klingon to me, but as far as I know I just have one context. – Duck Sep 19 '13 at 08:25
  • Can you show the code where you set up the persistent store? – bneely Sep 19 '13 at 08:26
  • Thanks. Andy Etheridge nailed the problem. Thanks anyway for your help. – Duck Sep 19 '13 at 08:39

1 Answers1

9

The data is being saved to the database if the write-ahead-log (wal) is increasing in size, See documentation here http://www.sqlite.org/draft/wal.html. What are you using to open the database file? You must use a tool that works with WAL journal mode. Rather than looking at the db files directly, try creating a new context and fetching your saved entities.

Andy Etheridge
  • 1,283
  • 9
  • 6
  • 2
    you are right! I was inspecting just the sqlite file independently. The moment I copied the other two files to the same directory and inspected the database again, the database shows populated. Now here is the big question of them all: when the app starts for the first time, it has to copy the sqlite file from the bundle to the documents directory. What do I have to do now? Copy all 3 files from the bundle to the documents directory? The database is now not just one but 3 files? wtf! Or in other words: do I have to include these 3 files in the bundle, right? – Duck Sep 19 '13 at 08:32
  • or another alternative: there is a way to disable this wal thing on core data or to commit the changes to the sqlite file? Thanks – Duck Sep 19 '13 at 08:48
  • Yes. See this post http://stackoverflow.com/questions/18870387/core-data-and-ios-7-different-behavior-of-persistent-store/18870738#18870738 – Andy Etheridge Sep 19 '13 at 08:57
  • 1
    Why do you have to move the files? Are you deploying with a pre-populated database? – Andy Etheridge Sep 19 '13 at 08:58
  • yes. Thanks, I have managed to disabled this journal thing and now the total database has 128 kb instead of 3.3 MB!!!!!!!!!!! – Duck Sep 19 '13 at 09:01
  • @RubberDuck could you please state how you managed to disable it? Thanks – user2512523 Nov 19 '13 at 02:12
  • @user2512523 - I don't have the project right now but was basically by disabling the journal mode: http://stackoverflow.com/a/18870738/316469 – Duck Nov 19 '13 at 07:26