Note that there are some explanatory texts on larger screens.

plurals
  1. POHow to best design custom delegates wrapping other delegates
    primarykey
    data
    text
    <p>I am relatively new to Objective C, and have an architecture question regarding creating custom delegates around existing delegates. It's probably best described in pseudocode. Apologies for the length, I couldn't determine how to make it more succinct.</p> <p><strong>Overall desired architecture summary</strong></p> <p>I have a class in some library that makes a call to a web service, which takes a delegate for callbacks when the web service returns asynchronously. This low-level library is wrapped in a facade (a singleton) to shield clients from knowledge of it, and the facade methods can take a delegate that effectively wraps the low-level library's delegates, so clients can assign themselves as delegates when the facade (itself a delegate) gets data back from web service calls).</p> <p>In other words, client instance A calls the facade [f makeCallWithDelegate:self], and the facade hides its internal workings so A only has to implement the facade's delegate's protocol.</p> <p><strong>Pseudocode</strong></p> <p>The low level class:</p> <pre><code>// The low-level class that calls a web service // LowLevelClass.h @protocol LowLevelClassDelegate - (void)success:(LowLevelClass*) c; @end @interface LowLevelClass - (void)makeAsynchronousCallWithDelegate:(id &lt;LowLevelClassDelegate&gt;) d; @end // LowLevelClass.m omitted </code></pre> <p>The facade wrapping the low-level class. The issue is noted in the makeCallWithDelegate selector, and in the success delegate callback:</p> <pre><code>// partial Facade.h @protocol FacadeDelegate - (void)success; @end @interface Facade &lt;LowLevelClassDelegate&gt; // the facade handles the delegate calls of the LowLevelClass ... - (void)makeCallWithDelegate:(id &lt;FacadeDelegate&gt;) d; ... @end // Facade.m (pseudocode) @implementation Facade - (void)makeCallWithDelegate:(id &lt;FacadeDelegate&gt;) the_delegate { LowLevelClass * llc = ... // get instance [llc makeAsynchronousCallWithDelegate:self]; // delegate to self, catch events // Issue: have to somehow pass the_delegate to the "success" method below, // or have it available } // LowLevelClassDelegate implementation, hands result to the FacadeDelegate - (void)success:(LowLevelClass*) c { // Issue: need the_delegate to get down here somehow, or be reachable. [the_delegate success] } @end </code></pre> <p>The client:</p> <pre><code>// Client.m fragment, client implements the FacadeDelegate protocol ... - (void)makeFacadeGetData { [facade makeCallWithDelegate:self]; } - (void)success { NSLog(@"Hooray"); } ... </code></pre> <p><strong>Recap</strong></p> <p>The Client instance calls the facade and passes itself as a delegate. The facade in turn calls the low level class instance, passing the facade itself as a delegate. The facade hears the response from the low level class instance, and returns a nicer result to the Client instance. The problem I am working around is how to ensure that the correct facade delegate is getting used when wrapping the low-level delegate's response.</p> <p>Reiterating the above, with instances: Client instance A calls the facade [f makeCallWithDelegate:self]. Client instance B also calls the facade [f makeCallWithDelegate:self]. I need to be sure that client A is used as the delegate for client A's call, and client B is used as the delegate for client B's call.</p> <p><strong>Notes impacting the design</strong></p> <p>The LowLevelClass can't be modified. If it could, I would perhaps pass in the delegate to call as a "userdata" field, or something ... I'm not a fan of that approach anyway, because that pollutes a lower-layer class with knowledge of higher-layer classes.</p> <p>The facade is a singleton, for a few reasons. Perhaps this could be changed, and I could create multiple facade instances.</p> <p><strong>Possible Solutions</strong></p> <p>A. Now, if I could guarantee that the facade was going to be used by a single client at a time, I could just store the_delegate in a Facade member variable, and I could call it in the Facade success: selector; however, I can't guarantee that. Several different client instances can call the facade, and I need to be sure that each client's call's delegate is the client itself, and not some other client (using the instance example above, A needs to handle A's call, and B needs to handle B's).</p> <p>B. I could make the facade a non-singleton class, and just store the_delegate in a member variable. Maybe that's the best solution ... something feels wrong about it though.</p> <p>C. I was thinking that perhaps I could assign a unique key to each call to makeCallWithDelegate, and the facade singleton instance would store a dictionary of the call keys and the delegate passed in for that call. (Note: The only key that makes sense to me is the actual value of the LowLevelClass pointer -- after all, if I generate a random string ID, I'm just deferring the problem -- but this somehow feels shaky.) The facade's "success" method would have access to the dictionary, and so would call the correct delegate:</p> <pre><code>// Facade.m (pseudocode) @implementation Facade - (void)makeCallWithDelegate:(id &lt;FacadeDelegate&gt;) the_delegate { LowLevelClass * llc = ... // get instance NSString* uniqueKey = makeUniqueKeyFromPointerValue(llc); [self.delegateDictionary setObject:the_delegate forKey:uniqueKey]; [llc makeAsynchronousCallWithDelegate:self]; // delegate to self, catch events } // LowLevelClassDelegate implementation, hands result to the FacadeDelegate - (void)success:(LowLevelClass*) c { NSString* uniqueKey = makeUniqueKeyFromPointerValue(c); id&lt;FacadeDelegate&gt; del = [self.delegateDictionary objectForKey:uniqueKey]; [del success] } @end </code></pre> <p>I hope that the above is sufficiently detailed and clear enough. After all that, I feel confused myself (again, new to objective C).</p> <p>If you've made it this far, I thank you (and congratulate you). I'd like to hear any suggestions as to what would be a good design for this problem.</p> <p>Thanks very much for your time.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload