I'm loading a rather simple XML file from a URL. I wanted to show the network activity indicator while the parser is loading the file, but obviously not while it is parsing.
Question: Does the initWithContentsOfUrl: method lock program execution while the document is loaded from the url? In other words, is the code below correct?
It seems obvious to me that this is okay, but I wanted to make 100% sure.
NSString* const urlString = #"...";
NSURL* url = [NSURL URLWithString:urlString];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
NSXMLParser* parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
This article explains it pretty well http://akosma.com/2010/05/28/initwithcontentsofurl-methods-considered-harmful/:
The main problem with these methods, of course, is the fact that they
are synchronous; this means that the thread executing them (usually
the UI thread) will block completely until they return, and in most
applications this means that you are de-facto blocking the whole
application for an unknown amount of time. This means that no buttons
or UI widgets will react to input, no navigation will be possible, no
touch events will be delivered or executed, nothing will happen at all
until the network operation completes.
Even worse; when using initWithContentsOfURL:, there is no timeout,
there is no meaningful feedback for network failures, and no way for
the user to cancel the current network operation. This last factor
justifies by itself not using initWithContentsOfURL: at all; you must
never ship code that leads to a bad user experience. Your users will
resent this and will complain!
If you want to display download progress, you will need to download the file yourself using something like NSURLConnection, then pass the local file path to the XML parser.
Yes, it does block. So your activity indocator won't be displayed.
Related
I'm trying to update PFObjects with server information, basically to discard local changes when user cancels actions, but I''m having a hard time. I'm not sure if I'm missing something or it's a bug, it has been a long programming session.
Here is a test code:
PFObject *object = [PFObject objectWithoutDataWithClassName:#"UserRecipes" objectId:#"1NOxhVZVXJ"];
object[#"instructions"] = #"TEST INSTRUCTIONS";
NSLog(#"%#", object[#"instructions"]);
[object fetch];
NSLog(#"%#", object[#"instructions"]);
I've tried with different objects and classes to make sure it isn't something related to acl. By the way, in one particular PFQueryTableViewController, when I change the object with the method above, no success, but when I leave the controller and enter again, forcing everything to be reloaded, the object is fetched with data from the server and local changes are gone.
UPDATE:
As I temporary solution, I'm using [PFObject revert] to discard changes and it works. But I would really prefer to perform tasks in background using fetchInBackground.
I was using ShareKit for a p long time, without downloading last updates from its repository.
Now i need some of its new features, so i've pulled all last commits and tried to run my app with new ShareKit without making any changes to my code (except some small changes in includes).
App compiles without errors but when i try to execute usual Facebook sharing code - it crashes without any useful error description.
old code i want to get to work
NSURL *url = [NSURL URLWithString:[NSString stringWithUTF8String:sk::game_services::get_app_store_url()]];
SHKItem *item = [SHKItem URL:url title:[NSString stringWithUTF8String:message] contentType:SHKURLContentTypeWebpage];
[SHKFacebook shareItem:item];
and according to updated ShareKit demo app and its documentation - this code should still work.
but it doesn't
it horribly crashes in currentIndicator method of SHKActivityIndicator inside macro DEFINE_SHARED_INSTANCE_USING_BLOCK that contains dispatch_once call.
debugger just says "SIGABRT" and thats all.
i don't know what to do.
First of all I'm using MagicalRecord to manage my Core Data. So right now my database layer works just great and it saves changes every time the change occurs. For example: I'm adding a new entry to a table and it gets written to db file stored on my hard disc right away. What I want to achieve is to keep all the changes in-memory, and write them to db file only on "save" command click.
I figured that the call that does the db file writing is:
[managedObjectContext MR_saveToPersistentStoreAndWait];
So as I figured, I can do all my modifications without calling that method, and then on "save" click, call that method. However, it works only if the thread wasn't changed. Every time, the thread changes the ManagedObjectContext gets reset or recreated, and I lose all my data.
EDIT: just found method in Magical Record:
[NSPersistentStoreCoordinator MR_coordinatorWithInMemoryStore];
which is what I need for the first part of my problem. Although, I don't know how to change from that type of the coordinator to
[NSPersistentStoreCoordinator MR_coordinatorWithSqliteStoreNamed:objectModelName];
without losing any data.
Does anyone know how to do this right?! Any kind of help is really appreciated!
Check these options:
1- There is setupCoreDataStackWithInMemoryStore which might be helpful if you want to run everything in memory.
2-You can save in the background:
[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext){
}];
3-To get the default context or the one for the thread
[NSManagedObjectContext MR_defaultContext];
[NSManagedObjectContext MR_contextForCurrentThread];
I figured it out by doing serious research. I need to use
NSPersistentStoreCoordinator *coordinator = [NSPersistentStoreCoordinator MR_coordinatorWithInMemoryStore];
to have my data "hang" before user clicks on save, where I do the following:
// psc - my current persistentStoreCoordinator; urlForStore - place where I'm gonna store SQLite
[psc migratePersistentStore:[psc persistentStores][0] toURL:urlForStore options:nil withType:NSSQLiteStoreType error:&error];
I am using the following code to populate a webView in my Mac app...
NSURL *url = [NSURL URLWithString:#"https://joeworkman.net"];
[self setMainFrameURL:[url absoluteString]];
I would like to be able to capture when there are errors loading this URL and then instead load a local HTML file stored within my app. This way the users gets presented with a presentable error instead of an ugly 500 server error (if that was the case). Not to mention that most users would not know what that meant anyhow.
I am making an iPad app where you can download files (like PDF, doc, etc) and view them offline.
I already have the view part and you can download a file to the document directory.
As it is now you need to wait for the download to be finished to move on.
This can be solved by putting it in a thread, but what happens when the user downloads multiple files or even download the same file multiple times?
My idea is to make a download queue, with a view for the progress.
Workflow:
The user opens a document and press download, the user gets a message that the download is started and can be viewed in the offline documents view.
The user downloads 3 more documents.
When the user goes to the offline document view the user sees a table view with 4 filled cells. 2 documents are done loading and 2 other are still downloading because there is a download/status bar shown in the table view cell.
The downloaded documents can be viewed or deleted.
The downloads in progress can not be watched (yet) but can be cancelled.
I want to make a threaded download class where you can add urls to be downloaded. the class has methods to cancel and delete document-downloads, but also has methods to return the progress.
If possible the class can handle simultaneous downloads.
The problem is, I don't know where to start?
NSURLConnection is already asynchronous. All you need to do is to create NSURLConnection instances, associate them with your data structures, and have at it.
Here's an example where I assume you have one UIView per item. If you use a table view you can't count on view instances, but instead associate a download with an NSIndexPath, or something else.
#implementation MyDownloadView
- (void)startDownload {
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:myURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10];
[req setHTTPMethod:#"GET"];
// Set headers etc. if you need
[[[NSURLConnection alloc] initWithRequest:req delegate:self] autorelease];
[req release];
self.responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Alternatively, store to a file so you don't run out of memory
[self.responseData appendData:data];
}
#end
Then implement the other NSURLConnection delegate methods to do what you need.
I've written an open source example that has pretty much all features you want, canceling a download is currently only available in code, but it's pretty easy to add a button for that.
I'm using asi-http-request for managing the downloads, and they are displayed in a grid view (AQGridView) instead of a UITableView, but i think you get the idea.
Download progress is managed via KVO.
See PSPDFDownload.m for a start. Download the full demo here
Full disclosure: This demo uses PSPDFKit for faster pdf display. But the Kiosk example is exactly what you need, and you don't need to use PSPDFKit for pdf display. There's even an example code path that uses Apple's QuickLook.