Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

SimonBS

macrumors regular
Original poster
Dec 30, 2009
202
0
Hi,

I am developing an application which retrieves its data from a plist located on my webserver. Therefore the application is dependent on a network connection. I want the user to be able to use my application when offline and therefore I am saving a copy of the plist on the users device every time the application is loaded with network connection. From there, I read the data from the plist located on the device.

I am having troubles, though. I am starting the download of the data in didFinishLaunchingWithOptions method of the AppDelegate. This is done like this:

Code:
if(hasConnection) { // returns TRUE or FALSE based on Apple's example on checking network reachability
        NSLog(@"Starting download of data.");

        // loading using NSURLConnection
        NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:FETCH_URL] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];

        // create the connection with the request
        // and start loading the data
        NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
        if (theConnection) {
            // Create the NSMutableData to hold the received data.
            // receivedData is an instance variable declared elsewhere.
            receivedData = [[NSMutableData data] retain];
        }
    }

Then I add the data to my plist (Bands.plist) in connectionDidFinishLoading

Code:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    // throw data into a property list (plist)
    NSMutableArray *tmpArray = [NSPropertyListSerialization propertyListFromData:receivedData mutabilityOption:NSPropertyListMutableContainers format:nil errorDescription:nil];

    NSMutableArray *plistArray = [[NSMutableArray alloc] initWithArray:tmpArray];

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];

    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"Bands.plist"];

    [plistArray writeToFile:path atomically:YES];
    [plistArray release];

    // release the connection, and the data object
    [connection release];
    [receivedData release];
}

But when loading the application for the first time, it crashes. I believe this is due to the application trying to reach this data even though it is not yet saved. If I remove the part that tries to reach the data saved locally, I have no problem. Neither do I have a problem if I add it again and restart the application (second load).

Does anyone have an idea how to fix this issue?

It is as if my application tries to load and handle data which does not yet exist.
 
Without seeing more of the code, I can't answer what's wrong.

It seems pretty clear you know where it's crashing. If that's the case, just make darn sure you are checking that all your data is valid at that point before you try to use it. Otherwise, read up on how to use the debugger to set up some breakpoints, so you can see exactly where it is crashing.
 
Can you ship the app with a dummy plist that will satisfy it as existing for the first run.

B

Using a dummy seems to be working. I just check if Bands.plist is in the Documents directory, if not, then the "original" Bands.plist is copied to Documents.

I am experiencing another problem, though. This is related to the same question.

My problem is, that if the data has been updated and an old version of Bands.plist is stored on the device, then my application starts reading from Bands.plist while updating and the data becomes a mix of old and new data which causes the application to crash. I can't figure out how to fix this issue and hope that someone has an idea.

I have an image which illustrates my issue:

tableview_crash.PNG


What happens is, that my view has a subview which has a page controller. Each page contains a table view where the data is loaded into.

Here you see that I am sliding from one view to another. In the first view (the left) is the old data and in the second view (the right) is the new data. In this transition of views, the application crashes.

Can anyone help me figure this out? It seems that the application starts loading the data before it is saved.

Here is some code. Please say, if more code will help. I can't figure out how to fix this issue.

This is from the App Delegate. Here the data is retrieved from the webserver and is saved to Bands.plist.

Code:
NSLog(@"[AppDelegate] Data has been downloaded.");
    	
// throw data into a property list (plist)
NSMutableArray *tmpArray = [NSPropertyListSerialization propertyListFromData:receivedData mutabilityOption:NSPropertyListMutableContainers format:nil errorDescription:nil];
    
NSMutableArray *plistArray = [[NSMutableArray alloc] initWithArray:tmpArray];
    	
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
    	
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"Bands.plist"];
    	
NSLog(@"[AppDelegate] Bands.plist has been populated.");

This is from the table view. Here the data is loaded from Bands.plist.

Code:
pageNumber = page;
        		
NSLog(@"[TableView] Loading view.");
        			
NSArray *whatDays = [[NSArray alloc] initWithObjects:@"Onsdag", @"Torsdag", @"Fredag", @"Lørdag", @"Ikke programsat", nil];
        			
sections = [[NSMutableDictionary alloc] init];
        		
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"Bands.plist"];
tmpArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
        			
// scan through data and sort
for(int i = 0; i < [whatDays count]; i++) {
     NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF.weekDay == %@", [whatDays objectAtIndex:i]];
     NSArray *bandsThisDay = [tmpArray filteredArrayUsingPredicate:predicate];
     [sections setObject:bandsThisDay forKey:[whatDays objectAtIndex:i]];
}
        			
[whatDays release];

I hope that someone can help my fix this issue as I really can't figure out how to solve it.

Would it be possible to call a method in tableview implementation from the app delegate and tell it to populate the table? So it's waiting for the download to be done?
 
my application starts reading from Bands.plist while updating and the data

Can you post the console log for when that happens?

Are you actually seeing something like:

Code:
[AppDelegate] Data has been downloaded.
[TableView] Loading view.
[AppDelegate] Bands.plist has been populated.

B
 
Can you post the console log for when that happens?

Are you actually seeing something like:

Code:
[AppDelegate] Data has been downloaded.
[TableView] Loading view.
[AppDelegate] Bands.plist has been populated.

B

A successfull load is like this:

Code:
2011-03-10 17:26:20.907 NibeFestival[237:707] [AppDelegate] File does not exist.
2011-03-10 17:26:20.920 NibeFestival[237:707] /var/mobile/Applications/B3D56373-0885-401C-ACCA-86EA057EE816/NibeFestival.app/Bands.plist
2011-03-10 17:26:20.954 NibeFestival[237:707] [AppDelegate] Bands.plist has been copied to Documents.
2011-03-10 17:26:21.008 NibeFestival[237:707] [AppDelegate] Starting download of data.
2011-03-10 17:26:21.087 NibeFestival[237:707] Loading program view.
2011-03-10 17:26:21.092 NibeFestival[237:707] Initializing
2011-03-10 17:26:21.098 NibeFestival[237:707] [TableView] File exists!
2011-03-10 17:26:21.102 NibeFestival[237:707] [TableView] Loading view.
2011-03-10 17:26:21.149 NibeFestival[237:707] Initializing
2011-03-10 17:26:21.155 NibeFestival[237:707] [TableView] File exists!
2011-03-10 17:26:21.159 NibeFestival[237:707] [TableView] Loading view.
2011-03-10 17:26:21.612 NibeFestival[237:707] Reachability Flag Status: -R -----l- networkStatusForFlags
2011-03-10 17:26:21.613 NibeFestival[237:707] Has connection.
2011-03-10 17:26:21.615 NibeFestival[237:707] Reachability Flag Status: -R ------- networkStatusForFlags
2011-03-10 17:26:21.616 NibeFestival[237:707] Has connection.
2011-03-10 17:26:21.810 NibeFestival[237:707] [AppDelegate] Appending data.
2011-03-10 17:26:21.815 NibeFestival[237:707] [AppDelegate] Appending data.
2011-03-10 17:26:21.821 NibeFestival[237:707] [AppDelegate] Appending data.
2011-03-10 17:26:21.824 NibeFestival[237:707] [AppDelegate] Appending data.
2011-03-10 17:26:21.827 NibeFestival[237:707] [AppDelegate] Data has been downloaded.
2011-03-10 17:26:21.842 NibeFestival[237:707] [AppDelegate] Bands.plist has been populated.
2011-03-10 17:26:27.619 NibeFestival[237:707] Initializing
2011-03-10 17:26:27.624 NibeFestival[237:707] [TableView] File exists!
2011-03-10 17:26:27.625 NibeFestival[237:707] [TableView] Loading view.
2011-03-10 17:26:31.055 NibeFestival[237:707] Initializing
2011-03-10 17:26:31.058 NibeFestival[237:707] [TableView] File exists!
2011-03-10 17:26:31.060 NibeFestival[237:707] [TableView] Loading view.
2011-03-10 17:26:32.020 NibeFestival[237:707] Initializing
2011-03-10 17:26:32.024 NibeFestival[237:707] [TableView] File exists!
2011-03-10 17:26:32.026 NibeFestival[237:707] [TableView] Loading view.

Then I update the data and a crash looks like this:

Code:
2011-03-10 17:29:08.146 NibeFestival[257:707] [AppDelegate] File exists!
2011-03-10 17:29:08.196 NibeFestival[257:707] [AppDelegate] Starting download of data.
2011-03-10 17:29:08.243 NibeFestival[257:707] Loading program view.
2011-03-10 17:29:08.248 NibeFestival[257:707] Initializing
2011-03-10 17:29:08.253 NibeFestival[257:707] [TableView] File exists!
2011-03-10 17:29:08.257 NibeFestival[257:707] [TableView] Loading view.
2011-03-10 17:29:08.296 NibeFestival[257:707] Initializing
2011-03-10 17:29:08.301 NibeFestival[257:707] [TableView] File exists!
2011-03-10 17:29:08.305 NibeFestival[257:707] [TableView] Loading view.
2011-03-10 17:29:08.758 NibeFestival[257:707] Reachability Flag Status: -R -----l- networkStatusForFlags
2011-03-10 17:29:08.760 NibeFestival[257:707] Has connection.
2011-03-10 17:29:08.761 NibeFestival[257:707] Reachability Flag Status: -R ------- networkStatusForFlags
2011-03-10 17:29:08.762 NibeFestival[257:707] Has connection.
2011-03-10 17:29:08.934 NibeFestival[257:707] [AppDelegate] Appending data.
2011-03-10 17:29:08.939 NibeFestival[257:707] [AppDelegate] Data has been downloaded.
2011-03-10 17:29:08.942 NibeFestival[257:707] [AppDelegate] Bands.plist has been populated.
2011-03-10 17:29:19.056 NibeFestival[257:707] Initializing
2011-03-10 17:29:19.061 NibeFestival[257:707] [TableView] File exists!
2011-03-10 17:29:19.062 NibeFestival[257:707] [TableView] Loading view.
2011-03-10 17:29:20.805 NibeFestival[257:707] Initializing
2011-03-10 17:29:20.809 NibeFestival[257:707] [TableView] File exists!
2011-03-10 17:29:20.811 NibeFestival[257:707] [TableView] Loading view.
2011-03-10 17:29:20.835 NibeFestival[257:707] *** Assertion failure in -[UITableView _createPreparedCellForGlobalRow:withIndexPath:], /SourceCache/UIKit/UIKit-1448.89/UITableView.m:5678
2011-03-10 17:29:20.859 NibeFestival[257:707] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
*** Call stack at first throw:
(
	0   CoreFoundation                      0x345b164f __exceptionPreprocess + 114
	1   libobjc.A.dylib                     0x35586c5d objc_exception_throw + 24
	2   CoreFoundation                      0x345b1491 +[NSException raise:format:arguments:] + 68
	3   Foundation                          0x30dad573 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 62
	4   UIKit                               0x34d05a89 -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:withIndexPath:] + 672
	5   UIKit                               0x34d0576b -[UITableView(UITableViewInternal) _createPreparedCellForGlobalRow:] + 34
	6   UIKit                               0x34cfe0cd -[UITableView(_UITableViewPrivate) _updateVisibleCellsNow:] + 936
	7   UIKit                               0x34cfd27d -[UITableView layoutSubviews] + 140
	8   UIKit                               0x34ca95fb -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 26
	9   CoreFoundation                      0x3451ef03 -[NSObject(NSObject) performSelector:withObject:] + 22
	10  QuartzCore                          0x30286bb5 -[CALayer layoutSublayers] + 120
	11  QuartzCore                          0x3028696d CALayerLayoutIfNeeded + 184
	12  QuartzCore                          0x3028c1c5 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 212
	13  QuartzCore                          0x3028bfd7 _ZN2CA11Transaction6commitEv + 190
	14  QuartzCore                          0x30285055 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 56
	15  CoreFoundation                      0x34588a35 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 16
	16  CoreFoundation                      0x3458a465 __CFRunLoopDoObservers + 412
	17  CoreFoundation                      0x3458b75b __CFRunLoopRun + 854
	18  CoreFoundation                      0x3451bec3 CFRunLoopRunSpecific + 230
	19  CoreFoundation                      0x3451bdcb CFRunLoopRunInMode + 58
	20  GraphicsServices                    0x319bb41f GSEventRunModal + 114
	21  GraphicsServices                    0x319bb4cb GSEventRun + 62
	22  UIKit                               0x34cd2d69 -[UIApplication _run] + 404
	23  UIKit                               0x34cd0807 UIApplicationMain + 670
	24  NibeFestival                        0x000022f7 main + 70
	25  NibeFestival                        0x000022ac start + 40
)
terminate called after throwing an instance of 'NSException'
Program received signal:  “SIGABRT”.

When it says "[TableView] Loading view." is obviously everytime I change the view in the page view controller and has it loading the data for the view.

You see that in the third view (where the new data is) it says "Loading data" and crashes.
 
Related to this thread: https://forums.macrumors.com/threads/1113061/

Where are you releasing sections and tmpArray?

B

They are both released in the dealloc.

Code:
- (void)dealloc {
	[tableProgram release];
	[days release];
	[thisDay release];
	[B][tmpArray release];
	[sections release];[/B]
	[navController release];
	[headerImage release];
    [super dealloc];
}

I don't think this is a problem.
 
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
Review the code for your tableView:cellForRowAtIndexPath: method. Somehow you're not returning a cell in some circumstance.
 
2011-03-10 17:26:21.087 NibeFestival[237:707] Loading program view.
2011-03-10 17:26:21.092 NibeFestival[237:707] Initializing
2011-03-10 17:26:21.098 NibeFestival[237:707] [TableView] File exists!
2011-03-10 17:26:21.102 NibeFestival[237:707] [TableView] Loading view.
2011-03-10 17:26:21.149 NibeFestival[237:707] Initializing
2011-03-10 17:26:21.155 NibeFestival[237:707] [TableView] File exists!
2011-03-10 17:26:21.159 NibeFestival[237:707] [TableView] Loading view.
2011-03-10 17:26:21.612 NibeFestival[237:707] Reachability Flag Status: -R -----l- networkStatusForFlags
2011-03-10 17:26:21.613 NibeFestival[237:707] Has connection.
2011-03-10 17:26:21.615 NibeFestival[237:707] Reachability Flag Status: -R ------- networkStatusForFlags
2011-03-10 17:26:21.616 NibeFestival[237:707] Has connection.
2011-03-10 17:26:21.810 NibeFestival[237:707] [AppDelegate] Appending data.
2011-03-10 17:26:21.815 NibeFestival[237:707] [AppDelegate] Appending data.
2011-03-10 17:26:21.821 NibeFestival[237:707] [AppDelegate] Appending data.
2011-03-10 17:26:21.824 NibeFestival[237:707] [AppDelegate] Appending data.
2011-03-10 17:26:21.827 NibeFestival[237:707] [AppDelegate] Data has been downloaded.
2011-03-10 17:26:21.842 NibeFestival[237:707] [AppDelegate] Bands.plist has been populated.
2011-03-10 17:26:27.619 NibeFestival[237:707] Initializing
]2011-03-10 17:26:27.624 NibeFestival[237:707] [TableView] File exists!
2011-03-10 17:26:27.625 NibeFestival[237:707] [TableView] Loading view.
2011-03-10 17:26:31.055 NibeFestival[237:707] Initializing
2011-03-10 17:26:31.058 NibeFestival[237:707] [TableView] File exists!
2011-03-10 17:26:31.060 NibeFestival[237:707] [TableView] Loading view.
2011-03-10 17:26:32.020 NibeFestival[237:707] Initializing
2011-03-10 17:26:32.024 NibeFestival[237:707] [TableView] File exists!
2011-03-10 17:26:32.026 NibeFestival[237:707] [TableView] Loading view.

...
2011-03-10 17:29:08.146 NibeFestival[257:707] [AppDelegate] File exists!
2011-03-10 17:29:08.196 NibeFestival[257:707] [AppDelegate] Starting download of data.
2011-03-10 17:29:08.243 NibeFestival[257:707] Loading program view.
2011-03-10 17:29:08.248 NibeFestival[257:707] Initializing
2011-03-10 17:29:08.253 NibeFestival[257:707] [TableView] File exists!
2011-03-10 17:29:08.257 NibeFestival[257:707] [TableView] Loading view.
2011-03-10 17:29:08.296 NibeFestival[257:707] Initializing
2011-03-10 17:29:08.301 NibeFestival[257:707] [TableView] File exists!
2011-03-10 17:29:08.305 NibeFestival[257:707] [TableView] Loading view.
2011-03-10 17:29:08.758 NibeFestival[257:707] Reachability Flag Status: -R -----l- networkStatusForFlags
2011-03-10 17:29:08.760 NibeFestival[257:707] Has connection.
2011-03-10 17:29:08.761 NibeFestival[257:707] Reachability Flag Status: -R ------- networkStatusForFlags
2011-03-10 17:29:08.762 NibeFestival[257:707] Has connection.
2011-03-10 17:29:08.934 NibeFestival[257:707] [AppDelegate] Appending data.
2011-03-10 17:29:08.939 NibeFestival[257:707] [AppDelegate] Data has been downloaded.
2011-03-10 17:29:08.942 NibeFestival[257:707] [AppDelegate] Bands.plist has been populated.
2011-03-10 17:29:19.056 NibeFestival[257:707] Initializing
2011-03-10 17:29:19.061 NibeFestival[257:707] [TableView] File exists!
2011-03-10 17:29:19.062 NibeFestival[257:707] [TableView] Loading view.
2011-03-10 17:29:20.805 NibeFestival[257:707] Initializing
2011-03-10 17:29:20.809 NibeFestival[257:707] [TableView] File exists!
2011-03-10 17:29:20.811 NibeFestival[257:707] [TableView] Loading view.


I've hilited peculiar log outputs in red. Notice how they appear in pairs, only a few milliseconds apart.

The blue-hilited one is the lone exception. It doesn't have a time near the others, nor is there any doubling.

Doubling strikes me as potentially harmful, especially if there's no apparent reason for it, and the code path has side effects such as creating or mutating objects. Uncontrolled threaded access may prove especially harmful in such cases.

The doubled items being only a few milliseconds apart suggests some kind of race condition, or possibly multiple objects where only one should exist. Those are just guesses. It's difficult to diagnose or debug with only the code fragments posted.
 
I've hilited peculiar log outputs in red. Notice how they appear in pairs, only a few milliseconds apart.

The blue-hilited one is the lone exception. It doesn't have a time near the others, nor is there any doubling.

Doubling strikes me as potentially harmful, especially if there's no apparent reason for it, and the code path has side effects such as creating or mutating objects. Uncontrolled threaded access may prove especially harmful in such cases.

The doubled items being only a few milliseconds apart suggests some kind of race condition, or possibly multiple objects where only one should exist. Those are just guesses. It's difficult to diagnose or debug with only the code fragments posted.

You are right. That does indeed look.. Ugly. I really have to take a closer look on that. I'm sure it's my bad code. I'm quite new to Objective-C (and iOS development of course) and I think I just haven't figured out how to do it with less requests and optimize my code as much as possible. It is certainly something that I will have to look into!

Review the code for your tableView:cellForRowAtIndexPath: method. Somehow you're not returning a cell in some circumstance.

Thank you for pointing that out. Actually I had seen that but I didn't really give it a thought as I was pretty sure the issue was something else. It seems to (almost) work now, though. I rewrote the method and now it isn't crashing.

This leads me to another issue though. The first two (out of five table views in the page controller) are loaded when the app starts up. It's like Bands.plist is not updated yet at this time and therefore these two views shows old information while the three others are updated.

Does anyone have an idea how to fix this? To "pause" the loading for a bit or something similar? As it is now, people are actually never sure that the information they see is the latest.
 
This leads me to another issue though. The first two (out of five table views in the page controller) are loaded when the app starts up. It's like Bands.plist is not updated yet at this time and therefore these two views shows old information while the three others are updated.

Does anyone have an idea how to fix this? To "pause" the loading for a bit or something similar? As it is now, people are actually never sure that the information they see is the latest.
How about putting up a "Refreshing Data..." indicator (complete with UIActivityIndicatorView) as the new data gets loaded. Similar to Apple's "Loading..." message.
 
How about putting up a "Refreshing Data..." indicator (complete with UIActivityIndicatorView) as the new data gets loaded. Similar to Apple's "Loading..." message.

That's a good idea. So I imagine I would make a method in my table view controller which populates the table view and is called from the app delegate when the app delegate is done refreshing my property list.

Is it possible to call a method in another view from the app delegate? Honestly I thought I would just write the method, import the header file and call it like this
Code:
[myViewController myMethod];
but that does not seem to work.
 
Is it possible to call a method in another view from the app delegate? Honestly I thought I would just write the method, import the header file and call it like this
Code:
[myViewController myMethod];
but that does not seem to work.
Yes, it's possible. Doesn't seem to work how / why? Can you be more specific? Any errors / warnings? Does it crash? Etc. Plus, did you make myMethod public?
 
Yes, it's possible. Doesn't seem to work how / why? Can you be more specific? Any errors / warnings? Does it crash? Etc. Plus, did you make myMethod public?

My method is defined like this:

The header:
Code:
- (void)refreshTableView;

The implementation:
Code:
- (void)refreshTableView {
    NSLog(@"Amazing! This awesome method has been called.");
}

In my AppDelegate.m I have added #import "NewProgramTableViewController.h".
Then I try to call refreshTableView via [NewProgramTableViewController refreshTableView]; but I get the following warnings and if I try to run the app, then it crashes.

The two warnings are:
Code:
Method 'NewProgramTableViewController' not found (return type defaults to 'id')

Code:
'NewProgramTableViewController' may not respond to '+refreshTableView'

I have not done anything to make the method public. How is this done? I thought only variables could be public.
 
In my AppDelegate.m I have added #import "NewProgramTableViewController.h".
Then I try to call refreshTableView via [NewProgramTableViewController refreshTableView]; but I get the following warnings and if I try to run the app, then it crashes.
You've declared refreshTableView as an instance method but then try to call it as a class method; NewProgramTableViewController is a class name not a object instance. If you're not comfortable with how these are different, perhaps it's time to step away from the real coding and (re)learn the fundamentals of Objective-C programming.

I have not done anything to make the method public. How is this done? I thought only variables could be public.
By declaring the method in your header file, you've made it public.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.