--UPDATE: I've decided to give AFNetworking a try. Even though RestKit has a really nice object mapping functionality, the way the networking calls were designed have made some things difficult for us.
I'm hoping for some advice on how to organize my project that's using RestKit.
I have several REST calls from a repository class and its results get passed to controllers. For example I have a getProfile method in the repository class that is getting called from our browse view controller. The view controller is set as the delegate to the get profile calls while the repository class is set to the delegate for the restkit calls.
The problem is if the browse controller makes several get profile requests, it is difficult to distinguish which result should go to which delegate function since all the restkit calls share the same delegate method objectLoaderDidFinishLoading. I then have 4 delegates that I have to match the results of the 4 asynchronous restkit requests.
Is there any way I can use blocks so that I can pass a function to execute as the asnynrhounous result comes back so that I can assign a proper delegate? The block support that I saw allowed a block to be used before the request was sent out in rest kit but I am interested in using it for when the asynchronous result is returned.
The alternative of examining the results or setting user data and sleuthing what delegate goes with what asynchronous results seems unreliable and bulky.
You can solve your issues with disambiguating between your profile requests by using the userData opaque pointer on RKObjectLoader. That will allow you to hang any object you want on the request, which can then be used to help distinguish between multiple profile requests. Also, if those profile requests are going to different resourcePaths then you can just use the wasSentToResourcePath: method on RKObjectLoader to distinguish between them.
I just stumbled upon this question while trying to figure out this problem for my own REST interface. I'm glad I did, I'll probably use RestKit now.
I digress, back to your question. As you noted it doesn't seem like the block argument in the RKObjectManager is meant to be used this way. Instead, how about writing a class that implements RKObjectLoaderDelegate, takes in a block, and calls that block on any of the delegate calls.
Maybe something like this?
#interface MyObjectLoaderDelegate : NSObject <RKObjectLoaderDelegate>
#property (nonatomic, copy) void (^callback)(RKObjectLoader *loader, NSDictionary *objectDictionary, NSError *error)
- (id)initWithCallback:(void (^)(RKObjectLoader*, NSDictionary*, NSError*)aCallaback;
#end
And on any implemented delegate method you can execute the block. Since blocks retain scoped variables you can run code against the calling delegate.
Whatcha think?
I am not sure using blocks is the right way to solve your issue.
How about having a class GetProfile that implements RKObjectLoaderDelegate.
So you call the request from within here and set itself to be the delegate.
Then you have an objectLoader per request.
So in your view controller, each time you what GetProfile, you create an instance. And then when that instance messages your controller back (via delegates?) you know which it is.
I am just grappling with this issue as well, so am keen to hear feedback.
Switching over to AFNetworking seems to be the way to go... it was a much easier implementation for what I needed.
Related
I’m working of writing an Objective-C wrapper for a REST API. A lot of what I’m reading says not to use synchronous NSUrlConnection requests to avoid blocking the main thread. This makes sense, but I am wondering, what is the best way to make an API wrapper in Objective-C?
For example, we have an API method that takes a session ID and returns whether or not the session is valid. Ideally the wrapper method prototype would look like the following:
- (BOOL) sessionIsValid:(NSString *)sessionId;
So, that method could be called and the return value could be used to decide what to do next. But how would this work if the API call is asynchronous? It seems like the caller would need to set itself as the wrapper's delegate, make a request, and then process the response from the delegate method. Seems pretty ugly for such a simple API call. Is there a good way to achieve this?
Whether a session is valid or expired, should be an implementation detail of the underlying network layer. Thus, you would rarely have this method in a Objective-C API. Rather you would have an API that looks more like this:
typedef void (^completion_t)(id result, NSError* error);
- (void) fetchAllPostsWithUser:(ID)userID completion:(completion_t)completionHandler;
This is an asynchronous method. The call-site will be notified about the completion through calling the completion handler, which passes the result of the operation and possibly an error object. What result is actually, is entirely up to you: it may be an NSArray of custom objects of class Post or it may be JSON (either serialized or as objects), or whatever. In any case it must be clearly defined in the documentation.
Your "session problem" is part of the authentication scheme. In many cases, NSURLSession, respectively NSURLConnection can already handle authentication completely transparent for you. If not, there are a few delegate methods which can be overridden where you can tailor the behavior. A client (a developer using this API as a library) of that Objective-C API should never be concerned with such "abstract" and "obscure" notions like "session". He/she, knows just passwords, users, posts, etc. ;)
If you wanted to return a BOOL you would need to have already setup the session and cached the validity (expiry date) - which isn't always going to be possible. A delegate pattern is a good option. Don't forget that you can implement the delegate pattern using blocks (which give you the option of having multiple different delegates all using the API at the same time).
I was using method swizzling to wrap all method invocations in a class with some extra functionality. Specifically I was:
Checking if the required object for this method call was in the cache
If the cache had that object return it.
If not, dispatch to the original implementation, populate the cache and return that.
For each method, I would reroute to an advised method. And implement the new method using + (BOOL)resolveInstanceMethod:(SEL)sel and IMP_implementationWithBlock.
It worked fine, but the code didn't read nicely. It seems NSProxy will provide a neater way to implement this functionality.
But still another alternative, would be to simply have an NSObject subclass stand-in and intercept method calls around my target object's methods. By overriding forwardInvocation and methodSignatureForSelector, I can get the required outcome.
So what does NSProxy give me? Why should I use this instead?
The point of NSProxy is that it doesn't implement most methods. That's necessary to be sure that the Objective-C forwarding machinery gets invoked to begin with. If you start with NSObject, there are a lot of methods which will just be directly dispatched without you having an opportunity to forward them.
OK I alllmost get it. I've done a bunch of reading in objective-c and I really want to understand delegation because it seems super important for cocoa-touch and I want to design an iPhone app.
So, a delegate is an object that can be given a certain task. It is said to follow a 'protocol' if it implements certain functions. So a view-controller, for example, can say "hey, I'm not sure where to get this data from..or hey, I'm not sure how to format this thing...can you do it for me?" and the delegate is like "sure I got you covered".
OK that makes sense to me. What doesn't make sense to me is how I get the delegate to return stuff to a view controller. Like say my delegate can go to a URL and read a sports score or something. So I say "delegate get me this score" ...how do I get the view controller to say "got it, here it is" and then have it inside the view controller. There might be a gap in my understanding here. Would I have to instantiate the view controller inside the delegate? That doesn't make sense to me...because then I'd have two view controllers...Feel free to change my analogy if you can make it clearer.
Thanks!
I think you're confused because a similar pattern is used for (but is not limited to!) two common tasks, both of which apply to your situation.
The patterns
Having an external object provide data for you (this is usually called a data source). See, for example, the UITableViewDataSource protocol.
This is implemented by a return value from the method: such as - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
The object implementing the protocol returns some value (in this case, a cell) to the caller.
The thing I'll mention about data sources: the data source itself (the object implementing the protocol) usually contains more of your application's custom logic, while the controller which requires a data source can be more generic. For example, UITableView is a generic view controller that displays a table view, while a class implementing the UITableViewDataSource protocol needs to know the details of your application's database.
(However, to be thorough, you do often subclass UITableView for custom logic, but this is most often presentation logic and not business logic.)
These methods call in to your application logic, and are expected to return immediately.
Providing callbacks once you've finished loading data.
For example, the NSURLConnection class has the corresponding NSURLConnectionDelegate protocol. The most common use pattern is:
Your object creates a NSURLConnection, with itself as the delegate.
You configure and start the connection.
You receive progress and data via the delegate methods you implement.
In this case the object which requires a delegate is an auxiliary object that knows how to load data from a URL in the background. The delegate methods are callbacks to your application logic, and are called at any time after the object is told to start loading data (or whatever it's designed to do).
Delegation is also used for other things on iOS, such as the UI-related tasks performed by objects conforming to UITableViewDelegate.
Your situation
This all depends on what your application is, and what your view controller is responsible for — but it sounds like you want the view controller to delegate the loading of data — basically, it needs a data source. (You should also consider if the built-in UITableView & UITableViewDataSource might suit your needs.) But if your data source is going to asynchronously load data from the internet, it might need to implement some data-loading callbacks via something such as NSURLConnection.
These two methods don't necessarily go together well, because the view controller will expect its data source to immediately return data, but the data source might need to wait for data to load.
This is why UITableView has a method -reloadData, so the object which serves as the data source can tell it when data is available. You might want to use a pattern like this in your application.
(But again, in all likelihood you won't need to implement a fully custom stack — either you can combine some classes to reduce your use of delegation, or you can use more built-in classes to suit your needs.)
Define your protocol's methods with return values.
That said, getting a URL is a bad example, since waiting for a delegate to return the results would block the calling thread. In this case, you would have to have a way for the delegate to call back with the results when done. This can be achieved either by passing a delegate to the delegate, or passing one or more Objective-C blocks to the delegate (onSuccess, onError, …).
On that subject, blocks are much easier to code than delegates and protocols, and are gradually supplanting them in Apple's and third-party APIs.
I'm creating a service class in Objective-C that communicates with a HTTP server. I'm familiar with NSURLConnection and the associated classes to make an asynchronous request to a server. So far, I've used self as the delegate, responding to the four delegate methods (didReceiveResponse, didReceiveData etc.) required for the call.
The service class I'm building must be able to handle several requests at once, and respond differently to each. I'm looking for a way to achieve this, without an endless switch or if-elseif in the connectionDidFinishLoading method.
I have the idea to create a delegate class that implements the four methods described above, and give that to the NSURLConnection object to talk to. However, I want to notify the parent class when the connection finishes loading and then fire another method within the parent class. And of course, the delegate doesn't know which method this is - it could be anything.
How to proceed? Is there a way to set a selector for the delegate class, and fire that selector (which is a method) when the request is finished? How do I do such a thing?
(Creating a delegate for my delegate, and then calling a superclass method could do the trick, but I'm really looking into wildcard methods, such as a selector.)
Any help is greatly appreciated!
Regards,
Reinder
I would pass a block (^{ ... })to the delegate that it should call when the connection has finished.
You have three options:
Using a block callback
Using notifications
Using a delegate, as you already suggested.
All three are perfectly valid, but with various advantages/disadvantages. Learn them all, then decide which to use in each scenario. :-)
Often more than one solution will be chosen. For example many of Apple's classes implement a delegate method and send a notification for the same event.
Sorry for noobness-level of the question.
I saw this answer, where it says
Add something like this to your NSURLConnection delegate
However, the method in question, sendSynchronousRequest:returningResponse:error:, is a class method. If I understand delegate methods correctly, they use a delegate which is specified when the NSURLConnection object is initiated.
So, if I have no instance of the class, a delegate can't be used. Did I understand correctly?
Yes, you are correct. In Cocoa, assigning a delegate requires an instance. In the case of NSURLConnection, its delegate is supposed to be used when making an asynchronous request that is initiated with -initWithRequest:delegate: or +connectionWithRequest:delegate:
Since classes are also objects it could be conceived that they also could have delegates. However, since there is no concept of class variables in Objective-C, a class would have nowhere to store its delegate (it could use a standard C variable in its .m file, though). Anyway, this concept is not used in Cocoa.
Yes delegates are used for handling events when you send your request asynchronously.
While sending synchronous request using +sendSynchronousRequest:returningResponse:error: method delegates are not used - you only get the resulting data and (optionally) server response and error and not aware about intermediate loading events (and remember that calling thread is also blocked while connection is in progress).