So I'm writing a function that authenticates against an API that returns an XML document packed with things I'd like to pass. The function can make a synchronous call with NSURLConnection's sendSynchronousRequest method, quite easily. Yet, once this method returns, I need to instantiate an instance of NSXMLParser, initiate it with the data, let it parse, etc, etc.
What I'd like to do, is in my function (after sendSynchronousRequest returns), instantiate NSXMLParser, give it the data to parse, and in a single method call, get it to start the parse, and not return until the parse is over.
For example:
- (void) myFunction
{
/* other code */
NSData *returnedData = [NSURLConnection sendSynchronousRequest:authRequest returningResponse:&authResponse error:&authError];
MyParserClass *parser = [[MyParserClass alloc] initWithData:returnedData];
[parser parseData];
}
In the above example, MyParserClass is a NSXMLParser delegate, and does all the parsing in the usual, delegated fashion. parseData will start the parse, and not return until it is over.
In what manner should I write parseData? A block-approach?
EDIT: I just remembered the -parse function in NSXMLParser is synchronous. Still, i'd love to know if anyone could explain a way to do this in asynchronous classes of any sort.
I'd recommend writing a class that implements the NSXMLParserDelegate protocol (for example, MyParserDelegate). When the delegate methods get called by the parser, it should build up a data structure with the information you want to extract from the XML file. I'd call it as follows:
// create the delegate
MyParserDelegate *delegate = [[MyParserDelegate alloc] init];
// create the parser
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:returnedData];
[parser setDelegate:delegate];
// parse the file
if ([parser parse])
{
// the XML was valid and parsed without error
// now, do something with the data you extracted
// e.g., self.xmlData = [delegate xmlBasedDataStructureBuiltByDelegate];
}
else
{
// the XML was invalid
// handle the error
}
The functionality of MyParserDelegate depends entirely on what you want to extract out of the XML file. I'd recommend reading Apple's Event-Driven XML Programming Guide for more information.
Remember, if you want to use a synchronous NSURLConnection, you should do this on a background thread.
Related
I have a DataManager class that is responsible for fetching data and handing it over to the DatabaseManager, which in-turn will insert it into core data.
The method that exposes itself to the webservice is below
-(void)fetchDetailsForId:(NSString *)userId withFieldInformation:(NSString *)fieldInfo
{
if (SessionIsActive) {
[APIRequester startWithRequest:[NSString stringWithFormat:#"%#?%#",userId, fieldInfo]
completionHandler:^(APIConnectionManager *connection, id user, NSError *error) {
//This is where the results are returned and the API manages its own threads and returns the result lazily
}];
}
}
The above method is within the DataManager class. Now, a few methods in the same class call the above method to get data from the server. This fetched data is then forwarded to the DatabaseManager for inserting into core data. A sample of this is
-(void)fetchCurrentDataForLoggedInUser
{
NSData *fetchedData = [self fetchDetailsForId:loggedInId withFieldInformation:#"all"];
//the fetchedData is then forwarded to DatabaseManager
}
Now, since the web method (first method) gets the data in the background thread (managed by the API), the value of the "fetchedData" in the above method will be null since the web method exits before the API gets the relevant data.
Can someone tell me the most recommended way of handling a situation like this? I am not asking for sample code or anything, just the right direction should be enough. I am looking for a permanent solution than a hack or easy workaround.
Thank you
Make a property for NSData says fetchedData. in .h and synthesize it in .m
then set this property after date fetch then call the method which is required. There are some delegate which tells you data data loading is done so at there you can set your property an from background you can call UI updating method on main thread that gives you your desired result.
you can use selector like this for calling on main thread
[self performSelectorOnMainThread:#selector(rollBar:)
withObject:nil
waitUntilDone:false];
I have a class, which has a delegate based system for sending different type of requests. it uses delegate to tell the object when the request is complete and also if it was a success o an error.
Now, I also have to check what type of request was it in response to take appropriate action.
I have wrapper class that should give me a block based interface for the same.
I pass a completion-block and an error-block to a request method which should internally use this delegate based class.
And when the response comes, should automatically call the appropriate handler for that request type and depending on success and error as well.
I saw a similar question on SO, but it was a little unclear to me, So please give a general idea of how to go about it instead of marking it as duplicate straight away.
Here is one way to do it. Use this RAExpendable class to dynamically build a delegate with a block based implementation.
Let's say your delegate is:
#protocol XDelegate
-(void) foo:(id)response;
#end
Add RAExpendable.h, RAExpendable.m from https://github.com/evadne/RAExpendable to your project. Dynamically add the delegate method:
RAExpendable *expendable = [RAExpendable new];
[expendable addMethodForSelector:#selector(foo:) types:"v#:#" block:^(id x, SEL sel, id response){
NSLog(#"response is %#", response);
}];
And set the expendable class as your delegate:
someObject.delegate = expendable;
Now, if you do this:
[expendable performSelector:#selector(foo:) withObject:#"OK"];
You get the string response is OK. Replace NSLog with whatever success/failure implementation you see fit. From now on, when you call foo:, the block executes instead.
If you want to modify this for your use case, note that the parameters for this example were v#:#, which according to the Type Encoding guide of the runtime means: void return, self, SEL, object. self and SEL are the two hidden parameters present on every Objective-C methods, the third parameter is the first non hidden parameter of the method. The signature of the block has to match the signature of the method.
With REKit, you can make a delegate dynamically like below:
id dynamicDelegate;
dynamicDelegate = [[NSObject alloc] init];
[dynamicDelegate respondsToSelector:#selector(foo:) withKey:nil usingBlock:^(id receiver, id response) {
NSLog(#"response is %#", response);
}];
someObject.delegate = dynamicDelegate;
Is there a way to transfer object from one application to another? I mean something like that:
//FirstApp:
- (void)someFunction{
myClass *myObj = [[myClass alloc] init]
//do some work...
[self runSecondAppWithObj:myObj];
}
//SecondApp:
int main (int argc, const char * argv[])
{
#autoreleasepool {
myClass *obj = //get obj from firstApp
//do some another work...
}
return 0;
}
myClass not so simple, what is why I can't transfer all info with NSString -> char. Also I need exactly the first object in second app, and not its duplicate.
I imagine it like:
first app launches second app, after that first app talk to second: "Hey, #adr is the address to variable(pointer), go and take it". After, second app take variable(pointer) with it's address. But i don't know how to make it.
There are two options that immediately come to my mind for doing this (I would recommend implementing the first option):
Make myClass serializable: The basic idea here is that you implement the NSCoding protocol and then use NSKeyedArchiver for serializing it into an NSDictionary. The dictionary can then easily be converted to an interchangeable format, e.g. a plist (XML). You would then pass the XML text to your second application and deserialize it there using NSDictionary and NSKeyedUnarchiver. You can find good guides and examples about archiving and serialization in Cocoa in Apple's developer documentation.
Pass the object using some inter process communication mechanism: This is a bit more complex and I would rather recommend it for apps that talk to each other continuously – not for just passing an object to another app on launch. One way to do this is using distributed objects. This enables you to share objects between processes or even between different physical machines as if they were local (with some limitations.) For example to share the object in your first app you might do:
NSConnection *connection = [[NSConnection connectionWithReceivePort:[NSPort port]] sendPort:nil];
[connection setRootObject:theObject];
[connection registerName:#"com.example.whatever"];
And in the second app you would then call:
id theObject = (id)[NSConnection rootProxyForConnectionWithRegisteredName:#"com.example.whatever" host:nil];
[theObject someMethod];
This blog offers a nice solution for handling multiple NSURLConnections: make a custom "CustomURLConnection" class that has an additional tag property.
http://blog.emmerinc.be/index.php/2009/03/02/custom-nsurlconnection-class-with-tag/
http://blog.emmerinc.be/index.php/2009/03/15/multiple-async-nsurlconnections-example/
Basically, he has simply added a tag property to the exsisting NSURLConnection:
CustomURLConnection.m
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString*)tag {
self = [super initWithRequest:request delegate:delegate startImmediately:startImmediately];
if (self) {
self.tag = tag;
}
return self;
}
then, later in the normal NSURLConnection loading methods, you can do:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//Log the connection’s tag
CustomURLConnection *ttttag = (CustomURLConnection *)connection; // **HERE**
NSLog(#”%#”, ttttag.tag);
NSMutableData *dataForConnection = [self dataForConnection:(CustomURLConnection*)connection];
[connection release];
}
So, that's where I'm having trouble. The way I see it, this is how things go:
I create a "connection+tag"
The first code snippet I posted above creates a regular "connection" (no tag), which will eventually call the the normal NSURLConnection methods like connectionDidFinishLoading. What happens to the tag at this point?
In the connectionDidFinishLoading method I'm able to cast the connection back into a "connection+tag", then find that missing tag information that had been discarded. How?
Maybe I'm just confusing myself, but it seems as if the tag was discarded when it starts down the normal NSURLConnection path. But then by casting it as the subclass, I'm again able to recover the tag property. Where did it live/go in the mean time?
Could someone with a better understanding of inheritance explain this to me?
With this code:
[[CustomURLConnection alloc] initWithRequest:... delegate:... startImmediately:... startImmediately tag:...];
you create an instance of CustomURLConnection. Now here is where your understanding is wrong: this CustomURLConnection object can freely call all methods of its superclasses but it will always remain a CustomURLConnection. The tag is always there.
The methods that are defined in the superclass such as initWithRequest:delegate:startImmediately: don't know about the tag but they don't have to, either. When the delegate method gets called:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
the connection argument is the very same CustomURLConnection that you created yourself above. The type in the method signature is different but that doesn't matter; because you know that this connection is of the CustomURLConnection type, you can just cast the connection object to the correct type and access the new property. But even if you wouldn't do that, the tag would still be there all the time.
I'm not sure what you mean by:
The first code snippet I posted above creates a regular "connection" (no tag).
What you've done here is create a subclass of NSURLConnection. Anywhere you can use the latter, you can use the former. NSURLConnection* means "a pointer to an NSURLConnection* or a subclass of it." So the original object you created was a CustomURLConnection and it included an extra ivar. That ivar doesn't disappear just because intermediary users refer to it by its superclass.
In order to use asynchronous http requests in objective c, you need to set a delegate to NSURLConnection. The problem is that I need to make multiple http requests, so having the same delegate (self) wont work.
What is the best way to go about this? Should I make a new delegate class for each http request? Are these delegates just NSObjects?
You have a few options. The two most most common are:
Make a new class for each connection (yes, a subclass of NSObject) and set them as delegates -- have them carry out whatever logic you need when the data is loaded
Set one class as the delegate and store references to all of your NSURLConnections. That way, when your delegate gets - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data called, you can test which NSURLConnection is being used (eg if ([connection == myConnection]) -- or whichever delegate method you're implementing)
What I do is make a class that will handle downloading a file, and notify me when it is done through a selector. I pass it a delegate, a selector and the Info it needs to perform the download.
- (void) downloadFileFrom:(NSString*) httpLocation respondAt:(SEL)selector on:(id)target withParam:(id)param
{
self.finishSelector = selector;
self.delegate = target;
self.responseParams = param;
}
the Class is its own NSURLConnection delegate. Therefore the instance is separated from the others that I may instantiate, And it handles creating its own result for me to work with. I hold onto the param object. which could be anything.
At the end of the download it does a performSelector: on the delegate. passing itself to the delegate.
if ([self.target respondsToSelector:self.selector])
{
[self.target performSelector:self.selector withObject:self.param];
}
then you can create an instance of the downloader and call your method... telling it where to reply to you.
MyDownloader downloader = [[MyDownloader alloc] init];
[downloader downloadFileFrom:#"http://www.mydomain.com/myimage" respondAt:#selector(myFileIsComplete:) on:self withParam: downloader];
[downloader autorelease];
another option is to create a #protocol for your class to respond at, and have you delegate conform to the responder.
That should work, but there is another option to consider. You could make a generic class that creates and calls the NSURLConnection provided they are common enough. Then keep an NSArray or NSDictionary of the classes. One for each connection.
Example: I have an app that needs to download several photos simultaneously. Therefore, I have a GetFlickrPhoto class. It has a custom init method that receives the URL and any other necessary info. Each individual class creates the NSURLConnection and can safely set the delegate to self
This helps keep things contained and very manageable/reusable.
To take it a step further:
The app I mentioned before, also needed to download JSON feeds. So I made a GenericDownload class that took in URL and asynchronously downloaded the NSData and then returned the NSData to the calling delegate via defined success/failure protocols. It didn't care what the NSData contained.
I remodeled GetFlickrPhoto to call GenericDownload and use the returned NSData for a photo. I then made a GetJSON class that also called GenericDownload and parsed the returned NSData into a JSON feed.
Takes a bit more time but in the end you will be glad for maintenance and future projects.